]> Projects (at) Tadryanom (dot) Me - AdrOS.git/log
AdrOS.git
4 weeks agouserland: enhance 17 utilities and ulibc with POSIX features
Tulio A M Mendes [Mon, 27 Apr 2026 02:34:15 +0000 (23:34 -0300)]
userland: enhance 17 utilities and ulibc with POSIX features

ulibc:
- Add sys/statvfs.h and statvfs() implementation
- Extend time.h: strftime, localtime, gmtime, ctime, asctime, difftime
- Fix time_t conflict (was uint32_t in time.h, int32_t in sys/types.h)
- Add unistd.h include to statvfs.c for close()

High-priority commands:
- awk: BEGIN/END blocks, NF/NR vars, print with OFS, -v var=val, /bin/bash
- find: -mtime, -size, -perm, -user, -maxdepth, -delete, -exec
- chmod: symbolic mode support (u+x, go-w, a=rw)
- grep: BRE/ERE regex, -i, -r, -l, -q, -n flags
- sed: d/p/q/a/i/c/y commands, line/regex addresses, -n mode
- ps: BSD-style output with UID/PID/PPID/STAT/TIME/CMD
- top: system summary header, process list with -n iterations

Medium-priority commands:
- sh: command substitution $(...), tilde expansion ~, $$/$! vars,
  variable overflow protection
- cp/mv: preserve source file permissions via fstat
- rm: proper -rf recursive directory removal with getdents
- kill: -l flag to list signal names
- init: SIGUSR1/SIGUSR2 handlers for poweroff/reboot

Low-priority commands:
- stat: date/time display, UID/GID name lookup, file type
- dd: conv= (ucase/lcase/swab/noerror/sync/trunc), skip=, seek=
- ls: -n flag for numeric UID/GID

Tests: 120/120 QEMU, 33/33 battery, 57/57 host (3 skipped)

5 weeks agocppcheck: fix unreadVariable in elf.c and sync.c
Tulio A M Mendes [Mon, 27 Apr 2026 01:59:00 +0000 (22:59 -0300)]
cppcheck: fix unreadVariable in elf.c and sync.c

- elf.c: remove dead seg_vmm_flags computation from elf32_load_segments
  (re-protection is done by elf32_reprotect_segments after relocations)
- sync.c: eliminate unused deadline variable, inline into wake_at_tick

Tests: 120/120 QEMU, 33/33 battery, 69/69 host

5 weeks agokernel: security audit fixes — C1-C6, H3-H4, M5
Tulio A M Mendes [Mon, 27 Apr 2026 01:41:15 +0000 (22:41 -0300)]
kernel: security audit fixes — C1-C6, H3-H4, M5

Critical fixes:
- C1: procfs UAF — add g_pid_cmdline pool, stop overwriting g_pid_status
- C2: heap corruption — kill process + schedule() instead of infinite loop
- C3: ext2 consistency — write inode after each i_blocks increment
- C4: shm UAF — reject shm_at on IPC_RMID'd segments (-EIDRM),
  skip marked_rm segments in shm_get lookup
- C6: ELF W^X — parse p_flags for segment permissions, re-protect
  after relocations, only re-protect full pages within non-writable
  segments (partial pages may be shared with .data)

High fixes:
- H3: execve_copy_user_str — add upfront user_range_ok check
- H4: rq_remove_if_queued — scan all priority queues in both
  active and expired runqueues (not just current priority)

Low fixes:
- M5: frame_refcount uint16_t → uint32_t to prevent overflow

Also adds EIDRM/ENOMSG errno definitions.

Tests: 120/120 QEMU, 33/33 battery, 69/69 host — zero regressions

5 weeks agodocs: add SYSCALL_TEST_COVERAGE.md — detailed per-syscall test mapping and POSIX...
Tulio A M Mendes [Mon, 27 Apr 2026 00:54:42 +0000 (21:54 -0300)]
docs: add SYSCALL_TEST_COVERAGE.md — detailed per-syscall test mapping and POSIX gap analysis

5 weeks agodocs: update all documentation with current test coverage and POSIX gap analysis
Tulio A M Mendes [Mon, 27 Apr 2026 00:51:02 +0000 (21:51 -0300)]
docs: update all documentation with current test coverage and POSIX gap analysis

- Update test counts across all docs: 120 smoke tests, 33 battery, 69 host
- Update syscall count: 141 syscalls, 124/141 tested (87.9% coverage)
- Add syscall test coverage section listing 17 untested syscalls
- Add comprehensive POSIX gaps section for 100% compliance
- Categories: process/credentials, filesystem, signals, POSIX timers,
  IPC, memory, network, threads
- Mark madvise and execveat as implemented in FULL_POSIX_AUDIT.md

5 weeks agokernel: fix execve after pivot_root via vfs_lookup_initrd(); fork execve test
Tulio A M Mendes [Mon, 27 Apr 2026 00:28:10 +0000 (21:28 -0300)]
kernel: fix execve after pivot_root via vfs_lookup_initrd(); fork execve test

pivot_root changes the global fs_root and '/' mount entry to a tmpfs,
which breaks subsequent execve calls because vfs_lookup('/bin/echo')
fails in the new root.  This caused the 'echo execve' flaky test
failure (it always failed when run after the pivot_root test).

Fix: save the original initrd overlayfs root at boot and add
vfs_lookup_initrd() which does a direct finddir traversal from that
saved root, bypassing the VFS mount table.  Use it in:
- elf32_load_user_from_initrd (binary lookup)
- elf32_load_interp (ld.so lookup)
- elf32_load_shared_lib_at (libc.so lookup)
- syscall_execve_impl (early ENOENT check)

Also change the execve test to fork a child (like execveat), so PID 1
is not replaced and the test harness can verify the output.

Tests: 120/120 smoke, 33/33 battery, 69/69 host, cppcheck clean.

5 weeks agokernel: fix COW page write + signal delivery; add 6 new tests
Tulio A M Mendes [Sun, 26 Apr 2026 23:51:23 +0000 (20:51 -0300)]
kernel: fix COW page write + signal delivery; add 6 new tests

Kernel fixes:
- uaccess: x86_user_page_writable_user() now recognizes COW pages as
  logically writable (checks X86_PTE_COW flag bit 9).  Previously,
  copy_to_user() rejected writes to forked COW pages, preventing
  signal frame delivery after fork().
- idt: handle COW faults in kernel mode before uaccess_try_recover().
  A write from copy_to_user() to a COW page now triggers page fault
  resolution (private copy) instead of returning -EFAULT.
- scheduler: fork inherits sigactions and sig_blocked_mask from parent
  (POSIX requirement).  Pending signals stay 0 per POSIX spec.
- syscall: sigprocmask how values now match POSIX (0=BLOCK, 1=UNBLOCK,
  2=SETMASK).  sigsuspend no longer restores old mask before signal
  delivery, allowing the handler to run.

New tests (fulltest.c):
- I12: clone — CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND thread
  creation with shared memory verification
- I13: sigqueue — send SIGUSR1 with value via sigsuspend
- I14: inotify_init1 — basic inotify with flags=0
- I15: dlopen/dlsym/dlclose — dynamic linker via libpietest.so
- I16: execveat — execute /bin/echo with AT_FDCWD
- I17: pivot_root — mount tmpfs, pivot, verify in isolated fork

Test harness updates:
- smoke_test.exp: +6 patterns (114→120)
- test_battery.exp: +6 patterns (27→33)

Results: smoke 119/120 (echo execve flaky), battery 33/33 PASS

6 weeks agofulltest: add 11 new POSIX syscall tests and wrappers
Tulio A M Mendes [Sun, 19 Apr 2026 22:36:29 +0000 (19:36 -0300)]
fulltest: add 11 new POSIX syscall tests and wrappers

New tests (I1-I12):
- geteuid/getegid: verify euid/egid match uid/gid for root
- seteuid/setegid: root can change and restore euid/egid
- CLOCK_MONOTONIC: monotonic clock advances after nanosleep
- chown: change file owner
- futex: FUTEX_WAIT returns EAGAIN on mismatch, WAKE returns 0
- sigaltstack: set/query/disable alternate signal stack
- socket API: TCP socket create/bind/listen/getsockname/shutdown
- mqueue: POSIX message queue open/send/receive/close/unlink
- named semaphore: sem_open/post/wait/getvalue/close/unlink
- getrusage: query RUSAGE_SELF (non-zero fields)
- mount/umount2: mount tmpfs, create file, unmount

New syscall wrappers added to fulltest:
sys_chown, sys_futex, sys_sigaltstack, sys_socket, sys_bind,
sys_listen, sys_accept, sys_connect, sys_send, sys_recv,
sys_shutdown, sys_getsockname, sys_mq_open/close/send/receive/unlink,
sys_sem_open/close/wait/post/unlink/getvalue, sys_getrusage,
sys_sigqueue, sys_mount, sys_umount2, sys_execveat

New constants: FUTEX_WAIT/WAKE, AF_INET, SOCK_STREAM, IPPROTO_TCP,
RUSAGE_SELF, struct sockaddr_in, plus enum values for all new syscalls

Updated smoke_test.exp and test_battery.exp to track new tests.
Test results: 114/114 QEMU, 27/27 battery, 69/69 host

6 weeks agofix select() timeout conversion — was passing raw pointer as kernel timeout
Tulio A M Mendes [Sun, 19 Apr 2026 21:30:12 +0000 (18:30 -0300)]
fix select() timeout conversion — was passing raw pointer as kernel timeout

The select() wrapper in both newlib/posix_stubs.c and ulibc/unistd.c
was casting the struct timeval* pointer directly to int and passing it
as the 5th syscall argument. The kernel expects an int32_t timeout:
  -1 = infinite wait, 0 = poll (return immediately), >0 = ticks.

When bash calls select() with timeout=NULL (infinite wait), the raw
pointer value was interpreted as a large positive number (poll with
timeout), not as -1 (infinite). Worse, on some code paths the pointer
could be 0 (NULL), which the kernel treats as timeout=0 (poll only),
causing select() to return 0 immediately with no fds ready — which
bash interprets as EOF on stdin, causing it to exit immediately.

Fix: convert struct timeval to int32_t ticks before passing to the
kernel. NULL timeout → -1 (infinite). tv_sec=0, tv_usec=0 → 0 (poll).
Otherwise convert ms → ticks (TIMER_HZ=100, 10ms/tick).

6 weeks agolibgloss: fix isatty() and ttyname() — use TCGETS instead of TIOCGPGRP with NULL
Tulio A M Mendes [Sun, 19 Apr 2026 21:05:46 +0000 (18:05 -0300)]
libgloss: fix isatty() and ttyname() — use TCGETS instead of TIOCGPGRP with NULL

isatty() in syscalls.c used ioctl(TIOCGPGRP, 0) which always failed
because the kernel rejects NULL user_arg with -EFAULT. This caused
bash to think stdin was not a terminal, running in non-interactive
mode (no prompt, no line editing).

Fix: use TCGETS (0x5401) with a stack-allocated termios struct instead.
TCGETS succeeds on any tty regardless of process group state — this
is the standard POSIX way to test for a terminal.

Also fix ttyname() in posix_stubs.c which had the same bug.

6 weeks agotoolchain: fix libgloss/adros build errors
Tulio A M Mendes [Sun, 19 Apr 2026 20:41:52 +0000 (17:41 -0300)]
toolchain: fix libgloss/adros build errors

- Add sys/uio.h compat header (struct iovec + readv/writev declarations)
  needed by posix_stubs.c which was missing from sysroot_headers
- Remove duplicate fcntl() definition in posix_stubs.c (line 541 was
  identical to line 256, causing redefinition error with newlib headers)
- Move libgloss/adros source sync outside patch_newlib() marker guard
  so edits to stubs are always picked up on rebuild (cp -f instead of
  cp -u); keep mkdir -p inside patch_newlib() for autoconf file creation

6 weeks agokernel: remove dead-code duplicate SETITIMER/GETITIMER from socket_syscall_dispatch
Tulio A M Mendes [Sun, 19 Apr 2026 19:16:01 +0000 (16:16 -0300)]
kernel: remove dead-code duplicate SETITIMER/GETITIMER from socket_syscall_dispatch

The SETITIMER and GETITIMER syscall handlers were implemented twice:
1. In syscall_handler (lines 3941/4005) — using proper POSIX struct
   k_itimerval with tv_sec/tv_usec fields and timeval_to_ticks/
   ticks_to_timeval conversion helpers.
2. In socket_syscall_dispatch (lines 4673/4729) — using raw uint32_t
   tick pairs, which would be incorrect if ever reached.

The socket_syscall_dispatch versions were dead code because the main
handler returns before falling through to socket dispatch. Removing
them eliminates confusion and prevents accidental use of the incorrect
tick-based format.

Part of POSIX compliance audit: all 141 SYSCALL_ enum values now
have exactly one handler each.

Tests: 103/103 QEMU, 16/16 battery, 69/69 host — zero regressions.

6 weeks agoPOSIX compliance audit: fix syscall wrappers, headers, and commands
Tulio A M Mendes [Sun, 19 Apr 2026 18:38:13 +0000 (15:38 -0300)]
POSIX compliance audit: fix syscall wrappers, headers, and commands

ulibc fixes:
- mkdir(): change from variadic to (path, mode_t) per POSIX
- stat/fstat/fstatat: use struct stat* instead of void*
- wait4: use pid_t return and struct rusage* parameter
- waitid: use siginfo_t* instead of void*
- lseek: return off_t instead of int
- pread/pwrite: return ssize_t, use off_t offset
- truncate/ftruncate: use off_t length
- chmod: use mode_t instead of int
- open(): extract mode from va_args (was silently ignored)
- openat(): extract mode and pass as 4th syscall arg
- Add pivot_root() wrapper (syscall 120 existed but had no wrapper)
- Add lstat() (delegates to stat, no symlinks in AdrOS)
- Add fchmod() stub (ENOSYS, no kernel syscall yet)

ulibc header fixes:
- unistd.h: add sys/types.h include, forward declare struct stat/rusage
- fcntl.h: add O_EXCL, O_NOCTTY, O_DIRECTORY, O_NOFOLLOW, AT_FDCWD,
  AT_SYMLINK_NOFOLLOW, AT_REMOVEDIR, AT_SYMLINK_FOLLOW, AT_EACCESS
- sys/stat.h: add lstat, fchmod declarations, fix mkdir/chmod types

newlib fixes:
- openat(): extract mode from va_args, pass as 4th arg (was _sc3)
- Add pivot_root() wrapper

command fixes:
- mkdir: pass mode 0755 to mkdir() calls (was calling with 0 args)
- stat: remove unnecessary void* cast
- df: replace hardcoded stub with /proc/mounts reader

Tests: 103/103 PASS, 16/16 battery, 69/69 host

6 weeks agoFix mount/umount commands: use proper syscall wrappers and error messages
Tulio A M Mendes [Sun, 19 Apr 2026 18:23:01 +0000 (15:23 -0300)]
Fix mount/umount commands: use proper syscall wrappers and error messages

mount.c: Replace direct _syscall3 call with mount() wrapper from
sys/mount.h. Print strerror(errno) instead of raw -1 return value
from __syscall_ret, which was always -1 and unhelpful for debugging.

umount.c: Replace stub that always printed 'operation not supported'
with a working implementation using the umount() wrapper from
sys/mount.h. The SYS_UMOUNT2 syscall existed in the kernel but the
umount command never used it.

Tests: 103/103 PASS

6 weeks agoComplete syscall coverage in ulibc and newlib wrappers
Tulio A M Mendes [Sun, 19 Apr 2026 18:12:28 +0000 (15:12 -0300)]
Complete syscall coverage in ulibc and newlib wrappers

Audit of all 141 kernel syscalls vs ulibc/newlib wrappers revealed
many missing implementations. Both libraries had syscall numbers
defined but no corresponding POSIX wrapper functions.

ulibc additions (unistd.c):
  - select, fcntl, rename (was stub returning -1), umask
  - dup3, openat, fstatat, unlinkat
  - mount, umount2, umount
  - wait4, waitid, sigreturn, sigqueue, set_thread_area

ulibc header updates:
  - unistd.h: declarations for all new functions
  - signal.h: union sigval, sigqueue, sigreturn declarations
  - sys/mount.h: new header with mount/umount2/umount + flags
  - Removed old rename() stub from stdio.c

newlib additions (posix_stubs.c):
  - Complete syscall number table (was 32 entries, now all 141)
  - All missing POSIX wrappers: fcntl, rename, rmdir, mkdir, unlink,
    dup3, openat, fstatat, unlinkat, mount, umount2, umount, wait4,
    waitid, sigreturn, sigqueue, set_thread_area, pread, pwrite,
    truncate, ftruncate, fsync, fdatasync, sigpending, sigsuspend,
    readv, writev, times, flock, setitimer, getitimer, link, symlink,
    readlink, pipe2, clock_gettime, gettimeofday, mmap, munmap,
    mprotect, madvise, getrlimit, setrlimit, getrusage, uname,
    brk, sbrk
  - Added includes: sys/mman.h, sys/resource.h, sys/utsname.h,
    sys/uio.h, sys/times.h, sys/wait.h

Fix: setitimer/getitimer signatures corrected from void* to
struct itimerval* to match sys/time.h declarations.

Tests: 103/103 PASS, 16/16 battery PASS, 69/69 host PASS.

6 weeks agoFix getcwd(NULL, 0): allocate buffer when buf is NULL (glibc extension)
Tulio A M Mendes [Sun, 19 Apr 2026 17:55:59 +0000 (14:55 -0300)]
Fix getcwd(NULL, 0): allocate buffer when buf is NULL (glibc extension)

Bash calls getcwd(NULL, 0) — a glibc extension where NULL means
"allocate a buffer for me". Both ulibc and newlib wrappers passed
NULL straight to the kernel syscall, which returned -EFAULT because
the kernel rejects user_buf == NULL.

This caused:
  shell-init: error retrieving current directory: getcwd:
  cannot access parent directories: Bad address

Now both wrappers handle NULL buf by malloc'ing a buffer (4096 bytes
if size is 0), passing it to the kernel, and returning the allocated
pointer on success (or freeing it on error). This matches glibc
behavior and unblocks bash and other ported software.

6 weeks agoFix getcwd: return char* (POSIX) instead of int
Tulio A M Mendes [Sun, 19 Apr 2026 17:38:54 +0000 (14:38 -0300)]
Fix getcwd: return char* (POSIX) instead of int

ulibc getcwd() returned int (0 on success, -1 on error) but POSIX
specifies char* (buf on success, NULL on error). Bash and other
ported software call getcwd expecting a pointer return; getting 0
(NULL) on success caused:

  shell-init: error retrieving current directory: getcwd:
  cannot access parent directories: Bad address

The kernel syscall already returns 0 on success, so the fix is in
the ulibc wrapper: return buf on success, NULL on error (setting
errno). This matches the newlib wrapper in posix_stubs.c which had
the correct signature all along.

Updated all internal callers (sh, pwd, realpath) from int-style
checks (>= 0 / < 0) to pointer-style checks (truthy / !ptr).

6 weeks agoFix initrd LZ4 OOM: use page-level allocation instead of kmalloc
Tulio A M Mendes [Sun, 19 Apr 2026 01:25:23 +0000 (22:25 -0300)]
Fix initrd LZ4 OOM: use page-level allocation instead of kmalloc

initrd_init used kmalloc() to allocate the decompression buffer for
LZ4-compressed initrd images. The kernel buddy heap is only 8 MB, so
a 4 MB allocation (the default content size) can easily fail due to
fragmentation from earlier allocations, producing:

  [HEAP] OOM: kmalloc failed.
  [INITRD] OOM decompressing LZ4 (4194304 bytes)

This was especially likely with custom initrd images compressed with
lz4 -1 or -9, which can declare larger content sizes.

Replace kmalloc(orig_sz) with initrd_alloc_pages(), which allocates
individual physical pages via pmm_alloc_page() and maps them into a
dedicated virtual region (0xD0800000, above the 8MB heap). This
bypasses the buddy allocator entirely, using the physical memory
manager directly. On OOM, already-mapped pages are rolled back.

Both LZ4 Frame and legacy LZ4B paths are updated.

6 weeks agoFix FAT/ext2 heap corruption: skip kfree on static root nodes
Tulio A M Mendes [Sun, 19 Apr 2026 00:17:24 +0000 (21:17 -0300)]
Fix FAT/ext2 heap corruption: skip kfree on static root nodes

fat_close_impl and ext2_close_impl unconditionally called kfree() on
the node passed to vfs_close(). When a directory fd pointed to a
filesystem mount root (a static variable like g_fat_root or
g_ext2_root), this kfree corrupted the heap (bad magic 0x0).

diskfs_close_impl already had the guard pattern (dn == &g_root).
Apply the same guard to fat and ext2 close handlers.

6 weeks agoFix job control TIOCSPGRP: allow session leader to claim TTY/PTY
Tulio A M Mendes [Fri, 17 Apr 2026 21:46:32 +0000 (18:46 -0300)]
Fix job control TIOCSPGRP: allow session leader to claim TTY/PTY

When fulltest runs from the shell, the TTY's tty_session_id is already
set to the shell's session. The test's leader child calls setsid() then
TIOCSPGRP, which fails with EPERM because the new session doesn't match
tty_session_id. The kernel never allowed a new session leader to claim
the TTY as its controlling terminal.

Fix: in TIOCSPGRP handler for both tty.c and pty.c, allow a session
leader (session_id == pid) to claim the terminal by updating the
session when it explicitly sets its own pgrp as foreground. This
matches POSIX semantics where a session leader may establish a new
controlling terminal.

Test results: 103/103 smoke test PASS, 16/16 battery PASS.

6 weeks agodiskfs: fix LBA space leak on unlink/rmdir + add spinlock protection + ATA DMA irqsave
Tulio A M Mendes [Fri, 17 Apr 2026 21:08:02 +0000 (18:08 -0300)]
diskfs: fix LBA space leak on unlink/rmdir + add spinlock protection + ATA DMA irqsave

Root cause: diskfs_unlink and diskfs_rmdir zeroed freed inodes but
never reclaimed LBA space -- next_free_lba only grew, never shrank.
After many test runs, next_free_lba exceeded the 8192-sector disk,
causing ata_pio_read28 to fail with -EIO on out-of-range LBAs,
making writes to newly created files fail silently.

Fixes:
- Add diskfs_reclaim_space() that recalculates next_free_lba by
  scanning all inodes for the highest used LBA extent. Called from
  both diskfs_unlink and diskfs_rmdir after freeing an inode.
- Add g_diskfs_lock spinlock with irqsave/irqrestore variants
  protecting all superblock load/modify/store operations across all
  diskfs functions. All early returns properly unlock.
- Restructure diskfs_read_impl and diskfs_write_impl to cache inode
  metadata under lock, then release before sector I/O, avoiding long
  interrupt-disabled periods that could cause deadlocks/timeouts.
- Replace spin_lock/spin_unlock with spin_lock_irqsave/spin_unlock_
  irqrestore in all 4 ATA DMA channel functions to prevent deadlocks
  from timer interrupts during lock hold.

Test results: 103/103 PASS (3 consecutive runs, SMP=4),
16/16 battery PASS, 69/69 host PASS.

6 weeks agoFix fulltest TIOCGPGRP failure when run from shell
Tulio A M Mendes [Fri, 17 Apr 2026 17:01:34 +0000 (14:01 -0300)]
Fix fulltest TIOCGPGRP failure when run from shell

When fulltest runs from /bin/sh (rather than as init directly), the
shell has already claimed the controlling TTY via setsid(), setting
tty_fg_pgrp to a non-zero value. The test assumed fg == 0, which
only holds when running as PID 1 (session_id=0, pgrp_id=0).

Fix: compare TIOCGPGRP result against sys_getpgrp() instead of
hardcoded 0, use our own pgrp for TIOCSPGRP success test, and use
fg=-1 (EINVAL) instead of fg=1 for the expected-failure case.

Also rename 'hello from init.elf' to 'hello from fulltest.elf' in
both the test binary and the smoke test expectations.

6 weeks agofix(mm): munmap/brk page leaks — free physical frames on unmap, rollback on partial...
Tulio A M Mendes [Fri, 17 Apr 2026 04:09:18 +0000 (01:09 -0300)]
fix(mm): munmap/brk page leaks — free physical frames on unmap, rollback on partial failure

1. munmap: vmm_unmap_page only clears PTEs without freeing physical
   frames. For anonymous mmaps (shmid == -1), call vmm_virt_to_phys +
   pmm_free_page before vmm_unmap_page. Device-backed/shared mappings
   keep their frames (managed by their own subsystems).

2. brk shrink: same leak — free physical frames before unmapping when
   the heap shrinks.

3. brk grow partial failure: if pmm_alloc_page fails mid-expansion,
   rollback all pages already mapped in this call (unmap + free), then
   return old heap_break. Previously these pages were leaked permanently.

4. mmap anonymous partial failure: same rollback pattern applied to
   syscall_mmap_impl — if pmm_alloc_page fails mid-allocation, unmap
   and free all pages already mapped before returning -ENOMEM.

All tests pass: 69/69 host + 103/103 QEMU, zero regressions.

6 weeks agofix: 3 residual bugs from round-3 audit
Tulio A M Mendes [Fri, 17 Apr 2026 03:54:10 +0000 (00:54 -0300)]
fix: 3 residual bugs from round-3 audit

5. dlopen page leak: if pmm_alloc_page() fails mid-segment-load,
   rollback all previously mapped pages (unmap + pmm_free_page)
   instead of leaking them. Added vmm_virt_to_phys() API to
   recover physical frames before unmapping.

6. CLONE_THREAD without CLONE_VM: Linux requires CLONE_THREAD to
   imply CLONE_VM (threads share address space). Now returns
   -EINVAL if CLONE_THREAD is set without CLONE_VM, preventing
   unexpected behavior where a "thread" gets its own AS copy.

7. pipe_close SMP race: readers/writers decrement and conditional
   kfree(ps) were unprotected by a lock. Added spinlock_t to
   pipe_state and wrapped the critical section in
   spin_lock_irqsave, preventing underflow/double-free when both
   pipe ends are closed concurrently on different CPUs.

6 weeks agofix: 4 security/robustness bugs from audit
Tulio A M Mendes [Fri, 17 Apr 2026 03:16:43 +0000 (00:16 -0300)]
fix: 4 security/robustness bugs from audit

1. epoll: remove fd from epoll instances on close() — prevents
   use-after-close where a reused fd number would monitor the
   wrong file. Also auto-remove stale entries in epoll_wait.

2. clone: validate flags against CLONE_SUPPORTED_MASK — unknown
   flags (CLONE_NEWPID, CLONE_NEWUSER, etc.) now return EINVAL
   instead of being silently ignored.

3. futex: cleanup waiters on process exit — futex_waiters table
   moved to file scope; futex_cleanup_process() called from
   SYSCALL_EXIT prevents UAF when FUTEX_WAKE dereferences a
   freed process pointer.

4. ksem: fix infinite spin when waiters array full — replace
   schedule() spin-yield with process_sleep(1), and increase
   KSEM_MAX_WAITERS/KCOND_MAX_WAITERS from 16 to 64.

7 weeks agofix(test): skip /persist/counter test when no disk is available
Tulio A M Mendes [Thu, 16 Apr 2026 05:42:54 +0000 (02:42 -0300)]
fix(test): skip /persist/counter test when no disk is available

Instead of failing with sys_exit(1) when /persist/counter can't
be opened, skip the test with goto. This allows the fulltest to
continue running other tests in environments without persistent
storage.

7 weeks agoperf(net): reduce ping test sleep times for faster test runs
Tulio A M Mendes [Thu, 16 Apr 2026 05:42:40 +0000 (02:42 -0300)]
perf(net): reduce ping test sleep times for faster test runs

QEMU virtual NIC doesn't need 2s to stabilize. Reduced initial
wait from 2s to 500ms and inter-ping delay from 1s to 250ms.

7 weeks agocleanup(idt): remove debug page fault traces for PID 1
Tulio A M Mendes [Thu, 16 Apr 2026 05:36:59 +0000 (02:36 -0300)]
cleanup(idt): remove debug page fault traces for PID 1

Removed debug kprintf statements that were added during the
execve/SIGSEGV debugging session: PDE/PTE dumps, CR3 comparison,
GOT dumps, and LAPIC ID traces. These are no longer needed now
that the root cause has been fixed.

7 weeks agofix(ulibc): add null-pointer guards in __libc_init_array/fini_array
Tulio A M Mendes [Thu, 16 Apr 2026 05:36:47 +0000 (02:36 -0300)]
fix(ulibc): add null-pointer guards in __libc_init_array/fini_array

In a PIC shared library build, linker-generated symbols like
__init_array_start, __fini_array_end, and _init may remain
SHN_UNDEF (value 0) when the library has no .init_array/.fini_array
sections. The GOT entries for these resolve to 0.

Added null-pointer checks using volatile pointers (to prevent the
compiler from assuming the address is always non-null) for all
init/fini array pointers and for _init/_fini function pointers.
This prevents crashes when these symbols are unresolved.

7 weeks agofix(elf): skip SHN_UNDEF symbols in GLOB_DAT/32 relocations
Tulio A M Mendes [Thu, 16 Apr 2026 05:36:15 +0000 (02:36 -0300)]
fix(elf): skip SHN_UNDEF symbols in GLOB_DAT/32 relocations

The kernel's elf32_process_relocations incorrectly resolved R_386_GLOB_DAT
relocations for SHN_UNDEF symbols (st_shndx == 0) by setting the GOT
entry to base_offset. For libc.so loaded at 0x11000000, this meant
symbols like __init_array_start, __fini_array_end, and _init were
resolved to 0x11000000 (the ELF header), causing __libc_init_array
to execute garbage code at the ELF header and crash with SIGSEGV
at eip=0x11000012.

Now, SHN_UNDEF symbols are set to 0 in the GOT, leaving them for
the dynamic linker to resolve. Defined symbols (st_shndx != 0)
continue to be resolved as sym->st_value + base_offset.

Also removed debug kprintf from elf32_load_needed_libs.

7 weeks agofix(ld.so): use correct AdrOS syscall numbers instead of Linux numbers
Tulio A M Mendes [Thu, 16 Apr 2026 05:35:55 +0000 (02:35 -0300)]
fix(ld.so): use correct AdrOS syscall numbers instead of Linux numbers

dbg_write() was using Linux syscall number 4 (write) in EAX, but
AdrOS's SYSCALL_WRITE is 1. This caused dbg_write to call open()
instead of write(), so ld.so debug output never appeared.

Also fixed the inline asm to properly declare EAX as an output
operand (int $0x80 returns the result in EAX), preventing potential
compiler misoptimization.

Removed all debug prints from _start_c and dl_fixup that were
added during debugging.

7 weeks agofix(execve): reset signal handlers and clear pending signals on execve
Tulio A M Mendes [Thu, 16 Apr 2026 05:35:36 +0000 (02:35 -0300)]
fix(execve): reset signal handlers and clear pending signals on execve

POSIX requires that execve resets all signal handlers to SIG_DFL
(except SIG_IGN) and clears pending signals. Without this, the
kernel could attempt to deliver a signal to a handler address in
the destroyed old address space, causing a crash.

Also removed debug trace code (ldso trace, write trace, exit trace,
PTE dumps, GOT dumps) that was added during debugging and was
interfering with test output.

8 weeks agofix: resolve all sparse static analysis warnings
Tulio A M Mendes [Mon, 6 Apr 2026 23:37:35 +0000 (20:37 -0300)]
fix: resolve all sparse static analysis warnings

- Replace plain integer 0 with NULL for pointer assignments, returns,
  and comparisons across 24 source files (kernel, drivers, HAL, arch, net)
- Add missing <stddef.h> includes for NULL in keyboard.c and uart.c
- Make file-local symbols static in idt.c (idt, idtp, interrupt_handlers,
  idt_set_gate, pic_remap, print_reg) and scheduler.c (ready_queue_tail)
- Add missing extern/prototype declarations to headers:
  idt.h (isr_handler), syscall.h (syscall_handler),
  process.h (ready_queue_head, sched_lock, sched_set_init_pid,
  sched_assign_pid1, sched_ap_init), net.h (net_dhcp_start),
  percpu.h (_percpu_gs_lut, percpu_get_ptr), smp.h (ap_sched_go,
  ap_entry), arch_syscall.h (x86_sysenter_set_kernel_stack,
  sysenter_init_ap), hal/usermode.h (x86_usermode_test_start),
  hal/cpu.h (g_smap_enabled), ata_pio.h (ata_register_devfs),
  boot_info.h (kernel_main)
- Add forward declarations for compiler ABI symbols in utils.c
- Include own headers in source files so sparse sees declarations
- Add lwIP/net include paths to SPARSE_FLAGS in Makefile

Verified: make check (0 warnings), make test-host (116/116 pass),
make test (102/103 pass, 1 pre-existing timeout)

8 weeks agofix(security): mq_receive_impl TOCTOU race — copy msg data under lock
Tulio A M Mendes [Sun, 5 Apr 2026 15:30:44 +0000 (12:30 -0300)]
fix(security): mq_receive_impl TOCTOU race — copy msg data under lock

Mirror the mq_send_impl fix: copy message data into a kernel buffer
while holding mq_lock, then copy_to_user after release. Without this,
another thread could overwrite the dequeued slot via mq_send before
the copy_to_user completes.

Found during deep re-scan. All tests pass (102/103, 1 pre-existing timeout).

8 weeks agofix(security): Red Team bug fixes + deep analysis hardening
Tulio A M Mendes [Sun, 5 Apr 2026 15:19:46 +0000 (12:19 -0300)]
fix(security): Red Team bug fixes + deep analysis hardening

Red Team report fixes (8 bugs):
- pmm_free_page: add rc==0 double-free guard with BUG log
- uaccess: move global state to per-CPU via GS segment (SMP safety)
- elf.c: add overflow checks for p_vaddr+base_offset and vaddr+p_memsz
- ksem_wait: retry loop on waiter-array-full instead of silent discard
- kstack: add free-slot recycling stack (256 entries) to prevent leak
- schedule(): fix fallback dequeue from active (not expired) after swap
- buddy_of(): add bounds check, kfree handles NULL return
- e1000: add compiler memory barriers to MMIO read/write helpers

Deep analysis fixes (4 additional bugs):
- readdir: fix strcpy buffer overflow d_name[24] from name[128] in
  tmpfs, devfs, initrd (use strncpy + null termination)
- mq_send_impl: fix TOCTOU race — copy user data to kbuf before lock
- dlopen: add 32-bit overflow checks for p_vaddr+base and vaddr+p_memsz
- tmpfs/overlayfs: replace unbounded strcpy into vfs.name[128] and
  symlink_target[128] with strncpy

Regression fix:
- percpu: add self-pointer as first field of percpu_data so that
  percpu_get() returns a valid pointer (was returning cpu_index=0 as
  NULL pointer on BSP, causing triple fault)

Verified: make ARCH=x86 builds clean, cppcheck+sparse pass,
102/103 smoke tests PASS (1 pre-existing timeout).

2 months agodocs: add submodule setup instructions, renumber BUILD_GUIDE sections
Tulio A M Mendes [Fri, 3 Apr 2026 20:07:49 +0000 (17:07 -0300)]
docs: add submodule setup instructions, renumber BUILD_GUIDE sections

- Add new section 2 'Getting the Source' with git clone --recursive
  and fallback 'git submodule update --init --recursive' instructions.
- Note that the build system auto-initializes submodules and applies
  patches if not done manually.
- Fix DOOM section: reference submodule init instead of manual git
  clone; simplify to 'make doom'.
- Renumber sections 3-8 to accommodate the new section.

2 months agofix(build): auto-init submodules, auto-apply lwIP patch, fix corrupt patch
Tulio A M Mendes [Fri, 3 Apr 2026 20:07:27 +0000 (17:07 -0300)]
fix(build): auto-init submodules, auto-apply lwIP patch, fix corrupt patch

The build system now handles submodules and patches automatically so
a fresh 'git clone' + 'make' works without manual steps:

- Makefile: add LWIP_SENTINEL target that runs 'git submodule update
  --init --recursive' if lwIP sources are missing.
- Makefile: add .lwip_patched marker target that applies the lwIP
  volatile patch (patches/lwip-tcpip-volatile.patch) once after
  submodule init. Kernel build depends on this marker.
- Makefile: clean target now removes .lwip_patched marker.
- patches/lwip-tcpip-volatile.patch: regenerated with correct hunk
  header (@@ -55,9 +55,9 @@ instead of broken @@ -55,8 +55,8 @@)
  and repo-root-relative paths (third_party/lwip/...).
- user/doom/Makefile: fix error message to reference 'git submodule
  update --init' instead of 'git clone'. Check for actual header file
  instead of just directory existence.
- .gitignore: add .lwip_patched marker, remove temp hello/hello.c.

2 months agofeat(toolchain): dual-libc support — ulibc default, Newlib optional
Tulio A M Mendes [Fri, 3 Apr 2026 19:53:38 +0000 (16:53 -0300)]
feat(toolchain): dual-libc support — ulibc default, Newlib optional

Redesign the toolchain build to safely support both ulibc (dynamic
linking) and Newlib (static linking) with a safe-by-default approach:

Architecture:
- adros.h now has Newlib-safe defaults (LIB_SPEC with -ladros, no
  dynamic-linker in LINK_SPEC) so a fresh 'git clone' + 'build.sh'
  always produces a working toolchain even without prior 'make iso'.
- Step 4b: generates specs from dumpspecs (Newlib-compatible).
- Step 4c: if ulibc build artifacts exist, re-patches specs for ulibc
  (removes -ladros, adds -dynamic-linker /lib/ld.so).
- Step 4d: creates newlib.specs + i686-adros-gcc-newlib wrapper for
  optional Newlib builds.

Bug fixes for fresh-clone reproducibility:
- Newlib build skip check changed from libc.a (overwritten by ulibc)
  to a .built marker file.
- Newlib headers/libs backed up unconditionally (before ulibc check)
  so newlib.specs always has valid include/newlib/ directory.
- Bash cross-compilation now uses newlib.specs explicitly (Bash needs
  Newlib's full POSIX libc: regex, locale, wchar).
- Wrapper script uses expanded paths instead of hardcoded GCC version.

Usage:
  i686-adros-gcc -o out in.c          # ulibc dynamic (default)
  i686-adros-gcc -static -o out in.c  # ulibc static
  i686-adros-gcc-newlib -o out in.c   # Newlib static

2 months agofeat(ulibc): add missing fdopen() and fileno() to stdio
Tulio A M Mendes [Fri, 3 Apr 2026 19:53:14 +0000 (16:53 -0300)]
feat(ulibc): add missing fdopen() and fileno() to stdio

Add POSIX fdopen() and fileno() implementations required by
popen/pclose and other stdio operations:
- fdopen(): wraps an existing file descriptor into a FILE*
- fileno(): returns the file descriptor from a FILE*

2 months agofeat(ulibc): add __libc_init_array/__libc_fini_array and update crt0.S
Tulio A M Mendes [Fri, 3 Apr 2026 19:52:58 +0000 (16:52 -0300)]
feat(ulibc): add __libc_init_array/__libc_fini_array and update crt0.S

Add constructor/destructor support to ulibc:
- New file init_fini.c: implements __libc_init_array() to run .preinit_array
  and .init_array global constructors, and __libc_fini_array() to run
  .fini_array global destructors.
- Updated crt0.S: calls __libc_init_array before main and __libc_fini_array
  after main returns (matching Newlib crt0 behavior). Also uses 'environ'
  symbol (POSIX) and adds .note.GNU-stack for NX compatibility.

2 months agofix(ulibc): rename __environ to environ for POSIX compliance
Tulio A M Mendes [Fri, 3 Apr 2026 19:52:43 +0000 (16:52 -0300)]
fix(ulibc): rename __environ to environ for POSIX compliance

Replace __environ with environ as the primary environment pointer
symbol throughout ulibc and userland commands. POSIX specifies
'environ' as the standard name (IEEE Std 1003.1).

Files changed:
- user/ulibc/src/stdlib.c: environ as primary symbol, update getenv()
- user/ulibc/src/environ.c: all references updated
- user/ulibc/src/execvp.c: pass environ to execve()
- user/ulibc/include/unistd.h: declare extern environ
- user/cmds/env/env.c: use environ
- user/cmds/printenv/printenv.c: use environ

2 months agofix: toolchain build reproducibility — header ordering, libcody, specs, GNU-stack
Tulio A M Mendes [Fri, 3 Apr 2026 15:11:39 +0000 (12:11 -0300)]
fix: toolchain build reproducibility — header ordering, libcody, specs, GNU-stack

- Move AdrOS sysroot headers installation to AFTER Newlib install so
  Newlib's make install doesn't overwrite our custom sys/dirent.h
  (fixes DIR unknown type error on first build)
- Move Newlib header patching (sys/stat.h, sys/signal.h, sys/wait.h,
  glob.h) to after Newlib install (fixes sed on non-existent files)
- Keep libcody in GCC host_libs — required by cc1plus (C++ frontend)
- Add all-libcody to step 4 make targets
- Move specs file creation to after step 4 (GCC full install overwrites it)
- Add .note.GNU-stack to crti-adros.S (fixes executable stack warning)
- Fix *.h glob in libgloss/adros cp (no .h files exist)
- Add toolchain/build/ and toolchain/logs/ to .gitignore
- Update README.md: clarify patches/ are reference diffs, add native-toolchain.patch

2 months agorefactor: build system improvements — ulibc out-of-tree, -Werror, noexecstack, doom...
Tulio A M Mendes [Sat, 14 Mar 2026 17:07:48 +0000 (14:07 -0300)]
refactor: build system improvements — ulibc out-of-tree, -Werror, noexecstack, doom target

1. ulibc out-of-tree build: .o files now go to build/$ARCH/user/ulibc/
   instead of in-tree user/ulibc/src/. BUILDDIR passed from root Makefile.

2. ulibc -Werror: added -Werror, removed -Wno-incompatible-pointer-types.
   Fixed warnings:
   - execve signature in unistd.h: const char* const* -> char* const* (POSIX)
   - execve impl in unistd.c, execle: match new signature
   - pthread.c: removed unused thread_entry_trampoline
   - socket.c: suppress unused addrlen in sendto/recvfrom
   - sh.c, env.c, init.c: remove stale const casts for execve

3. .note.GNU-stack: added -z noexecstack to linker flags in common.mk
   and doom Makefile. GNU_STACK segment now RW (not RWE).

4. make doom: new phony target in root Makefile builds doom via sub-Make,
   passing ULIBC_BUILDDIR. Doom LDFLAGS now use out-of-tree ulibc path.

5. which test: added positive test (which finds sh) alongside the existing
   negative test (which rejects missing cmd).

Tests: 103/103 smoke, 69/69 host utils, cppcheck clean

2 months agofix: doom build + move userspace build output to build/$ARCH/user/
Tulio A M Mendes [Sat, 14 Mar 2026 16:14:55 +0000 (13:14 -0300)]
fix: doom build + move userspace build output to build/$ARCH/user/

- Fix doom.elf build: force i686-elf toolchain (CC/LD := instead of ?=),
  add --unresolved-symbols=ignore-in-shared-libs for libc.so undefined refs
- Reorganize userspace build output from build/user/ to build/$ARCH/user/
  (e.g. build/x86/user/) so all arch artifacts live under one directory
- Sub-Makefiles (common.mk, fulltest, ldso, pie_test) now accept BUILDDIR
  override from root Makefile via ?= operator
- Root Makefile passes BUILDDIR=$(CURDIR)/$(USER_BUILD)/cmds/<name> to
  all sub-Make invocations

Tests: 103/103 smoke, 68/68 host utils, cppcheck clean

2 months agorefactor: reorganize userland into user/cmds/<name>/ with per-program Makefiles
Tulio A M Mendes [Sat, 14 Mar 2026 15:40:11 +0000 (12:40 -0300)]
refactor: reorganize userland into user/cmds/<name>/ with per-program Makefiles

- Move 53 user commands from user/<name>.c to user/cmds/<name>/<name>.c
- Add user/cmds/common.mk shared build rules for dynamically-linked commands
- Add per-program Makefiles for all commands (including fulltest, ldso, pie_test)
- Build all .o/.elf into build/user/cmds/<name>/ (out-of-tree)
- Replace [init] test prefix with [test] in fulltest.c, pie_main.c, test scripts
- Fix find.c and which.c: use opendir/readdir/closedir instead of raw getdents
- Fix ulibc glob.c missing stdio.h include
- Fix ulibc -Wno-incompatible-pointer-types for GCC 14+
- Fix test_host_utils.sh which test set -e issue
- Doom rootfs path changed to /usr/games/doom
- make clean now also cleans ulibc and doom in-tree artifacts

Tests: 103/103 smoke, 28/28 unit, 19/19 security, 68/68 host utils, cppcheck clean

2 months agodocs: mark Tier 6E (USTAR+LZ4 InitRD) as DONE — update all documentation
Tulio A M Mendes [Sat, 14 Mar 2026 14:11:44 +0000 (11:11 -0300)]
docs: mark Tier 6E (USTAR+LZ4 InitRD) as DONE — update all documentation

- TIER6_PLAN.md: 6E marked DONE (mkinitrd produces USTAR+LZ4, kernel parses both)
- POSIX_ROADMAP.md: USTAR initrd format added as feature 91, removed from remaining work
- SUPPLEMENTARY_ANALYSIS.md: boot flow updated to USTAR+LZ4, feature count 91
- README.md: feature count updated to 91, mkinitrd description updated

Tests: 103/103 smoke, 64/64 host, cppcheck clean

2 months agofeat: Tier 6F + 6G — minor POSIX gaps, Bash/Busybox port infrastructure
Tulio A M Mendes [Sat, 14 Mar 2026 13:58:06 +0000 (10:58 -0300)]
feat: Tier 6F + 6G — minor POSIX gaps, Bash/Busybox port infrastructure

Tier 6F — Minor POSIX gaps:
- madvise kernel syscall (no-op, always succeeds) + ulibc wrapper
- execveat kernel syscall (AT_FDCWD mode, delegates to execve) + ulibc wrapper
- mntent.h + mntent.c: setmntent/getmntent/addmntent/endmntent/hasmntopt
  with full /etc/fstab-style whitespace-delimited parsing
- utmp.h + utmp.c: setutent/getutent/getutid/getutline/pututline/endutent
  stubs for login record compatibility (Bash, w, who, login)
- Fix duplicate ssize_t typedef: stdio.h now includes sys/types.h instead
  of redefining ssize_t as 'long' (was conflicting with int32_t in types.h)
- MADV_NORMAL/RANDOM/SEQUENTIAL/WILLNEED/DONTNEED constants in sys/mman.h
- New smoke test: madvise (103/103 pass)

Tier 6G — Bash & Busybox port infrastructure:
- Bash: already integrated in toolchain/build.sh (cross-compiles with
  i686-adros-gcc, static, config.cache for cross-compilation)
- Busybox: ports/busybox/build.sh — download, configure, cross-compile
  script with adros_defconfig (~60 applets: ash, ls, cat, cp, grep, sed,
  awk, sort, ps, kill, mount, find, xargs, etc.)
- ports/busybox/adros_defconfig — minimal static config for AdrOS
- ports/README.md — documentation for building and installing ports

Tests: 103/103 smoke, cppcheck clean, 64/64 host tests

2 months agodocs: mark Tier 6A (Full SMP) as completed in TIER6_PLAN.md
Tulio A M Mendes [Sat, 14 Mar 2026 13:25:12 +0000 (10:25 -0300)]
docs: mark Tier 6A (Full SMP) as completed in TIER6_PLAN.md

2 months agofeat: full SMP scheduling — AP tick accounting, IPI wakeups, load balancing
Tulio A M Mendes [Sat, 14 Mar 2026 13:23:54 +0000 (10:23 -0300)]
feat: full SMP scheduling — AP tick accounting, IPI wakeups, load balancing

Scheduler changes (src/kernel/scheduler.c):
- sched_ap_tick(): per-CPU tick accounting for APs (utime, ITIMER_VIRTUAL,
  ITIMER_PROF) — previously only BSP tracked CPU time
- sched_load_balance(): periodic work stealing — migrates one process from
  busiest to idlest CPU when imbalance >= 2 (avoids ping-pong)
- IPI resched after sleep wakeups: process_wake_check() now sends IPI to
  remote CPUs that received newly-ready processes from the sleep queue
- IPI resched after parent wakeup: process_exit_notify() sends IPI when
  waking a parent blocked in waitpid on a different CPU
- Load counter (sched_pcpu_inc_load) added to wakeup paths that were
  missing it (exit_notify parent wake, sleep queue wake)

Timer changes:
- src/drivers/timer.c: hal_tick_bridge() now calls sched_ap_tick() on APs
  and sched_load_balance() on BSP every 10 ticks (~200ms at 50Hz)
- src/hal/x86/timer.c: APs now go through hal_tick_bridge instead of
  calling bare schedule(), enabling proper AP tick accounting
- Uses percpu_cpu_index() (GS segment read) instead of smp_current_cpu()
  (LAPIC ID linear scan) for faster CPU identification

Tests:
- New SMP parallel fork smoke test: forks 8 children with busy loops,
  verifies all complete with correct exit status (exercises multi-CPU
  scheduling, IPI wakeups, and load balancing)
- 102/102 smoke tests pass, cppcheck clean, 64/64 host tests pass

2 months agodocs: add Tier 6 implementation plan — SMP, multi-arch, Rump, HDA, Bash/Busybox
Tulio A M Mendes [Sat, 14 Mar 2026 12:58:41 +0000 (09:58 -0300)]
docs: add Tier 6 implementation plan — SMP, multi-arch, Rump, HDA, Bash/Busybox

Comprehensive plan for remaining large architectural features:
- 6A: Full SMP scheduling (per-CPU current, AP activation, load balancing)
- 6B: Multi-arch subsystems (ARM64/RISC-V/MIPS PMM+VMM+scheduler)
- 6C: Rump Kernel integration (USB, production drivers)
- 6D: Intel HDA audio driver
- 6E: USTAR initrd format
- 6F: Minor POSIX gaps (madvise, mntent, utmp)
- 6G: Bash & Busybox ports (all blockers resolved)

Recommended order: Bash/Busybox → minor gaps → SMP → Rump → USTAR → HDA → multi-arch

2 months agodocs: update FULL_POSIX_AUDIT.md — all Tier 1-5 items resolved
Tulio A M Mendes [Sat, 14 Mar 2026 12:56:49 +0000 (09:56 -0300)]
docs: update FULL_POSIX_AUDIT.md — all Tier 1-5 items resolved

- umount2 (syscall 138) marked implemented
- wait4 (syscall 139) marked implemented
- All 16 new ulibc headers marked implemented
- All missing functions in stdio/stdlib/string/unistd/termios marked resolved
- pthread sync primitives (mutex/cond/rwlock/key/once/barrier) marked implemented
- /etc/passwd and /etc/group parsing marked implemented
- ENOTSOCK/ENETUNREACH errno codes marked implemented
- Syscall count updated: 139 (138 entries), ~99% POSIX
- All Bash blockers resolved (getpwnam etc.)
- Busybox blockers reduced (getpwent/syslog now done)

2 months agofeat: implement POSIX Tiers 1-5 — complete ulibc headers, syscalls, and sync primitives
Tulio A M Mendes [Sat, 14 Mar 2026 12:54:07 +0000 (09:54 -0300)]
feat: implement POSIX Tiers 1-5 — complete ulibc headers, syscalls, and sync primitives

Tier 1: Add ENOTSOCK/ENETUNREACH errno codes (kernel + ulibc)

Tier 2: 13 new ulibc wrapper headers + source files:
  - sys/socket.h, netinet/in.h, arpa/inet.h, netdb.h, sys/un.h
  - sys/epoll.h, sys/inotify.h, dlfcn.h, spawn.h
  - semaphore.h, mqueue.h, aio.h, sys/shm.h
  - All with syscall wrappers matching kernel syscall numbers

Tier 3: Missing functions in existing headers:
  - termios: cfmakeraw, cfget/setispeed, tcdrain, tcflush, tcgetpgrp/tcsetpgrp
  - string: strsignal
  - stdlib: bsearch, div/ldiv, mkstemp, strtod/strtof
  - stdio: scanf, fscanf, tmpfile, tmpnam
  - unistd: execle, getlogin, confstr, sbrk, link/symlink/readlink wrappers
  - New syslog.h stub (openlog/syslog/closelog)

Tier 4: Kernel syscalls:
  - umount2 (SYSCALL_UMOUNT2=138) with vfs_umount in fs.c
  - wait4 (SYSCALL_WAIT4=139) wrapping process_waitpid + rusage

Tier 5: Medium implementations:
  - glob.h/wordexp.h with directory walk + fnmatch pattern matching
  - pthread_mutex/cond/rwlock/key/once/barrier (futex-based)
  - /etc/passwd + /etc/group file parsing with static root fallback

Build: make clean && make OK, cppcheck clean, 101/101 smoke tests pass

2 months agodocs: update FULL_POSIX_AUDIT.md sections 2B/2C/2D — mark 14 headers, 20+ functions...
Tulio A M Mendes [Sat, 14 Mar 2026 12:19:09 +0000 (09:19 -0300)]
docs: update FULL_POSIX_AUDIT.md sections 2B/2C/2D — mark 14 headers, 20+ functions, 9 errno codes as implemented

Many items listed as missing were already implemented since the original audit.
- Section 2B: 14/27 headers now exist (setjmp, locale, pwd, grp, regex, fnmatch, getopt, libgen, select, resource, utsname, poll, wait, inttypes)
- Section 2C: stdio (perror, popen, fdopen, fileno, getline, ungetc, clearerr), stdlib (strtoul, strtoll/strtoull, atexit, setenv, rand, abort), string (strerror, strtok_r, strpbrk, strspn/strcspn, strnlen), unistd (sleep, execvp/execlp/execl, pipe2, gethostname, ttyname, sysconf/pathconf), signal (sigset_t macros, signal())
- Section 2D: /etc/hosts, FIONREAD, setenv/putenv, process groups, permissions, free()
- errno: 9/11 codes exist (ERANGE, ECONNREFUSED, ETIMEDOUT, EADDRINUSE, EOVERFLOW, ELOOP, ENAMETOOLONG, ESPIPE, EROFS)

2 months agodocs: audit & update all documentation to reflect current implementation
Tulio A M Mendes [Sat, 14 Mar 2026 11:59:58 +0000 (08:59 -0300)]
docs: audit & update all documentation to reflect current implementation

- POSIX_ROADMAP: 75→90 features (15 new: gettimeofday, mprotect, getrlimit/setrlimit, uname, getrusage, socket ops, LZ4, EPOLLET, lazy PLT, MIPS, SysV init, 50+ utilities, host test harness, native toolchain, mount syscall)
- README: 80→102 smoke tests, 47→115 host tests, 75→90 features, 52 userland programs listed
- BUILD_GUIDE: fixed init path (/sbin/fulltest), expanded utility list, updated test counts
- TESTING_PLAN: 80→102 smoke, 19→115 host tests
- SUPPLEMENTARY_ANALYSIS: LZ4 ❌→✅, per-CPU runqueues ❌→✅, test counts aligned
- AUDIT_REPORT: 3 additional fixes marked (sc_* macros, SMAP, schedule race)
- FULL_POSIX_AUDIT: 7/12 syscalls now ✅, Newlib/GCC/Binutils ports ✅ COMPLETE, kernel ~85%→~98% POSIX

2 months agotoolchain: native GCC/Binutils Canadian cross build support
Tulio A M Mendes [Sat, 14 Mar 2026 11:11:46 +0000 (08:11 -0300)]
toolchain: native GCC/Binutils Canadian cross build support

- posix_compat.c: add ioctl, ftw, nftw, dlopen/dlsym/dlclose/dlerror,
  pathconf/fpathconf, execv stubs needed by native Binutils and GCC
- sysroot headers: add extern "C" guards to sys/mman.h, sys/ioctl.h,
  sys/dirent.h for C++ compatibility (prevents mangled symbol mismatches)
- build.sh: add patches for GCC prerequisites (GMP/MPFR/MPC/ISL config.sub),
  libcody removal from host_libs, Binutils tc-i386.c uint32_t type fix
- toolchain/patches/native-toolchain.patch: document all native build patches

Native toolchain produces ELF32 i686 static binaries:
  Binutils 2.42: ar, as, ld, objdump
  GCC 13.2.0: xgcc (2.3MB), cc1 (27MB), cpp, gcov, gcov-tool
  libstdc++: 809 headers + libstdc++.a (built with --with-newlib)

2 months agorefactor: move sysroot compat headers into project + update build.sh
Tulio A M Mendes [Sat, 14 Mar 2026 09:11:24 +0000 (06:11 -0300)]
refactor: move sysroot compat headers into project + update build.sh

All 71 Linux/POSIX compatibility headers that were previously created
directly in /opt/adros-toolchain/i686-adros/include/ are now stored
in newlib/sysroot_headers/ and installed by build.sh via cp -r.

Headers include: asm/*, linux/*, net/*, netinet/*, netpacket/*,
sys/socket.h, sys/mman.h, sys/mount.h, sys/epoll.h, poll.h, netdb.h,
mntent.h, paths.h, shadow.h, syslog.h, utmp.h, endian.h, byteswap.h,
features.h, dlfcn.h, ifaddrs.h, arpa/telnet.h, and more.

Also moved existing inline heredoc headers (sys/ioctl.h, sys/termios.h,
sys/dirent.h) from build.sh into sysroot_headers/ for consistency.

Newlib header patches (sys/stat.h, sys/signal.h, sys/wait.h, glob.h)
are now applied via idempotent sed commands in build.sh:
- sys/stat.h: expose lstat()/mknod() for __adros__
- sys/signal.h: add SA_RESTART/SA_NODEFER/SA_RESETHAND/SA_NOCLDWAIT
- sys/wait.h: add WCOREDUMP macro
- glob.h: add GLOB_NOMATCH

build.sh also updated to compile posix_compat.c into libadros.a.

Tests: 101/101 smoke, cppcheck clean

2 months agofeat: Phase 5 — posix_compat.c + Busybox 1.36.1 cross-compilation
Tulio A M Mendes [Sat, 14 Mar 2026 09:03:52 +0000 (06:03 -0300)]
feat: Phase 5 — posix_compat.c + Busybox 1.36.1 cross-compilation

Add posix_compat.c to libgloss/adros with comprehensive POSIX stubs
needed for cross-compiling Busybox and other ported software:
- poll() via select() emulation
- popen/pclose stubs
- getline/getdelim
- dirname/basename (libgen)
- symlink/lchown/utimes/ftruncate stubs
- getgroups/setpriority/getpriority
- getrlimit/setrlimit (RLIM_INFINITY defaults)
- mount table: setmntent/getmntent/endmntent/getmntent_r
- mount/umount2 stubs
- DNS stubs: getaddrinfo/freeaddrinfo/gai_strerror/getnameinfo/
  gethostbyname/hstrerror/h_errno
- mknod/execvp/sysinfo/getrusage
- signal() via sigaction wrapper
- uname() syscall (INT 0x80, nr=136)
- sigsuspend/vfork (fork emulation)
- mmap (MAP_ANONYMOUS via malloc)/munmap
- rmdir (INT 0x80, nr=40)
- chroot/readlink/realpath/sysconf/fchown stubs
- clock_settime/utimensat stubs
- clearenv/getgrnam/getgrgid stubs
- sethostname/inet_ntoa/htonl/htons/ntohl/ntohs

Busybox 1.36.1 cross-compiled successfully:
- 262KB static ELF32 i686-adros, 70 applets
- Includes: ash shell, cat, ls, cp, mv, rm, mkdir, chmod, chown,
  grep, sed, diff, find, xargs, sort, head, tail, wc, cut, tr,
  printf, echo, env, sleep, date, dd, stat, readlink, hexdump,
  md5sum, sha256sum, hostname, kill, clear, more, and more

Tests: 101/101 smoke, 64/64 host utils, cppcheck clean

2 months agofeat: Phase 3 — POSIX regex engine (regcomp/regexec/regfree/regerror)
Tulio A M Mendes [Sat, 14 Mar 2026 08:01:02 +0000 (05:01 -0300)]
feat: Phase 3 — POSIX regex engine (regcomp/regexec/regfree/regerror)

Add minimal POSIX regex engine to ulibc:
- regex.h: Full POSIX regex API (regex_t, regmatch_t, REG_EXTENDED,
  REG_ICASE, REG_NEWLINE, REG_NOSUB, error codes)
- regex.c: Recursive backtracking matcher supporting:
  - BRE and ERE modes
  - Literals, . (any), ^ (BOL), $ (EOL)
  - Character classes [abc], [^abc], ranges [a-z]
  - POSIX classes [:alpha:] [:digit:] [:alnum:] [:space:] [:upper:]
    [:lower:] [:print:] [:graph:] [:cntrl:] [:punct:] [:xdigit:] [:blank:]
  - Quantifiers: * (BRE/ERE), + ? (ERE only)
  - Alternation: | (ERE), \| (BRE)
  - Grouping: () (ERE), \(\) (BRE)
  - Submatch capture via regmatch_t
  - Case-insensitive matching (REG_ICASE)
  - regerror() with human-readable error messages

Tests: 101/101 smoke, 64/64 host utils, cppcheck clean

2 months agofeat: Phase 2 — getrusage syscall, FIONREAD ioctl, libgen.h, misc ulibc
Tulio A M Mendes [Sat, 14 Mar 2026 07:54:46 +0000 (04:54 -0300)]
feat: Phase 2 — getrusage syscall, FIONREAD ioctl, libgen.h, misc ulibc

Kernel:
- Add SYSCALL_GETRUSAGE (137) returning process utime/stime as struct rusage
- Add FIONREAD (0x541B) ioctl to TTY (returns canon_buf available bytes)
- Add FIONREAD (0x541B) ioctl to pipe (returns pipe buffer count)

ulibc:
- sys/resource.h: Add RUSAGE_SELF/CHILDREN, struct rusage, getrusage()
- resource.c: Add getrusage() syscall wrapper
- libgen.h + libgen.c: basename() and dirname() implementations
- misc.c: gethostname() (returns 'adros'), ttyname(), pipe2() wrapper
- syscall.h: Add SYS_GETRUSAGE (137)

Tests: 101/101 smoke, 64/64 host utils, cppcheck clean

2 months agofeat: Phase 1 — critical ulibc gaps + uname syscall + new smoke test
Tulio A M Mendes [Sat, 14 Mar 2026 07:47:11 +0000 (04:47 -0300)]
feat: Phase 1 — critical ulibc gaps + uname syscall + new smoke test

Kernel:
- Add SYSCALL_UNAME (136) with struct utsname (sysname=AdrOS, machine=i686)

ulibc headers:
- signal.h: Add sigset_t typedef, sigemptyset/sigfillset/sigaddset/sigdelset/
  sigismember inline functions, SIG_BLOCK/SIG_UNBLOCK/SIG_SETMASK constants
- poll.h: New header with struct pollfd, POLLIN/POLLOUT/POLLERR/POLLHUP/POLLNVAL
- sys/utsname.h: New header with struct utsname + uname() declaration
- dirent.h: Add DIR typedef, opendir/readdir/closedir/rewinddir declarations
- unistd.h: Add sysconf/pathconf/fpathconf + _SC_*/_PC_* constants,
  gethostname, ttyname, pipe2 declarations
- stdio.h: Add clearerr, ungetc, getc, putc, getline, getdelim, popen,
  pclose, tmpfile, tmpnam declarations + ssize_t typedef
- syscall.h: Add SYS_UNAME (136)

ulibc implementations:
- poll.c: poll() syscall wrapper
- utsname.c: uname() syscall wrapper
- dirent.c: opendir/readdir/closedir/rewinddir wrapping getdents syscall
- sysconf.c: sysconf/pathconf/fpathconf returning compile-time constants
- stdio.c: clearerr, ungetc, getc, putc, getdelim, getline, popen/pclose,
  tmpfile, tmpnam implementations

Tests:
- fulltest.c: Add uname syscall test (G1) verifying sysname=AdrOS, machine=i686
- smoke_test.exp: Add uname test pattern (101 total tests)

Tests: 101/101 smoke, 64/64 host utils, cppcheck clean

2 months agofeat: Phase 0 — convert libgloss posix_stubs.c from ENOSYS to real AdrOS syscalls
Tulio A M Mendes [Sat, 14 Mar 2026 07:34:43 +0000 (04:34 -0300)]
feat: Phase 0 — convert libgloss posix_stubs.c from ENOSYS to real AdrOS syscalls

Convert ~30 POSIX stub functions in newlib/libgloss/adros/posix_stubs.c from
returning ENOSYS to making real AdrOS kernel syscalls via INT 0x80.

Functions now making real syscalls:
- File ops: dup, dup2, fcntl, pipe, chown, chmod, access, chdir, getcwd
- Process: waitpid, setpgid, setsid, getpgrp, getppid, getuid/geteuid/
  getgid/getegid, setuid/setgid, setreuid/setregid, umask, alarm, sleep
- Signals: sigaction, sigprocmask
- Terminal: tcgetattr, tcsetattr, tcgetpgrp, tcsetpgrp, ioctl
- I/O mux: select, pselect (wraps select)
- Directory: opendir/readdir/closedir/rewinddir (wraps getdents)
- Improved fnmatch with proper * and ? wildcard support

Also adds docs/SELF_HOSTING_ANALYSIS.md with comprehensive POSIX gap
analysis, self-hosting assessment, and prioritized implementation plan.

Bash 5.2.21 rebuilt against new libadros.a (1.7MB static ELF i386).

Tests: 100/100 smoke, 64/64 host utils, cppcheck clean

2 months agofix: Bash cross-compilation for i686-adros toolchain
Tulio A M Mendes [Sat, 14 Mar 2026 06:54:09 +0000 (03:54 -0300)]
fix: Bash cross-compilation for i686-adros toolchain

- build.sh: remove ulibc header install (conflicts with newlib POSIX headers)
- build.sh: add sys/ioctl.h, sys/termios.h, sys/dirent.h stubs to sysroot
- build.sh: add libgloss build step (crt0.o + libadros.a with posix_stubs)
- build.sh: add GCC specs file creation (--start-group/-lc/-ladros/--end-group)
- build.sh: comprehensive Bash cross-compile config.cache (types, headers, funcs)
- build.sh: use -D_POSIX_VERSION=200112L and --allow-multiple-definition
- syscalls.c: remove rename() (provided by newlib libc.a)
- posix_stubs.c: new file with POSIX function stubs for Bash linking
  (dup2, fcntl, pipe, select, pselect, tcgetattr, sigaction, sigprocmask,
   getcwd, waitpid, getpwnam, opendir, access, chdir, umask, sleep, etc.)

Bash 5.2.21 now cross-compiles as a static ELF 32-bit i386 binary.

2 months agofix: newlib crt0.o build error and gcc-full c++tools install failure
Tulio A M Mendes [Sat, 14 Mar 2026 03:26:36 +0000 (00:26 -0300)]
fix: newlib crt0.o build error and gcc-full c++tools install failure

Two fixes to toolchain/build.sh:

1. Newlib: added have_crt0="no" to the adros entry in configure.host.
   Newlib's build system expected crt0.o from libc/sys/adros/, but our
   crt0 is provided by libgloss/adros. Setting have_crt0=no tells newlib
   to skip the crt0.o copy. Also replaced the old Makefile.am/configure.in
   (recursive autotools, wrong for newlib 4.4.0) with a minimal Makefile.inc
   (non-recursive build system).

2. GCC full rebuild: use explicit targets (all-gcc all-target-libgcc,
   install-gcc install-target-libgcc) instead of bare 'make install'.
   The bare install fails on c++tools/g++-mapper-server which is not
   needed for cross-compilation and doesn't build for freestanding
   targets.

Verified:
- i686-adros-gcc 13.2.0 and i686-adros-g++ 13.2.0 both work
- C and C++ cross-compilation succeeds
- newlib libc.a and libm.a installed
- 100/100 smoke tests pass (10 sec)

2 months agofix: rewrite toolchain patching to use sed instead of fragile unified diffs
Tulio A M Mendes [Sat, 14 Mar 2026 02:00:57 +0000 (23:00 -0300)]
fix: rewrite toolchain patching to use sed instead of fragile unified diffs

The original patch files (binutils-adros.patch, gcc-adros.patch,
newlib-adros.patch) used hardcoded line numbers that didn't match the
actual source tarballs (binutils 2.42, gcc 13.2.0, newlib 4.4.0),
causing config.sub and ld/configure.tgt hunks to fail.

Replaced the entire patching approach with pattern-based sed commands
that match unique context strings instead of line numbers. This is
robust against upstream line shifts across versions.

Changes to toolchain/build.sh:
- patch_config_sub(): inserts adros case before pikeos*) and adds
  adros* to the OS validation list next to dicos*
- patch_binutils(): sed-patches bfd/config.bfd, gas/configure.tgt,
  ld/configure.tgt using pattern anchors
- patch_gcc(): sed-patches gcc/config.gcc, libgcc/config.host;
  creates adros.h, crti-adros.S, crtn-adros.S inline
- patch_newlib(): sed-patches newlib/configure.host, sys/config.h,
  libgloss/configure.ac; creates sys/adros/ and libgloss/adros/
  files inline

All idempotent (marker files + grep guards).

Verified:
- config.sub returns 'i686-pc-adros' for all 3 packages
- binutils configure --target=i686-adros succeeds
- 100/100 smoke tests pass (13 sec)

2 months agofeat: i686-adros cross-toolchain infrastructure + kernel test expansion
Tulio A M Mendes [Sat, 14 Mar 2026 01:19:19 +0000 (22:19 -0300)]
feat: i686-adros cross-toolchain infrastructure + kernel test expansion

Toolchain Build System (toolchain/):
- toolchain/build.sh: Automated 5-stage build script
  Stage 1: Binutils 2.42 (i686-adros assembler/linker)
  Stage 2: GCC 13.2 bootstrap (C only, no libc)
  Stage 3: Newlib 4.4.0 (C library with libgloss/adros stubs)
  Stage 4: GCC 13.2 full rebuild (C/C++ with Newlib sysroot)
  Stage 5: Bash 5.2.21 cross-compilation (optional, static)

- toolchain/patches/binutils-adros.patch:
  config.sub, bfd/config.bfd, gas/configure.tgt, ld/configure.tgt
  Adds i686-adros as ELF32 target using i386 backend

- toolchain/patches/gcc-adros.patch:
  config.sub, gcc/config.gcc, gcc/config/i386/adros.h,
  libgcc/config.host, crti-adros.S, crtn-adros.S
  Defines __adros__, __unix__, ELF32 format, crt0.o startup,
  -lc -ladros -lgcc link spec, i386 wchar/size/ptrdiff types

- toolchain/patches/newlib-adros.patch:
  config.sub, newlib/configure.host, newlib/libc/include/sys/config.h,
  libgloss/configure.in, libgloss/adros/configure.in+Makefile.in,
  newlib/libc/sys/adros/ stubs
  SIGNAL_PROVIDED, MALLOC_PROVIDED, HAVE_OPENDIR flags

- toolchain/README.md: Build prerequisites, usage, sysroot layout,
  troubleshooting guide

ulibc Enhancements:
- errno.h: Full POSIX errno set (sorted, no duplicates), matches kernel
- ctype.h: Added iscntrl, isgraph, ispunct, isascii, isblank, toascii
- signal.h: Added SA_RESTART, SA_NOCLDSTOP, SA_NOCLDWAIT, SA_NODEFER,
  SA_RESETHAND, SIG_ERR, sighandler_t typedef, signal() declaration

Kernel Test Expansion:
- fulltest.c: 3 new syscall tests
  - gettimeofday: verify epoch timestamp > 1e9
  - mprotect: brk page, write, mprotect RW, verify success
  - getrlimit/setrlimit: get RLIMIT_NOFILE, set lower, read back, restore
- smoke_test.exp: 3 new patterns (100 total)

100/100 smoke tests pass (11 sec), cppcheck clean.

2 months agofeat: POSIX Phases 2-6 — syscalls, ulibc functions, headers, malloc, Newlib prep
Tulio A M Mendes [Sat, 14 Mar 2026 00:47:53 +0000 (21:47 -0300)]
feat: POSIX Phases 2-6 — syscalls, ulibc functions, headers, malloc, Newlib prep

Phase 2 — Critical Kernel Syscalls:
- SYSCALL_GETRLIMIT (129) / SYSCALL_SETRLIMIT (130): per-process resource
  limits (RLIMIT_NOFILE, RLIMIT_STACK, RLIMIT_CORE, etc.) stored in process
  struct, inherited on fork, enforced by setrlimit with root privilege check
- SYSCALL_SETSOCKOPT (131) / SYSCALL_GETSOCKOPT (132): SO_REUSEADDR,
  SO_KEEPALIVE, SO_RCVBUF/SNDBUF, SO_ERROR, SO_TYPE via lwIP
- SYSCALL_SHUTDOWN (133): TCP half-close via tcp_shutdown
- SYSCALL_GETPEERNAME (134) / SYSCALL_GETSOCKNAME (135): socket address query
- Added missing errno codes: ENOPROTOOPT, EOVERFLOW, ELOOP

Phase 3 — Critical ulibc Functions (12 new source files):
- setjmp/longjmp/_setjmp/_longjmp/sigsetjmp/siglongjmp (i386 assembly)
- sleep()/usleep() wrappers over nanosleep
- execvp()/execlp()/execl() with PATH search
- getopt()/getopt_long() full POSIX implementation
- strerror()/perror() with 35-entry error string table
- strtoul()/strtoll()/strtoull() with base auto-detection
- setenv()/unsetenv()/putenv() with owned environ array
- signal() wrapper over sigaction (SA_RESTART)
- abort() sends SIGABRT, atexit() with 32-handler registry
- exit() now calls atexit handlers in reverse order
- rand()/srand() LCG PRNG (RAND_MAX=0x7FFF)
- strtok_r(), strnlen(), strspn(), strcspn(), strpbrk()

Phase 4 — Critical Headers (9 new headers):
- setjmp.h: jmp_buf, sigjmp_buf for i386
- locale.h: LC_* constants, struct lconv, setlocale/localeconv stubs
- pwd.h/grp.h: struct passwd/group, getpwnam/getpwuid/getpwent stubs
- getopt.h: struct option, getopt_long declarations
- fnmatch.h + fnmatch(): glob-style pattern matching with FNM_PATHNAME
- sys/select.h: fd_set, FD_SET/CLR/ISSET/ZERO macros, select()
- sys/resource.h: struct rlimit, RLIMIT_* constants, getrlimit/setrlimit

Phase 5 — Proper malloc with free():
- Replaced bump allocator with address-ordered free-list allocator
- 8-byte aligned blocks with 8-byte header (size | used_bit, next_free)
- First-fit allocation with block splitting
- free() with address-ordered insertion and bidirectional coalescing
- realloc() now preserves old data size from block header

Phase 6 — Newlib Port (from previous commit):
- libgloss stubs already complete, syscall numbers updated

97/97 smoke tests pass, cppcheck clean.

2 months agofeat: gettimeofday + mprotect syscalls + Newlib libgloss port
Tulio A M Mendes [Sat, 14 Mar 2026 00:18:08 +0000 (21:18 -0300)]
feat: gettimeofday + mprotect syscalls + Newlib libgloss port

Kernel:
- Add SYSCALL_GETTIMEOFDAY (127): returns RTC epoch seconds + TSC-derived
  microseconds via struct timeval. Timezone arg ignored per POSIX.
- Add SYSCALL_MPROTECT (128): changes page protection on heap, mmap, and
  stack regions. Converts POSIX PROT_READ/WRITE/EXEC to VMM flags and
  calls vmm_protect_range(). Validates ownership before modifying PTEs.

ulibc:
- Add gettimeofday() wrapper in sys/time.h + time.c
- Add mprotect() wrapper in sys/mman.h + mman.c
- Add SYS_GETTIMEOFDAY/SYS_MPROTECT to ulibc syscall.h

Newlib port (newlib/):
- newlib/libgloss/adros/crt0.S: C runtime startup for AdrOS, calls
  __libc_init_array/__libc_fini_array for Newlib/C++ ctor/dtor support
- newlib/libgloss/adros/syscalls.c: all 21 Newlib-required OS stubs
  (_exit, _read, _write, _open, _close, _lseek, _fstat, _stat, _isatty,
  _kill, _getpid, _sbrk, _link, _unlink, _fork, _execve, _wait, _times,
  _gettimeofday, _rename, _mkdir) implemented via INT 0x80
- newlib/libgloss/adros/Makefile: builds crt0.o + libadros.a
- newlib/README.md: build instructions for full Newlib cross-compilation
- newlib/patches/README.md: documents config.sub, configure.host, and
  libgloss/configure.in changes needed in Newlib source tree

All 21 Newlib libgloss stubs are now implemented. To build Newlib:
  1. Copy libgloss/adros/ into Newlib source tree
  2. Add i686-*-adros* target to configure files
  3. Build with: ../configure --target=i686-adros && make

2 months agodocs: full POSIX/Unix audit + fix git-clone breakage with submodules
Tulio A M Mendes [Sat, 14 Mar 2026 00:02:44 +0000 (21:02 -0300)]
docs: full POSIX/Unix audit + fix git-clone breakage with submodules

- Add docs/FULL_POSIX_AUDIT.md: comprehensive analysis of POSIX gaps,
  build system issues, and porting requirements for Newlib/GCC/Binutils/
  Bash/Busybox
- Convert lwIP and doomgeneric from untracked nested repos to proper
  git submodules (.gitmodules) so git clone --recursive works
- Update .gitignore: remove third_party/ and user/doom/doomgeneric
  exclusions now that they are tracked as submodules

Build system was broken after git clone: lwIP (required) and doomgeneric
(optional) directories were empty because they were gitignored nested
repos with no .gitmodules. Now users can:
  git clone --recursive <url>
  make CROSS=1 && make iso

2 months agofix: build system cleanup — replace non-freestanding <string.h> with utils.h, paramet...
Tulio A M Mendes [Fri, 13 Mar 2026 23:45:00 +0000 (20:45 -0300)]
fix: build system cleanup — replace non-freestanding <string.h> with utils.h, parameterize toolchain, fix header conflicts

- Kernel: replace #include <string.h> (not freestanding) with #include "utils.h"
  in src/arch/x86/fpu.c, src/kernel/fpu.c, src/rump/rumpuser_adros.c
- Makefile: define USER_CC/USER_LD/USER_AR variables for userspace cross-compilation,
  replacing all hardcoded i686-elf-gcc/ld references in FULLTEST, DYN_CC, DYN_LD,
  LDSO, PIE_SO, PIE_ELF rules
- Makefile: pass USER_CC/USER_LD/USER_AR to ulibc sub-make for consistent toolchain
- Makefile: add missing .PHONY targets (test-battery, run-arm, run-riscv, run-mips)
- ulibc Makefile: use ?= for CC/AS/AR/LD so parent can override
- doom Makefile: use ?= for CC/LD so parent can override
- ulibc: remove duplicate stat/fstat declarations from unistd.h (conflicted with
  sys/stat.h when both included); proper declarations remain in sys/stat.h per POSIX

3 months agofeat: /dev/hdX block device nodes and /proc/dmesg
Tulio A M Mendes [Tue, 17 Feb 2026 08:01:55 +0000 (05:01 -0300)]
feat: /dev/hdX block device nodes and /proc/dmesg

1. ATA block devices in devfs:
   Detected ATA drives (hda, hdb, hdc, hdd) are now registered as
   block device nodes in /dev via ata_register_devfs(). Each node
   supports read/write at byte offsets (with sector-aligned I/O
   internally). Previously, drives were detected but invisible
   in /dev, so 'ls /dev' showed no disk devices and mount had to
   use ata_name_to_drive() internally.

2. /proc/dmesg:
   Added /proc/dmesg to procfs that reads the kprintf ring buffer
   via klog_read(). Uses heap allocation (16KB) to avoid kernel
   stack overflow. The 'dmesg' command now works correctly.

3 months agofeat: ALT key support in PS/2 keyboard driver
Tulio A M Mendes [Tue, 17 Feb 2026 07:51:33 +0000 (04:51 -0300)]
feat: ALT key support in PS/2 keyboard driver

Added alt_held state tracking for Left ALT (scancode 0x38/0xB8)
and Right ALT (E0 0x38/0xB8). When ALT is held, the driver
emits ESC (0x1B) prefix before the character, which is the
standard terminal encoding for Alt+key combinations.

Also handles Right CTRL (E0 0x1D/0x9D) which was previously
ignored since it uses E0-prefixed scancodes.

Summary of modifier keys now supported:
  - Shift (L/R): uppercase and symbol characters
  - CTRL (L/R):  control characters (c & 0x1F)
  - ALT (L/R):   ESC prefix + character

3 months agofix: mount command -t option argument parsing
Tulio A M Mendes [Tue, 17 Feb 2026 07:47:48 +0000 (04:47 -0300)]
fix: mount command -t option argument parsing

The mount command parsed device=argv[1] and mountpoint=argv[2]
BEFORE scanning for -t, so 'mount -t fat /dev/hdb /test' set
device='-t' and mountpoint='fat' instead of the correct values.

Rewrote argument parsing to scan for -t first, then collect
remaining positional arguments as device and mountpoint.
Added usage message for missing arguments.

3 months agofix: keyboard CTRL key support for CTRL+C/CTRL+Z in shell
Tulio A M Mendes [Tue, 17 Feb 2026 07:45:06 +0000 (04:45 -0300)]
fix: keyboard CTRL key support for CTRL+C/CTRL+Z in shell

Root cause: The PS/2 keyboard driver had no CTRL key tracking.
When CTRL was held and a letter pressed, the driver sent the
plain letter character (e.g., 'c') instead of the control
character (e.g., 0x03 for CTRL+C).

Fix: Added ctrl_held state tracking in hal/x86/keyboard.c for
Left CTRL (scancode 0x1D press / 0x9D release). When CTRL is
held and a letter key is pressed, emit (c & 0x1F) — the
standard control character encoding:
  CTRL+C = 0x03, CTRL+Z = 0x1A, CTRL+D = 0x04, etc.

Shell read_line_edit() already handled these bytes correctly:
  - 0x03 (CTRL+C): prints ^C, newline, cancels current line
  - 0x1A (CTRL+Z): ignored at prompt (no foreground job)
  - 0x04 (CTRL+D): EOF on empty line

During command execution, tty_restore() re-enables ISIG, so
the TTY kernel driver intercepts CTRL+C/CTRL+Z and sends
SIGINT/SIGTSTP to the foreground process group via TIOCSPGRP.

3 months agofix: CTRL+C/CTRL+Z job control and doom build errors
Tulio A M Mendes [Tue, 17 Feb 2026 07:30:53 +0000 (04:30 -0300)]
fix: CTRL+C/CTRL+Z job control and doom build errors

1. CTRL+C/CTRL+Z: Shell now calls setsid() instead of setpgid(0,0)
   to create a proper session. This initializes tty_session_id so
   TIOCSPGRP can actually set child processes as the foreground
   group. Previously, TIOCSPGRP silently returned -EPERM because
   tty_session_id was 0.

2. Doom mkdir: Added mkdir/stat/fstat/chmod declarations to
   user/ulibc/include/sys/stat.h where POSIX expects them.
   Doom's m_misc.c includes sys/stat.h for mkdir().

3. Doom __divdi3: Added libgcc.a to doom link step to provide
   compiler runtime helpers for 64-bit arithmetic on i386.

3 months agofeat: shell job control (&, &&, ||) and CTRL+C/CTRL+Z support
Tulio A M Mendes [Tue, 17 Feb 2026 07:15:18 +0000 (04:15 -0300)]
feat: shell job control (&, &&, ||) and CTRL+C/CTRL+Z support

1. Background processes (&): trailing & forks without waiting, prints
   [bg] PID. Works for simple commands and pipelines.

2. Command chaining (&&): executes next command only if previous
   succeeded (exit status 0). Skips remaining && chain on failure
   until a || or ; is found.

3. OR chaining (||): executes next command only if previous failed
   (exit status != 0). Skips remaining || chain on success until
   a && or ; is found.

4. CTRL+C / CTRL+Z: shell ignores SIGINT/SIGTSTP/SIGQUIT. Child
   processes get their own process group (setpgid) and are set as
   the foreground group (TIOCSPGRP). CTRL+C sends SIGINT only to
   the child, not the shell. After child exits, shell restores
   itself as foreground group.

New files:
- user/ulibc/include/sys/wait.h: WIFEXITED/WIFSIGNALED/etc macros

Modified:
- user/sh.c: process_line rewritten for ;/&&/||/& operators,
  run_simple and run_pipeline use setpgid+TIOCSPGRP job control
- user/ulibc/include/termios.h: added TIOCSPGRP/TIOCGPGRP

3 months agofix: init PID 1, ls -l permissions/size, doom dynamic linking
Tulio A M Mendes [Tue, 17 Feb 2026 07:01:05 +0000 (04:01 -0300)]
fix: init PID 1, ls -l permissions/size, doom dynamic linking

1. Init process now gets PID 1 (like Linux): next_pid starts at 2,
   sched_assign_pid1() explicitly assigns PID 1 to the init process
   after it loads. Kernel threads and AP idles get PIDs 2+.

2. ls -l now shows permissions, nlink, size via stat() on each entry
   instead of just the type character.

3. doom.elf Makefile switched from static linking (libulibc.a) to
   dynamic linking (libc.so via ld.so) like all other user commands.

3 months agofix: diskfs kfree-on-static-root, mount syscall, user addr space 8MiB->1GiB
Tulio A M Mendes [Tue, 17 Feb 2026 06:50:42 +0000 (03:50 -0300)]
fix: diskfs kfree-on-static-root, mount syscall, user addr space 8MiB->1GiB

Bug 1: ls /disk heap corruption — diskfs_close_impl called kfree on
static g_root BSS variable. Added guard: skip kfree when node == g_root.

Bug 2: mount command only displayed mounts. Added SYSCALL_MOUNT (126)
with support for tmpfs and disk-based filesystems (diskfs/fat/ext2/persistfs).
Updated userspace mount to call the syscall with device, mountpoint, and
-t fstype args.

Bug 3: doom 'Unable to allocate 5 MiB' — user address space was capped
at 8 MiB (USER_STACK_BASE=0x00800000). Raised to 1 GiB (0x40000000) in
elf.c, usermode.c, and syscall_brk_impl.

3 months agotest: add host tests for sed, awk, who, find, which (64/64 pass, 2 skip)
Tulio A M Mendes [Tue, 17 Feb 2026 06:27:57 +0000 (03:27 -0300)]
test: add host tests for sed, awk, who, find, which (64/64 pass, 2 skip)

3 months agofix: UAF in alarm queue on reap, FD leak on self-SIGKILL and posix_spawn execve failure
Tulio A M Mendes [Tue, 17 Feb 2026 06:26:48 +0000 (03:26 -0300)]
fix: UAF in alarm queue on reap, FD leak on self-SIGKILL and posix_spawn execve failure

3 months agofeat: add sed, awk, who, top, du, find, which commands + shell heredoc support
Tulio A M Mendes [Tue, 17 Feb 2026 06:20:25 +0000 (03:20 -0300)]
feat: add sed, awk, who, top, du, find, which commands + shell heredoc support

3 months agofix: ps shows [kernel] for empty cmdline, ls sorts alphabetically, add qsort to ulibc
Tulio A M Mendes [Tue, 17 Feb 2026 06:13:11 +0000 (03:13 -0300)]
fix: ps shows [kernel] for empty cmdline, ls sorts alphabetically, add qsort to ulibc

3 months agofix: create common mountpoint directories under root
Tulio A M Mendes [Tue, 17 Feb 2026 06:00:09 +0000 (03:00 -0300)]
fix: create common mountpoint directories under root

3 months agofix: handle ANSI clear/home sequences on VGA console
Tulio A M Mendes [Tue, 17 Feb 2026 05:56:08 +0000 (02:56 -0300)]
fix: handle ANSI clear/home sequences on VGA console

3 months agotest: expand test suite — 97 smoke tests, 56 host utility tests
Tulio A M Mendes [Tue, 17 Feb 2026 05:34:53 +0000 (02:34 -0300)]
test: expand test suite — 97 smoke tests, 56 host utility tests

- Add 8 new kernel tests to fulltest.c:
  /proc/PID/cmdline, /proc/PID/status, /dev/console, multi-pty,
  dup standalone, pipe EOF, readdir /proc, readdir /bin

- Create tests/test_host_utils.sh: host-compilable test harness for
  20 userspace utilities (echo, cat, head, tail, wc, sort, uniq, cut,
  grep, tr, basename, dirname, tee, dd, pwd, uname, id, printenv,
  cp, mv, touch, rm, mkdir, rmdir, ln) — 56 tests

- Fix echo: leading space when flags shift arg index
- Fix tail: off-by-one with trailing newline
- Fix tee/touch/cp/mv/dd: missing mode arg on open(O_CREAT)
- Fix ulibc open(): make variadic to accept optional mode

- Update smoke_test.exp with 8 new patterns (97 total)
- Add host utility tests to Makefile test-host target

Tests: 97/97 smoke, 28+19+56=103 host, cppcheck clean

3 months agofix: shell/command bugs, new utilities, procfs race condition
Tulio A M Mendes [Tue, 17 Feb 2026 04:31:30 +0000 (01:31 -0300)]
fix: shell/command bugs, new utilities, procfs race condition

Shell fixes:
- Fix DELETE key showing ~ (handle \x1b[3~ escape sequence + Home/End)
- Fix builtin redirections (echo > file now works via saved fd restore)
- Fix initrd readdir (root cause of ls /bin empty + tab completion broken)

Command fixes:
- Fix cut -dX/-fN combined argument parsing (POSIX style)
- Fix ps showing ? for PIDs: add cmdline[128] to process struct, populate in execve + init
- Fix procfs race condition: use sched_lock for process list traversal
- Make sched_lock non-static for procfs access

New commands (22 total):
- Previous session: mount, umount, env, kill, sleep, clear, ps, df, free, tee, basename, dirname, rmdir
- This session: grep, id, uname, dmesg, printenv, tr, dd, pwd, stat

Arch contamination note: vdso.c includes arch/x86/kernel_va_map.h directly (acceptable for now, only x86 target)

Tests: 89/89 smoke, cppcheck clean

3 months agouserspace: major refactoring — dynamic linking, new commands, SysV init
Tulio A M Mendes [Tue, 17 Feb 2026 02:22:03 +0000 (23:22 -0300)]
userspace: major refactoring — dynamic linking, new commands, SysV init

Infrastructure:
- Rename user/init.c → user/fulltest.c, move to /sbin/fulltest
- Remove .elf extensions from all InitRD binaries
- Build ulibc as shared library (libc.so) with PIC objects
- Fix crt0.S to correctly parse argc/argv/envp from execve stack
- Fix ld.so: restore stack pointer before jumping to program entry
- Fix ld.so: add R_386_GLOB_DAT/R_386_COPY eager relocation processing
- Fix ld.so: move find_shlib_info() before relocation processing
- Create dyn_linker.ld for dynamically-linked PIE executables
- Add missing ulibc functions: waitpid, getdents, stat, fstat, chmod,
  chown, link, symlink, readlink, sigaction
- Add dirent.h header, expand sys/stat.h with permission macros
- Update Makefile: dynamic linking build rules for all user commands

Rewritten commands (now use ulibc + dynamic linking via ld.so):
- cat: proper POSIX with stdin/- support, 4KB buffer
- echo: -n, -e, -E flags, escape sequence handling
- ls: -a, -l flags, getdents-based directory listing
- mkdir: -p flag for recursive parent creation
- rm: -r, -f, -d flags

New commands (all dynamically linked):
- File management: cp, mv, touch, ln (-s for symlinks)
- Text processing: head (-n), tail (-n), wc (-l/-w/-c),
  sort (-r/-n), uniq (-c/-d), cut (-d/-f)
- Permissions: chmod (octal), chown (owner:group), chgrp
- System info: date, hostname, uptime

Shell improvements (/bin/sh):
- Variable assignment (VAR=value) and expansion ($VAR, ${VAR}, $?)
- Environment variables (export VAR=value)
- Line editing: left/right arrows, Ctrl+A/E/U, backspace
- Command history: up/down arrows, dedup
- Builtins: cd, pwd, export, unset, set, type, exit, echo
- PATH-based command resolution with colon-separated dirs
- Quote handling (single and double quotes)
- Semicolon command separation
- Append redirection (>>)

SysV init (/sbin/init):
- Parses /etc/inittab (id:runlevels:action:process format)
- Actions: sysinit, wait, once, respawn, ctrlaltdel, shutdown
- Runlevel support (0-6, S)
- Default behavior without inittab: run rcS then respawn /bin/sh
- Child reaping and respawn loop

Kernel changes:
- Default init path changed to /sbin/init (was /bin/init.elf)
- grub.cfg: add fulltest and shell entries with console=serial
- Update fulltest.c references: /bin/init.elf → /sbin/fulltest,
  /bin/echo.elf → /bin/echo, /bin/pie_test.elf → /bin/pie_test

Tests: 89/89 smoke (9s), cppcheck clean

3 months agofix: KVA_IOAPIC VA collision with BSS — move from 0xC0201000 to 0xC0401000
Tulio A M Mendes [Tue, 17 Feb 2026 00:45:26 +0000 (21:45 -0300)]
fix: KVA_IOAPIC VA collision with BSS — move from 0xC0201000 to 0xC0401000

Root cause: multiboot_copy (64KB static buffer) starts at VA 0xC0200FE0,
spanning pages 0xC0200000-0xC0210000. KVA_IOAPIC at 0xC0201000 mapped
IOAPIC MMIO over the BSS page containing the multiboot2 cmdline tag data.
After arch_platform_setup, reading bi->cmdline returned IOAPIC register
data (zeros) instead of the original cmdline string.

Symptom: [CMDLINE] "" regardless of GRUB menu entry selected.
Classic Heisenbug — adding a debug kprintf before IOAPIC init read the
correct data, masking the corruption.

Fix: move KVA_IOAPIC to 0xC0401000 (next to LAPIC at 0xC0400000),
well past _end at 0xC0265728. Updated VA map comment to reflect
current BSS extent (~0xC0266000).

3 months agofix: VirtualBox compatibility — UART detection, alarm timing, usermode segments,...
Tulio A M Mendes [Tue, 17 Feb 2026 00:12:25 +0000 (21:12 -0300)]
fix: VirtualBox compatibility — UART detection, alarm timing, usermode segments, user-mode #GP

4 fixes for VirtualBox compatibility + 1 cosmetic:

1. UART hardware detection (fixes boot freeze with serial disabled)
   - hal_uart_init() now probes the scratch register before configuring
   - All UART operations (putc, drain_rx, poll_rx, try_getc) guarded
     behind uart_present flag — prevents infinite loop on floating bus
   - console_init() auto-enables VGA when no UART detected so boot
     messages are visible
   - Added hal_uart_is_present() API + stubs for ARM/MIPS/RISC-V

2. alarm/SIGALRM test: replace 20M-iteration busy-loop with nanosleep
   polling (50ms × 40 = 2s max wait). Fast VirtualBox CPUs completed
   the busy-loop before the 1-second alarm fired.

3. x86_enter_usermode: load DS/ES/FS/GS=0x23 before iret to ring 3.
   Without this, iret nulls segment registers (kernel DPL=0 < new CPL=3
   per Intel SDM §6.12.1). On QEMU this was masked by early context
   switches that fixed DS via x86_enter_usermode_regs, but VirtualBox
   with Hyper-V acceleration may expose the race window.

4. User-mode exception handling: deliver SIGSEGV for any ring-3
   exception (#GP, #UD, etc.) instead of kernel panic. Previously only
   #PF (14) had this handling. A user-mode #GP now kills the process
   cleanly instead of halting the entire system.

5. LAPIC timer ticks printed in decimal instead of hex.

89/89 smoke tests pass, cppcheck clean.

3 months agotest: expand smoke suite to 89 tests + fix SMP orphan reparenting
Tulio A M Mendes [Mon, 16 Feb 2026 22:56:49 +0000 (19:56 -0300)]
test: expand smoke suite to 89 tests + fix SMP orphan reparenting

New userspace tests in init.c:
- E1: setuid/setgid/seteuid/setegid credential manipulation
- E2: fcntl F_GETFL/F_SETFL (O_NONBLOCK toggle)
- E3: fcntl F_GETFD/F_SETFD (FD_CLOEXEC)
- E4: sigsuspend (block SIGUSR1, self-signal, sigsuspend unblocks)
- E5: orphan reparenting (grandchild reparented to init after middle exits)
- Boot-time LZ4 Frame decompression pattern check

Kernel fix — SMP orphan reparenting:
- process_exit_notify() hardcoded parent_pid=1 for reparenting, but with
  SMP the AP idle processes consume PIDs 1-3 before the init userspace
  process is created (PID 4+).
- Added sched_set_init_pid() to register the actual init process PID.
- arch_platform.c calls sched_set_init_pid(current_process->pid) before
  entering userspace, so orphan reparenting targets the correct process.

89/89 smoke tests pass (9s), cppcheck clean.

3 months agofix: fork FD race condition + orphaned zombie memory leak
Tulio A M Mendes [Mon, 16 Feb 2026 22:32:09 +0000 (19:32 -0300)]
fix: fork FD race condition + orphaned zombie memory leak

Bug 1 — Fork FD race (HIGH severity):
  process_fork_create() enqueued the child to the runqueue under
  sched_lock, but syscall_fork_impl() copied file descriptors AFTER
  the function returned — with sched_lock released. On SMP, the child
  could be scheduled on another CPU and reach userspace before FDs
  were populated, seeing NULL file descriptors.

  Fix: move FD copying (with refcount bumps) into process_fork_create()
  itself, under sched_lock, before the child is enqueued. Added proper
  rollback of refcount bumps if kstack_alloc fails.

Bug 2 — Orphaned zombie leak (MEDIUM severity):
  When a process exited, its children were not reparented to PID 1
  (init). Zombie children of exited parents could never be reaped via
  waitpid, leaking process structs and kernel stacks forever.

  Fix: in process_exit_notify(), iterate the process list and reparent
  all children to PID 1. If any reparented child is already a zombie
  and init is blocked in waitpid(-1), wake init immediately.

Also verified (no bugs found):
- EOI handling correct (sent before handlers, spurious skips EOI)
- Lock ordering safe (all locks use irqsave, no cross-CPU ABBA)
- Heap has double-free and corruption detection
- User stack has guard pages

83/83 smoke tests pass, cppcheck clean.

3 months agofeat: LZ4 official Frame format for initrd compression/decompression
Tulio A M Mendes [Mon, 16 Feb 2026 22:24:18 +0000 (19:24 -0300)]
feat: LZ4 official Frame format for initrd compression/decompression

Replace custom 'LZ4B' block wrapper with the official LZ4 Frame format
(spec: https://github.com/lz4/lz4/blob/dev/doc/lz4_Frame_format.md).

Compressor (tools/mkinitrd.c):
- Write official frame: magic 0x184D2204, FLG/BD descriptor with
  content size and content checksum flags, xxHash-32 header checksum,
  data block, EndMark, xxHash-32 content checksum
- Fix block compressor MFLIMIT: last match must start >= 12 bytes
  before end of block (was 5, violating spec)

Decompressor (src/kernel/lz4.c):
- New lz4_decompress_frame() parses frame header, verifies header
  checksum via xxHash-32, decompresses blocks, verifies content checksum
- Existing lz4_decompress_block() unchanged (used internally)

Kernel initrd loader (src/drivers/initrd.c):
- Detect official LZ4 Frame magic (0x184D2204) first
- Keep legacy LZ4B detection as backward-compat fallback
- initrd_init() now takes size parameter for frame bounds checking

New files:
- include/xxhash32.h: standalone header-only xxHash-32 implementation

Cross-compatibility verified:
- 'lz4 -t initrd.img' validates our frame (official lz4 v1.9.4)
- 'lz4 -d initrd.img' decompresses correctly, tar lists all 12 files
- 83/83 smoke tests pass, cppcheck clean

3 months agofix: PMM total_memory overflow — MMAP reserved regions near 4GB inflated highest_addr
Tulio A M Mendes [Mon, 16 Feb 2026 22:08:36 +0000 (19:08 -0300)]
fix: PMM total_memory overflow — MMAP reserved regions near 4GB inflated highest_addr

Root cause: Multiboot2 MMAP includes a BIOS reserved region at
0xFFFC0000-0x100000000. The end address (0x100000000) overflows
uint32_t when stored in a uint64_t local variable, and (unsigned)
truncation yields 0 — hence '[PMM] total_memory bytes: 0x0'.

Fixes:
- Use uint32_t locals (32-bit x86 caps RAM at 512 MB anyway)
- Clamp MMAP end addresses to 0xFFFFFFFF before comparison
- Only track highest_avail from AVAILABLE regions, not reserved
- Use 'if' instead of 'else if' so both BASIC_MEMINFO and MMAP
  are processed in the same pass
- Print total_memory and freed_frames in decimal with MB suffix

Before: [PMM] total_memory bytes: 0x0
After:  [PMM] total_memory: 134086656 bytes (127 MB)

83/83 smoke tests pass, cppcheck clean.

3 months agofeat: SMP load balancing for fork/clone + IPI resched
Tulio A M Mendes [Mon, 16 Feb 2026 21:08:55 +0000 (18:08 -0300)]
feat: SMP load balancing for fork/clone + IPI resched

Enable load balancing in process_fork_create and process_clone_create:
both now dispatch to the least-loaded CPU via sched_pcpu_least_loaded().

All three process creation functions (create_kernel, fork, clone) now
send IPI_RESCHED to the target CPU after releasing sched_lock, waking
idle APs immediately when work is enqueued to their runqueue.

83/83 smoke tests pass in 9s, cppcheck clean.

3 months agofeat: SMP load balancing — per-CPU TSS, AP GDT reload, BSP-only timer work
Tulio A M Mendes [Mon, 16 Feb 2026 21:04:47 +0000 (18:04 -0300)]
feat: SMP load balancing — per-CPU TSS, AP GDT reload, BSP-only timer work

Three fixes enable kernel thread dispatch to any CPU:

1. Per-CPU TSS (gdt.c, gdt.h): Replace single TSS with tss_array[SMP_MAX_CPUS].
   Each AP gets its own TSS via tss_init_ap() so ring 3→0 transitions use
   the correct per-task kernel stack on any CPU.

2. AP GDT virtual base reload (smp.c): The AP trampoline loads the GDT with
   a physical base for real→protected mode. After paging is active, reload
   the GDTR with the virtual base and flush all segment registers. Without
   this, ring transitions on APs read GDT entries from the identity-mapped
   physical address, causing silent failures for user-mode processes.

3. BSP-only timer work (timer.c): Gate tick increment, vdso update,
   vga_flush, hal_uart_poll_rx, and process_wake_check to run only on
   CPU 0. APs only call schedule(). Prevents non-atomic tick races,
   concurrent VGA/UART access, and duplicate wake processing.

4. Per-CPU SYSENTER stacks (sysenter_init.c): Each AP gets its own
   SYSENTER ESP MSR pointing to a dedicated stack.

5. Load balancing (scheduler.c): process_create_kernel dispatches to
   the least-loaded CPU via sched_pcpu_least_loaded(). All CPUs update
   their own TSS ESP0 during context switch.

83/83 smoke tests pass, cppcheck clean.

3 months agofeat: IPI reschedule infrastructure (SMP Phase 4)
Tulio A M Mendes [Mon, 16 Feb 2026 19:14:42 +0000 (16:14 -0300)]
feat: IPI reschedule infrastructure (SMP Phase 4)

Add inter-processor interrupt (IPI) reschedule mechanism:

- IPI vector 0xFD (253) registered in IDT + ISR assembly stub
- isr_handler dispatches vector 253: sends LAPIC EOI then calls
  schedule() on the receiving CPU
- sched_ipi_resched() sends IPI to wake a remote idle CPU when
  work is enqueued to its runqueue (avoids waking self)
- sched_enqueue_ready() sends IPI after enqueuing to remote CPU
- sched_pcpu_inc_load() called when enqueuing new kernel threads

All processes still dispatched to CPU 0 — per-CPU TSS is needed
before user processes can run on APs.  The IPI + load tracking
infrastructure is ready for when per-CPU TSS is added.

83/83 smoke tests pass (8s), cppcheck clean.

3 months agofeat: AP scheduler entry (SMP Phase 3)
Tulio A M Mendes [Mon, 16 Feb 2026 19:00:38 +0000 (16:00 -0300)]
feat: AP scheduler entry (SMP Phase 3)

Enable scheduling on Application Processors:

- Load IDT on APs via idt_load_ap() — root cause of AP crashes was
  missing lidt, causing triple-fault when LAPIC timer fires
- Create per-CPU idle process for each AP in sched_ap_init()
- Start LAPIC timer on APs using BSP-calibrated ticks (no PIT
  recalibration needed — all CPUs share same bus clock)
- AP timer handler calls schedule() for local CPU runqueue
- BSP signals APs via ap_sched_go flag after timer_init completes
- Allocations in sched_ap_init done outside sched_lock to avoid
  ABBA deadlock with heap lock
- TSS updates restricted to CPU 0 (shared TSS, only BSP runs
  user processes)
- AP stack increased to 8KB to match kernel thread stack size

All processes still assigned to CPU 0 — Phase 4 will add load
balancing to distribute processes across CPUs.

83/83 smoke tests pass (8s), cppcheck clean.

3 months agorefactor: per-CPU runqueue data structure (SMP Phase 2)
Tulio A M Mendes [Mon, 16 Feb 2026 18:26:26 +0000 (15:26 -0300)]
refactor: per-CPU runqueue data structure (SMP Phase 2)

Replace global rq_active/rq_expired with per-CPU runqueue array:

- struct cpu_rq: active/expired runqueue pair + idle process per CPU
- pcpu_rq[SCHED_MAX_CPUS] array replaces global runqueue pointers
- All enqueue/dequeue operations now index by process cpu_id field
- schedule() uses percpu_cpu_index() to select local CPU's runqueue
- process_init() initializes all CPU runqueues, sets pcpu_rq[0].idle
- Added cpu_id field to struct process (set to 0 for now)
- rq_pick_next() takes cpu parameter, swaps per-CPU active/expired
- All wake paths (kill, signal, sleep wake, exit_notify) enqueue
  to the target process's assigned CPU runqueue

All processes still assigned to CPU 0 — Phase 3/4 will activate
AP scheduling and load balancing.

83/83 smoke tests pass (9s), cppcheck clean.

3 months agorefactor: per-CPU current_process via GS segment (SMP Phase 1)
Tulio A M Mendes [Mon, 16 Feb 2026 18:17:26 +0000 (15:17 -0300)]
refactor: per-CPU current_process via GS segment (SMP Phase 1)

Replace the global current_process variable with per-CPU access
through the GS-based percpu_data structure on x86:

- process.h: #define current_process percpu_current() on x86,
  keeps extern fallback for non-x86
- scheduler.c: write sites use percpu_set_current()
- interrupts.S: ISR entry now reloads percpu GS by reading LAPIC ID
  from MMIO (0xC0400020) and looking up the correct GS selector in
  _percpu_gs_lut[256] — solves the chicken-and-egg problem of
  needing GS to find the CPU but GS being clobbered by user TLS
- percpu.c: _percpu_gs_lut lookup table populated during percpu_init()
- hal_cpu_set_tls: no longer loads GS immediately (would clobber
  kernel percpu GS); user TLS GS is restored on ISR exit via pop

This is the foundation for running the scheduler on AP cores.

83/83 smoke tests pass (9s), cppcheck clean.

3 months agofeat: USTAR+LZ4 compressed initrd
Tulio A M Mendes [Mon, 16 Feb 2026 18:03:36 +0000 (15:03 -0300)]
feat: USTAR+LZ4 compressed initrd

Add LZ4 block compression to the initrd pipeline:

- src/kernel/lz4.c + include/lz4.h: standalone LZ4 block decompressor
  (~80 lines, no external dependencies)
- src/drivers/initrd.c: auto-detect LZ4B magic at boot, decompress
  into heap buffer, then parse the contained USTAR tar as before
- tools/mkinitrd.c: built-in LZ4 block compressor (greedy hash-table),
  builds tar in memory then wraps in LZ4B envelope
  (magic + orig_size + comp_size + compressed data)

Format: LZ4B header (12 bytes) + raw LZ4 block.  Falls back to
uncompressed tar if compression fails.

Results on current initrd (12 files including doom.elf):
  TAR: 562 KB -> LZ4B: 326 KB (58% ratio)

Backward compatible: kernel still accepts plain USTAR tar
(no LZ4B magic = parse directly).

83/83 smoke tests pass (10s), cppcheck clean.

3 months agofix: replace pmm_alloc_page_low with pmm_alloc_page — fix fork OOM
Tulio A M Mendes [Mon, 16 Feb 2026 17:47:10 +0000 (14:47 -0300)]
fix: replace pmm_alloc_page_low with pmm_alloc_page — fix fork OOM

The below-16MB page allocator (pmm_alloc_page_low) randomly sampled
pages and discarded any above 16MB.  With 100 zombie children holding
CoW address spaces, the low-memory pool exhausted and fork() returned
-ENOMEM, killing init before the SIGSEGV/waitpid-100/echo.elf tests.

On 32-bit PAE all physical addresses are below 4GB, so the 16MB
restriction is unnecessary for PDPTs, page directories, page tables,
and user frames.

Changes:
- vmm.c: replace all pmm_alloc_page_low() with pmm_alloc_page(),
  remove the dead pmm_alloc_page_low function
- usermode.c: replace pmm_alloc_page_low_16mb() with pmm_alloc_page(),
  remove the dead function
- init.c: make SIGSEGV test failure non-fatal (goto instead of
  sys_exit) so subsequent tests still run

83/83 smoke tests pass (10s), cppcheck clean.