]> Projects (at) Tadryanom (dot) Me - AdrOS.git/log
AdrOS.git
7 weeks agodocs: update README with TTY/PTY OPOST, console routing, stdio buffering improvements
Tulio A M Mendes [Fri, 13 Feb 2026 05:54:14 +0000 (02:54 -0300)]
docs: update README with TTY/PTY OPOST, console routing, stdio buffering improvements

7 weeks agofeat: PTY line discipline with OPOST/ONLCR processing
Tulio A M Mendes [Fri, 13 Feb 2026 05:53:18 +0000 (02:53 -0300)]
feat: PTY line discipline with OPOST/ONLCR processing

- Add per-PTY oflag field (default: OPOST | ONLCR)
- PTY slave write now applies ONLCR: \n → \r\n conversion
- PTY slave ioctl now supports TCGETS/TCSETS for c_oflag
- isatty() now returns 1 for PTY slaves (TCGETS succeeds)
- Matches Linux n_tty line discipline behavior on PTY output

Build: clean, cppcheck: clean, smoke: 19/19 pass

7 weeks agofeat: industry-standard TTY output pipeline (Linux/BSD parity)
Tulio A M Mendes [Fri, 13 Feb 2026 05:48:55 +0000 (02:48 -0300)]
feat: industry-standard TTY output pipeline (Linux/BSD parity)

Kernel:
- Open /dev/console as fd 0/1/2 for init process (mirrors Linux
  kernel_init: open + dup + dup pattern)
- Add console_put_char() that outputs to both UART and VGA
- TTY write path now routes through console_put_char() instead of
  uart_put_char() only — userspace output now appears on VGA too
- Implement OPOST/ONLCR output processing: \n → \r\n conversion
  (POSIX termios c_oflag, enabled by default)
- TCGETS/TCSETS ioctl now reads/writes c_oflag
- All TTY echo paths (canonical, raw, line editing) use tty_output_char()
  for consistent UART+VGA output with OPOST processing
- Increase syscall write copy buffer from 256 to 1024 bytes
- Declare vga_put_char() in vga_console.h

Userspace (ulibc):
- stdout is now line-buffered (_STDIO_LBUF): flushes on \n
- stderr is now unbuffered (_STDIO_UNBUF): writes immediately
- printf()/vprintf() now go through fwrite(stdout) instead of raw
  write(), unifying all stdio output through the FILE buffer
- putchar()/puts() also route through fwrite(stdout)
- fwrite() respects buffering modes: unbuffered bypasses buffer,
  line-buffered flushes on newline, full-buffered flushes when full
- Add setvbuf()/setbuf() with _IOFBF/_IOLBF/_IONBF modes
- Add isatty() implemented via TCGETS ioctl probe (POSIX standard)

Build: clean, cppcheck: clean, smoke: 19/19 pass

7 weeks agodocs: update README for FAT12/16/32 RW and ext2 RW filesystems
Tulio A M Mendes [Fri, 13 Feb 2026 05:08:33 +0000 (02:08 -0300)]
docs: update README for FAT12/16/32 RW and ext2 RW filesystems

- Filesystems section: 8 types → 10 types
- FAT16 read-only → FAT12/16/32 unified RW driver
- Added ext2 RW description
- Removed ext2 from remaining work list (now implemented)
- Fixed lwIP mode description (NO_SYS=0 threaded)
- Updated directory structure listing

7 weeks agofeat: mount FAT and ext2 filesystems from init.c
Tulio A M Mendes [Fri, 13 Feb 2026 05:07:22 +0000 (02:07 -0300)]
feat: mount FAT and ext2 filesystems from init.c

- Probe IDE disk at LBA 0 for FAT and ext2 signatures during boot
- FAT mounts at /fat, ext2 mounts at /ext2
- Both fail gracefully on unformatted/zeroed disks (no panic)
- diskfs remains at /disk as primary RW filesystem

Build: clean, cppcheck: clean, smoke: 19/19 pass

7 weeks agofeat: ext2 filesystem driver with full RW support
Tulio A M Mendes [Fri, 13 Feb 2026 05:03:12 +0000 (02:03 -0300)]
feat: ext2 filesystem driver with full RW support

- New ext2.c + ext2.h: complete ext2 filesystem implementation
- Superblock parsing, block group descriptor table, inode read/write
- Block mapping: direct, singly/doubly/triply indirect blocks
- File read/write with automatic block allocation
- Directory operations: finddir, readdir, create, mkdir, unlink, rmdir,
  rename, truncate, link (hard links)
- Block and inode bitmap allocation/deallocation
- Symlink support (inline small symlinks via i_block)
- Auto-detection of inode size (128 or 256 for rev1)
- Supports 1KB, 2KB, and 4KB block sizes

Build: clean, cppcheck: clean, smoke: 19/19 pass

7 weeks agofeat: unified FAT12/16/32 RW driver replacing read-only FAT16
Tulio A M Mendes [Fri, 13 Feb 2026 04:54:13 +0000 (01:54 -0300)]
feat: unified FAT12/16/32 RW driver replacing read-only FAT16

- New fat.c: unified FAT driver with auto-detection (FAT12/16/32) based on
  cluster count per Microsoft FAT spec
- Full RW support: file read/write, create, delete, truncate, mkdir, rmdir,
  rename, readdir, finddir — all wired to VFS callbacks
- FAT table access for all three variants (12-bit, 16-bit, 32-bit entries)
- Cluster chain management: alloc, extend, free
- Subdirectory support (cluster-based dirs + fixed root for FAT12/16)
- 8.3 filename conversion (to/from human-readable lowercase)
- fat16.h retained as backward-compat wrapper redirecting to fat.h
- Old read-only fat16.c removed

Fix: BSS collision with ACPI temp VA window
- BSS grew past 0xC0202000 with lwIP memp pools + FAT statics
- Moved ACPI temp VA: 0xC0202000 -> 0xC0300000
- Moved DMA PRDT VA: 0xC0220000 -> 0xC0320000
- Moved DMA bounce VA: 0xC0221000 -> 0xC0321000

Build: clean, cppcheck: clean, smoke: 19/19 pass

7 weeks agokprintf: migrate all uart_print() calls to kprintf() (Route A)
Tulio A M Mendes [Fri, 13 Feb 2026 04:31:04 +0000 (01:31 -0300)]
kprintf: migrate all uart_print() calls to kprintf() (Route A)

Replace 270 direct uart_print() calls across 42 files with kprintf(),
routing all kernel messages through the klog ring buffer and multi-backend
console infrastructure (UART + VGA).

Key changes:
- All kernel log/debug messages now go through kprintf() -> klog_append()
  -> console_write(), ensuring they appear in dmesg and on all enabled
  output devices.
- Consolidated multi-call patterns (uart_print+itoa_hex) into single
  kprintf() calls with format specifiers (%x, %u, %s, %d, %c).
- Removed manual itoa/itoa_hex + uart_print concatenation throughout.
- Cleaned up stale #include uart_console.h from files that no longer
  need it (main.c, socket.c, syscall.c, slab.c, timer.c).
- uart_print() now only remains in 2 places:
  * uart_console.c (the implementation)
  * console.c (the UART backend in console_write)
- uart_put_char() retained in tty.c for direct terminal I/O (not logging).
- arch_early_setup files keep uart_console.h for uart_init() call.

Build: clean, cppcheck: clean, smoke: 19/19 pass.

7 weeks agorefactor: replace doubly-linked-list heap with buddy allocator
Tulio A M Mendes [Fri, 13 Feb 2026 04:07:06 +0000 (01:07 -0300)]
refactor: replace doubly-linked-list heap with buddy allocator

Power-of-2 block sizes from 2^5 (32B) to 2^23 (8MB) with O(log N)
alloc/free and automatic buddy coalescing on free.

Design:
- block_hdr_t (8B) at the start of every block: magic, order, is_free
- Free blocks embed circular doubly-linked list pointers in their
  data area (free_node_t) for O(1) insert/remove per order
- 19 free lists (one per order, sentinel-based)
- Buddy merge: XOR offset to find buddy, check magic + is_free + order
- Spinlock-protected for SMP safety

Allocation:
- size_to_order: find smallest 2^k >= size + 8 (header)
- Search free lists from requested order upward
- Split larger blocks down, placing upper buddies on their free lists

Deallocation:
- Verify magic and double-free
- Iteratively merge with buddy while buddy is free at same order
- Insert merged block into correct free list

Trade-offs vs previous doubly-linked-list allocator:
+ O(log N) worst case vs O(N) first-fit scan
+ No external fragmentation (buddy coalescing)
+ Deterministic allocation time
- Internal fragmentation from power-of-2 rounding (~50% worst case)
- Fixed 8MB heap (was 10MB growable to 64MB)

Updated smoke test expectation for new init message.
19/19 smoke tests pass, cppcheck clean.

7 weeks agofeat: enable lwIP NO_SYS=0 threaded mode with kernel sync primitives
Tulio A M Mendes [Fri, 13 Feb 2026 03:52:10 +0000 (00:52 -0300)]
feat: enable lwIP NO_SYS=0 threaded mode with kernel sync primitives

Kernel synchronization primitives (include/sync.h + src/kernel/sync.c):
- ksem_t: counting semaphore with sleep/wake blocking (not spin-wait)
  - ksem_init, ksem_wait, ksem_wait_timeout, ksem_signal
  - Timeout support via process wake_at_tick mechanism
  - Race-safe: ksem_signal skips already-woken (timed-out) waiters
- kmutex_t: binary semaphore wrapper for mutual exclusion
- kmbox_t: fixed-size circular queue with not_empty/not_full semaphores
  - kmbox_init, kmbox_free, kmbox_post, kmbox_trypost
  - kmbox_fetch (with timeout), kmbox_tryfetch

lwIP sys_arch layer (include/net/arch/sys_arch.h + sys_arch.c):
- sys_sem_t, sys_mutex_t, sys_mbox_t backed by kernel primitives
- sys_thread_new: creates kernel threads via process_create_kernel
  with static trampoline array (up to 4 lwIP threads)
- sys_arch_protect/unprotect: IRQ save/restore for SYS_LIGHTWEIGHT_PROT
- sys_init, sys_now (50Hz tick to ms conversion)

lwIP configuration (lwipopts.h):
- NO_SYS=0, LWIP_NETCONN=1, SYS_LIGHTWEIGHT_PROT=1
- LWIP_SOCKET=0 (kernel uses netconn API; avoids POSIX type conflicts)
- Thread/mbox sizing: TCPIP_MBOX_SIZE=16, recvmbox sizes=8

Build system (Makefile):
- Added lwIP api/ sources: api_lib, api_msg, err, if_api, netbuf,
  netifapi, tcpip

Network init (e1000_netif.c):
- tcpip_init(callback, NULL) with volatile flag polling for sync
- netif input changed from ethernet_input to tcpip_input
- net_poll no longer calls sys_check_timeouts (handled by tcpip_thread)

Kernel stack enlargement (scheduler.c):
- Increased from 4KB (1 page) to 8KB (2 pages) per thread
- Required for deeper call chains in lwIP threaded mode
- Updated kstack_alloc, kstack_free, and all stack+offset references

LAPIC VA relocation (lapic.c):
- Moved from 0xC0200000 to 0xC0400000 to avoid collision with
  enlarged kernel BSS (~764KB with NO_SYS=0 memp pools)

lwIP third-party patch (patches/lwip-tcpip-volatile.patch):
- tcpip_init_done and tcpip_init_done_arg marked volatile in tcpip.c
- Fixes cross-thread visibility: compiler was caching NULL from BSS
  init, preventing tcpip_thread from seeing the callback set by
  tcpip_init in the init thread

All 19/19 smoke tests pass, cppcheck clean.

7 weeks agofix: resolve implicit declaration warnings in init.c and keyboard.c
Tulio A M Mendes [Fri, 13 Feb 2026 03:01:34 +0000 (00:01 -0300)]
fix: resolve implicit declaration warnings in init.c and keyboard.c

- src/kernel/init.c: add missing #include "keyboard.h" for
  keyboard_register_devfs()
- src/drivers/keyboard.c: add #include "utils.h" for memset/strcpy
  prototypes
- include/utils.h: remove duplicate strcpy prototype

7 weeks agorefactor: abstract x86 register accesses in syscall dispatcher via sc_* macros
Tulio A M Mendes [Fri, 13 Feb 2026 01:09:53 +0000 (22:09 -0300)]
refactor: abstract x86 register accesses in syscall dispatcher via sc_* macros

- include/arch/x86/arch_syscall.h: define sc_num/sc_arg0..4/sc_ret/
  sc_ip/sc_usp macros mapping to x86 INT 0x80 ABI registers
  (eax/ebx/ecx/edx/esi/edi/eip/useresp)
- include/arch_syscall.h: generic dispatch header with non-x86 stubs
- src/kernel/syscall.c: replace all ~200 direct regs->eax/ebx/ecx/
  edx/esi/edi/eip/useresp accesses with arch-agnostic sc_* macros
  across syscall_handler, posix_ext_syscall_dispatch, and
  socket_syscall_dispatch

syscall.c now contains zero x86-specific register names. To port to
ARM, only arch/arm/arch_syscall.h needs to map sc_* to ARM registers
(r7/r0-r4/pc/sp).

7 weeks agorefactor: route link() through VFS callback — remove last diskfs bypass from syscall.c
Tulio A M Mendes [Fri, 13 Feb 2026 00:58:05 +0000 (21:58 -0300)]
refactor: route link() through VFS callback — remove last diskfs bypass from syscall.c

- fs.h: add link callback to fs_node_t (dir, name, target_node)
- fs.c: implement vfs_link() wrapper — resolves old_path to node,
  new_path to parent+basename, calls parent->link()
- diskfs.c: implement diskfs_vfs_link() using parent ino + target ino,
  wire into diskfs_set_dir_ops()
- syscall.c: syscall_link_impl now calls vfs_link() instead of
  extern diskfs_link() with /disk/ prefix stripping

syscall.c no longer references any diskfs symbol.

7 weeks agorefactor: remove /disk/ VFS bypass from syscall.c — route through VFS mount + callbacks
Tulio A M Mendes [Fri, 13 Feb 2026 00:43:13 +0000 (21:43 -0300)]
refactor: remove /disk/ VFS bypass from syscall.c — route through VFS mount + callbacks

- fs.h: add create/mkdir/unlink/rmdir/rename/truncate callbacks to fs_node_t
- fs.h: add vfs_lookup_parent, vfs_create, vfs_mkdir, vfs_unlink, vfs_rmdir,
  vfs_rename, vfs_truncate prototypes
- fs.c: implement vfs_lookup_parent (split path into parent dir + basename)
  and all VFS mutation wrappers that resolve mount points transparently
- diskfs.c: implement VFS callback wrappers (diskfs_vfs_create, diskfs_vfs_mkdir,
  diskfs_vfs_unlink, diskfs_vfs_rmdir, diskfs_vfs_rename, diskfs_vfs_truncate)
  using parent diskfs_node ino for correct hierarchy scoping
- diskfs.c: wire callbacks into root and subdirectory nodes via diskfs_set_dir_ops
- syscall.c: open/mkdir/unlink/rmdir/rename now use generic VFS functions
  instead of hardcoded path[0]==/ && path[1]==d... checks
- syscall.c: remove #include diskfs.h (only diskfs_link extern remains)

Any filesystem mounted via vfs_mount that implements these callbacks will
now transparently support file creation, directory operations, and rename
without requiring syscall.c modifications.

7 weeks agorefactor: move sigframe/sigreturn from syscall.c to arch/x86/signal.c
Tulio A M Mendes [Fri, 13 Feb 2026 00:33:03 +0000 (21:33 -0300)]
refactor: move sigframe/sigreturn from syscall.c to arch/x86/signal.c

- New include/arch/x86/signal.h: shared struct sigframe + SIGFRAME_MAGIC
- New include/arch_signal.h: arch-agnostic arch_sigreturn() prototype
- New src/arch/x86/signal.c: x86 sigreturn implementation (eflags sanitize,
  CS/SS ring3 validation, IOPL clear)
- src/arch/x86/idt.c: use shared arch/x86/signal.h instead of local copy
- src/kernel/syscall.c: remove x86-specific sigframe struct and sigreturn_impl,
  call arch_sigreturn() via generic void* interface

No x86 signal frame knowledge remains in generic kernel code.

7 weeks agorefactor: extract x86 kernel stack setup and register accessors from scheduler to...
Tulio A M Mendes [Fri, 13 Feb 2026 00:29:02 +0000 (21:29 -0300)]
refactor: extract x86 kernel stack setup and register accessors from scheduler to arch layer

- New include/arch_process.h: arch-agnostic prototypes for arch_kstack_init(),
  arch_regs_set_retval(), arch_regs_set_ustack()
- New src/arch/x86/arch_process.c: x86 implementation (EFLAGS 0x202, cdecl
  stack frame layout matching context_switch in process.S)
- scheduler.c: process_create_kernel, process_fork_create, process_clone_create
  now use arch_kstack_init() instead of inline x86 stack manipulation
- scheduler.c: process_clone_create uses arch_regs_set_retval/arch_regs_set_ustack
  instead of direct .eax/.useresp access

No x86-specific constants or register names remain in scheduler.c.

7 weeks agodocs: update all documentation for DOOM port, euid/egid, /dev/fb0, /dev/kbd, fd-backe...
Tulio A M Mendes [Thu, 12 Feb 2026 08:47:23 +0000 (05:47 -0300)]
docs: update all documentation for DOOM port, euid/egid, /dev/fb0, /dev/kbd, fd-backed mmap

README.md:
- Added DOOM port (/bin/doom.elf), /dev/fb0, /dev/kbd to features
- Updated permissions: euid/egid + VFS enforcement on open()
- Updated mmap: now includes fd-backed mappings
- Updated guard pages: kernel stacks at 0xC8000000
- Updated ulibc: added all new headers (stdlib.h, ctype.h, sys/mman.h, etc.)
- Updated POSIX score: 90% → 93%
- Removed file-backed mmap from remaining work (now implemented)
- Added user/doom/ to directory structure

BUILD_GUIDE.md:
- Added Section 3: Building DOOM (setup, build, run instructions)
- Updated ulibc description with all new headers
- Added doom.elf to initrd listing

docs/POSIX_ROADMAP.md:
- Added geteuid/getegid/seteuid/setegid syscalls (all [x])
- Updated permissions entry: euid/egid + VFS enforcement
- Updated devfs: added /dev/fb0, /dev/kbd
- Updated mmap: fd-backed mappings
- Added /dev/kbd and /bin/doom.elf entries
- Added 8 additional features (tasks 32-39) to progress section
- Removed file-backed mmap from remaining gaps (now done)
- Updated ulibc header list

docs/AUDIT_REPORT.md:
- Updated 4.3 (guard pages): kernel stacks now fixed too
- Updated summary table: 4.3 now FIXED (user + kernel)
- Updated fix summary: 2 MODERATE fixed, 6 remaining

docs/SUPPLEMENTARY_ANALYSIS.md:
- Updated POSIX score: 90% → 93%
- Updated VMM summary: fd-backed mmap, kernel guard pages
- Updated process model: full euid/egid syscall list
- Updated memory management: fd-backed mmap, kernel guard pages
- Updated security: VFS permission enforcement
- Updated userland: all new ulibc headers + DOOM port
- Removed file-backed mmap from remaining gaps
- Updated remaining actions (file-backed mmap removed)
- Updated conclusion

docs/TESTING_PLAN.md:
- Added DOOM Smoke Test section describing integration test value

7 weeks agofeat: proper uid/gid + euid/egid implementation with permission enforcement
Tulio A M Mendes [Thu, 12 Feb 2026 08:34:46 +0000 (05:34 -0300)]
feat: proper uid/gid + euid/egid implementation with permission enforcement

Kernel:
- struct process: added euid/egid (effective uid/gid) fields
- process_fork_create: now inherits uid/gid/euid/egid from parent
  (previously left at 0 from memset)
- process_clone_create: also inherits euid/egid
- setuid/setgid: permission checks — only euid==0 can set arbitrary
  uid/gid; unprivileged processes can only set to their real uid/gid
- New syscalls: geteuid (88), getegid (89), seteuid (90), setegid (91)
- vfs_check_permission(): checks owner/group/other rwx bits against
  process euid/egid and file uid/gid/mode
- open() now calls vfs_check_permission() for R/W/RW access
- chmod: only root or file owner can change mode
- chown: only root can change ownership
- Added EACCES (13) to errno.h

ulibc:
- Added SYS_GETUID (52), SYS_GETGID (53), SYS_CHMOD (50),
  SYS_CHOWN (51), SYS_GETEUID (88), SYS_GETEGID (89),
  SYS_SETEUID (90), SYS_SETEGID (91)
- Added getuid/getgid/geteuid/getegid/seteuid/setegid wrappers

All 19/19 smoke tests pass.

7 weeks agofeat: include doom.elf in initrd when built
Tulio A M Mendes [Thu, 12 Feb 2026 08:13:01 +0000 (05:13 -0300)]
feat: include doom.elf in initrd when built

The Makefile now conditionally includes user/doom/doom.elf in the
initrd as bin/doom.elf if it exists. This allows DOOM to be
launched from the AdrOS shell via: /bin/doom.elf -iwad /path/to/doom1.wad

The DOOM build is optional — the main kernel build is unaffected
if doomgeneric has not been cloned.

7 weeks agofeat: DOOM compiles and links — adapter, build system, ulibc compat headers
Tulio A M Mendes [Thu, 12 Feb 2026 08:08:50 +0000 (05:08 -0300)]
feat: DOOM compiles and links — adapter, build system, ulibc compat headers

doom.elf (450KB) now builds successfully from doomgeneric source
with the AdrOS platform adapter.

Build system:
- user/doom/Makefile: excludes platform-specific adapters (SDL,
  allegro, emscripten, xlib, win, soso, linuxvt) and links with
  ulibc + crt0
- doomgeneric_adros.c: added main() entry point calling
  doomgeneric_Create/doomgeneric_Tick

New ulibc compatibility headers for DOOM:
- strings.h (wrapper for string.h)
- inttypes.h (PRId32/PRIu32/PRIx32 format macros)
- math.h (fabs/fabsf inline stubs)
- fcntl.h (O_RDONLY, O_WRONLY, O_CREAT, etc.)
- assert.h (assert macro with printf+exit)
- sys/types.h (ssize_t, off_t, pid_t, etc.)
- sys/stat.h (struct stat, S_ISDIR/S_ISREG)

New ulibc functions:
- stdlib: atof, system (stub), strtol
- All 19/19 kernel tests pass

7 weeks agofeat: DOOM port — doomgeneric AdrOS adapter + remaining ulibc extensions
Tulio A M Mendes [Thu, 12 Feb 2026 07:54:41 +0000 (04:54 -0300)]
feat: DOOM port — doomgeneric AdrOS adapter + remaining ulibc extensions

Added user/doom/ with the AdrOS platform adapter for doomgeneric:
- doomgeneric_adros.c: implements DG_Init (fb0 mmap + kbd open),
  DG_DrawFrame (nearest-neighbor scale to framebuffer),
  DG_GetKey (PS/2 scancode → DOOM keycode mapping),
  DG_GetTicksMs (clock_gettime), DG_SleepMs (nanosleep)
- Makefile: builds doom.elf from doomgeneric source + adapter
- README.md: setup instructions

Additional ulibc functions for DOOM engine compatibility:
- ctype.h: isdigit, isspace, isalpha, toupper, tolower, etc.
- stdlib: strtol (base 8/10/16 + auto-detect)
- string: strncat, strdup, strcasecmp, strncasecmp, strstr,
  memchr, strtok
- stdio: fseek, ftell, rewind, sprintf, sscanf, remove

To build DOOM:
  cd user/doom && git clone https://github.com/ozkl/doomgeneric.git && make

7 weeks agofeat: ulibc DOOM-ready extensions — fseek, ftell, sprintf, sscanf, strdup, etc.
Tulio A M Mendes [Thu, 12 Feb 2026 07:48:04 +0000 (04:48 -0300)]
feat: ulibc DOOM-ready extensions — fseek, ftell, sprintf, sscanf, strdup, etc.

Added missing C library functions required by the DOOM engine:

stdio: fseek, ftell, rewind, sprintf, sscanf (minimal %d/%s),
       remove, rename (stub)
stdlib: getenv (stub), abs, labs
string: strncat, strdup, strcasecmp, strncasecmp, strstr,
        memchr, strtok

7 weeks agofeat: guard pages for kernel stacks — detect overflow via page fault
Tulio A M Mendes [Thu, 12 Feb 2026 07:40:52 +0000 (04:40 -0300)]
feat: guard pages for kernel stacks — detect overflow via page fault

Replaced kmalloc(4096) kernel stack allocation with a dedicated
kstack_alloc() that uses a virtual address region (0xC8000000+)
with guard pages. Each stack slot is 2 pages:

  [guard page (unmapped)] [stack page (mapped, 4KB)]

If a kernel stack overflows, the CPU hits the unmapped guard page
and triggers a page fault instead of silently corrupting heap
metadata. This eliminates the class of heap corruption bugs caused
by deep syscall call chains or large stack frames.

All 4 kernel stack allocation sites updated:
- process_init (PID 0)
- process_fork_create
- process_clone_impl
- create_kernel_thread

kstack_free() unmaps the stack page on process exit.

7 weeks agofeat: ulibc extensions for DOOM — mmap, munmap, ioctl, nanosleep, clock_gettime
Tulio A M Mendes [Thu, 12 Feb 2026 07:36:52 +0000 (04:36 -0300)]
feat: ulibc extensions for DOOM — mmap, munmap, ioctl, nanosleep, clock_gettime

Added userspace wrappers required for the DOOM port:
- sys/mman.h + mman.c: mmap() and munmap() for framebuffer mapping
- sys/ioctl.h + ioctl.c: ioctl() for framebuffer info queries
- time.h + time.c: nanosleep() and clock_gettime() for frame timing

All wrappers use the existing INT 0x80 syscall interface.

7 weeks agofeat: /dev/kbd raw scancode device for game input (DOOM)
Tulio A M Mendes [Thu, 12 Feb 2026 07:32:51 +0000 (04:32 -0300)]
feat: /dev/kbd raw scancode device for game input (DOOM)

Added raw scancode ring buffer to the keyboard driver. The HAL
keyboard layer now fires a second callback with the unprocessed
scancode byte (both key-press and key-release events).

- hal/keyboard.h: added hal_keyboard_scan_cb_t and setter
- hal/x86/keyboard.c: fires g_scan_cb before ASCII translation
- drivers/keyboard.c: raw scancode buffer + /dev/kbd device node
  registered via devfs (non-blocking read returns raw scancodes)
- init.c: calls keyboard_register_devfs() after devfs is mounted

DOOM can now open /dev/kbd and read raw PS/2 scancodes to detect
key press/release events without TTY line buffering.

7 weeks agofeat: /dev/fb0 framebuffer device + fd-backed mmap support
Tulio A M Mendes [Thu, 12 Feb 2026 07:26:30 +0000 (04:26 -0300)]
feat: /dev/fb0 framebuffer device + fd-backed mmap support

Added /dev/fb0 device node registered via devfs by vbe.c:
- ioctl: FBIOGET_VSCREENINFO (resolution, bpp), FBIOGET_FSCREENINFO
  (phys addr, pitch, size)
- mmap: maps physical framebuffer into userspace with NOCACHE flags
- read/write: direct pixel buffer access via offset

Extended syscall_mmap_impl to support fd-backed mmap: when
MAP_ANONYMOUS is not set, the file descriptor's node->mmap callback
is invoked. This enables userspace to mmap /dev/fb0 for direct
framebuffer access (required for DOOM).

Marked syscall_mmap_impl as noinline to prevent GCC from merging it
into syscall_handler (4KB kernel stack limit).

7 weeks agorefactor: add ioctl/mmap callbacks to fs_node_t, decouple ioctl dispatch
Tulio A M Mendes [Thu, 12 Feb 2026 07:11:59 +0000 (04:11 -0300)]
refactor: add ioctl/mmap callbacks to fs_node_t, decouple ioctl dispatch

Added ioctl and mmap function pointers to fs_node_t for generic
device dispatch. Refactored syscall_ioctl_impl to call node->ioctl
instead of hardcoding TTY/PTY dispatch by inode number.

- tty.c: added tty_devfs_ioctl wrapper, set on console/tty nodes
- pty.c: added pty_slave_ioctl_fn wrapper, set on all slave nodes
- syscall.c: ioctl now dispatches generically through node callback

This prepares the VFS for /dev/fb0 and other devices that need
ioctl and mmap support.

7 weeks agorefactor: extract generic VMM wrappers from x86 implementation to src/mm/vmm.c
Tulio A M Mendes [Thu, 12 Feb 2026 07:04:21 +0000 (04:04 -0300)]
refactor: extract generic VMM wrappers from x86 implementation to src/mm/vmm.c

Moved vmm_protect_range(), vmm_as_activate(), and vmm_as_map_page()
from src/arch/x86/vmm.c to src/mm/vmm.c. These functions contain only
architecture-independent logic (looping over pages, delegating to HAL
for address space switching).

The x86-specific VMM code (PAE page table manipulation, recursive
mapping, CoW handling, address space create/destroy/clone) remains
in src/arch/x86/vmm.c where it belongs.

This ensures new architectures only need to implement the core
primitives (vmm_init, vmm_map_page, vmm_unmap_page, vmm_set_page_flags,
vmm_as_create_kernel_clone, vmm_as_destroy, vmm_as_clone_user,
vmm_as_clone_user_cow, vmm_handle_cow_fault) and get the wrapper
functions for free.

7 weeks agorefactor: decouple DevFS from TTY/PTY drivers via device registration API
Tulio A M Mendes [Thu, 12 Feb 2026 07:01:14 +0000 (04:01 -0300)]
refactor: decouple DevFS from TTY/PTY drivers via device registration API

Added devfs_register_device() API so device drivers register their own
fs_node_t with DevFS. DevFS is now a generic device registry that
dispatches through function pointers — it no longer includes tty.h or
pty.h and has zero knowledge of TTY/PTY internals.

- tty.c: registers /dev/console and /dev/tty with VFS-compatible wrappers
- pty.c: registers /dev/ptmx and /dev/pts (with finddir/readdir moved
  from devfs.c)
- devfs.c: only owns built-in devices (null, zero, random, urandom);
  all other devices come from the registry

This enables any future driver to register device nodes without
modifying devfs.c.

7 weeks agorefactor: move lwIP port headers from src/net/lwip_port/ to include/net/
Tulio A M Mendes [Thu, 12 Feb 2026 06:46:40 +0000 (03:46 -0300)]
refactor: move lwIP port headers from src/net/lwip_port/ to include/net/

Moved lwipopts.h and arch/cc.h to include/net/ where they belong
alongside other public headers. Updated Makefile include path from
-Isrc/net/lwip_port to -Iinclude/net.

Also fixed cc.h to use arch-conditional BYTE_ORDER instead of
hardcoding x86 little-endian, supporting ARM, RISC-V, and MIPS
targets.

7 weeks agorefactor: extract x86 GDT/GS TLS setup from scheduler to HAL layer
Tulio A M Mendes [Thu, 12 Feb 2026 06:42:19 +0000 (03:42 -0300)]
refactor: extract x86 GDT/GS TLS setup from scheduler to HAL layer

Added hal_cpu_set_tls(base) to the HAL CPU API with x86 implementation
(GDT entry 22 + GS segment load) and a no-op fallback for other arches.

kernel/scheduler.c no longer contains x86 inline assembly for TLS —
the #if defined(__i386__) block is replaced by a single HAL call.

7 weeks agorefactor: extract x86 rdtsc from kernel/kaslr.c to HAL layer
Tulio A M Mendes [Thu, 12 Feb 2026 06:39:37 +0000 (03:39 -0300)]
refactor: extract x86 rdtsc from kernel/kaslr.c to HAL layer

Added hal_cpu_read_timestamp() to the HAL CPU API with x86
implementation using rdtsc and a fallback stub for other arches.

kernel/kaslr.c no longer contains x86 inline assembly — it calls
the generic HAL function for timestamp-based PRNG seeding.

7 weeks agorefactor: extract x86 CMOS I/O from drivers/rtc.c to HAL layer
Tulio A M Mendes [Thu, 12 Feb 2026 06:37:06 +0000 (03:37 -0300)]
refactor: extract x86 CMOS I/O from drivers/rtc.c to HAL layer

Created include/hal/rtc.h with generic HAL RTC interface and
src/hal/x86/rtc.c with x86 CMOS port I/O implementation.

src/drivers/rtc.c is now fully architecture-agnostic: it calls
hal_rtc_read_raw() for hardware access and keeps only the generic
BCD-to-binary conversion and UNIX timestamp calculation logic.

This follows the same HAL pattern used by timer, keyboard, uart,
and video drivers.

7 weeks agofix: add timeout to UART busy-wait in hal_uart_putc()
Tulio A M Mendes [Thu, 12 Feb 2026 06:25:39 +0000 (03:25 -0300)]
fix: add timeout to UART busy-wait in hal_uart_putc()

The UART transmit loop now gives up after ~100k iterations instead
of spinning forever. This prevents the kernel from hanging with
the console spinlock held if the UART hardware is unresponsive,
which would otherwise deadlock all CPUs attempting kprintf (including
panic and debug output paths).

7 weeks agofix: allocate dedicated heap kernel stack for PID 0 (idle task)
Tulio A M Mendes [Thu, 12 Feb 2026 06:23:30 +0000 (03:23 -0300)]
fix: allocate dedicated heap kernel stack for PID 0 (idle task)

PID 0 previously used the boot stack from assembly (_stack_top),
which is not heap-managed. This caused two issues:
- TSS esp0 was not updated when switching to PID 0 (kernel_stack
  was NULL, so the guard in schedule() skipped the update)
- If PID 0 were ever reaped or its stack freed, it would corrupt
  memory since the boot stack is not a kmalloc'd block

Now process_init() allocates a 4KB kernel stack via kmalloc and
sets TSS esp0 to its top, matching the pattern used by all other
processes.

7 weeks agofix: save/restore EFLAGS in context_switch instead of forcing sti after schedule()
Tulio A M Mendes [Thu, 12 Feb 2026 06:21:08 +0000 (03:21 -0300)]
fix: save/restore EFLAGS in context_switch instead of forcing sti after schedule()

context_switch now uses pushf/popf to properly save and restore the
EFLAGS register (including the IF bit) across context switches.
This replaces the unconditional hal_cpu_enable_interrupts() call
after context_switch in schedule(), which broke the interrupt-state
semantics for callers that needed atomicity.

All process creation functions (fork, clone, kernel thread) now push
EFLAGS=0x202 (IF=1) onto the initial stack so new processes start
with interrupts enabled via popf in context_switch.

7 weeks agodocs: update all documentation to reflect 31 completed POSIX tasks
Tulio A M Mendes [Thu, 12 Feb 2026 05:19:17 +0000 (02:19 -0300)]
docs: update all documentation to reflect 31 completed POSIX tasks

- POSIX_ROADMAP.md: mark all 31 tasks complete, add remaining gaps in 3 tiers
- README.md: add ASLR, vDSO, futex, guard pages, FAT16, DNS, zero-copy DMA, RTC, MTRR, ld.so stub; update POSIX score to ~90%
- BUILD_GUIDE.md: add ld.so, expanded ulibc details, updated smoke test list
- SUPPLEMENTARY_ANALYSIS.md: fix ~20 stale table entries, update score 70%->90%, rewrite gaps/recommendations/conclusion
- AUDIT_REPORT.md: mark user stack guard pages as fixed, update fix summary
- TESTING_PLAN.md: update current state to reflect all 4 testing layers operational

7 weeks agofeat: ASLR — TSC-seeded xorshift32 PRNG randomizes user stack base by up to 1MB per...
Tulio A M Mendes [Thu, 12 Feb 2026 04:47:34 +0000 (01:47 -0300)]
feat: ASLR — TSC-seeded xorshift32 PRNG randomizes user stack base by up to 1MB per execve

7 weeks agofeat: userspace ld.so stub — minimal dynamic linker placeholder, built and packed...
Tulio A M Mendes [Thu, 12 Feb 2026 04:42:40 +0000 (01:42 -0300)]
feat: userspace ld.so stub — minimal dynamic linker placeholder, built and packed into initrd as lib/ld.so

7 weeks agofeat: zero-copy DMA I/O — ata_dma_read_direct/ata_dma_write_direct bypass bounce...
Tulio A M Mendes [Thu, 12 Feb 2026 04:36:43 +0000 (01:36 -0300)]
feat: zero-copy DMA I/O — ata_dma_read_direct/ata_dma_write_direct bypass bounce buffer with caller-provided physical address

7 weeks agofeat: FAT16 read-only filesystem driver — BPB parsing, FAT chain traversal, root...
Tulio A M Mendes [Thu, 12 Feb 2026 04:30:56 +0000 (01:30 -0300)]
feat: FAT16 read-only filesystem driver — BPB parsing, FAT chain traversal, root dir finddir, VFS read

7 weeks agofeat: DNS resolver — enable lwIP DNS, kernel dns_resolve() wrapper with async callbac...
Tulio A M Mendes [Thu, 12 Feb 2026 04:24:31 +0000 (01:24 -0300)]
feat: DNS resolver — enable lwIP DNS, kernel dns_resolve() wrapper with async callback + timeout

7 weeks agofeat: vDSO shared page — kernel-updated tick_count mapped read-only into user address...
Tulio A M Mendes [Thu, 12 Feb 2026 04:18:08 +0000 (01:18 -0300)]
feat: vDSO shared page — kernel-updated tick_count mapped read-only into user address space at 0x007FE000

7 weeks agofeat: decay-based scheduler — priority decay on time slice exhaustion, boost on sleep...
Tulio A M Mendes [Thu, 12 Feb 2026 04:13:13 +0000 (01:13 -0300)]
feat: decay-based scheduler — priority decay on time slice exhaustion, boost on sleep wake

7 weeks agofeat: MTRR write-combining support — mtrr_init/mtrr_set_range for variable-range...
Tulio A M Mendes [Thu, 12 Feb 2026 04:09:08 +0000 (01:09 -0300)]
feat: MTRR write-combining support — mtrr_init/mtrr_set_range for variable-range MTRR programming

7 weeks agofeat: flock() syscall (87) — advisory file locking no-op stub + ulibc wrapper
Tulio A M Mendes [Thu, 12 Feb 2026 04:05:00 +0000 (01:05 -0300)]
feat: flock() syscall (87) — advisory file locking no-op stub + ulibc wrapper

7 weeks agofeat: sigaltstack syscall (86) — alternate signal stack per-process (ss_sp/ss_size...
Tulio A M Mendes [Thu, 12 Feb 2026 04:00:34 +0000 (01:00 -0300)]
feat: sigaltstack syscall (86) — alternate signal stack per-process (ss_sp/ss_size/ss_flags)

7 weeks agofeat: futex syscall (85) — FUTEX_WAIT/FUTEX_WAKE with global waiter table + ulibc...
Tulio A M Mendes [Thu, 12 Feb 2026 03:53:40 +0000 (00:53 -0300)]
feat: futex syscall (85) — FUTEX_WAIT/FUTEX_WAKE with global waiter table + ulibc wrapper

7 weeks agofeat: times() syscall (84) — per-process CPU time accounting (utime/stime fields...
Tulio A M Mendes [Thu, 12 Feb 2026 03:49:04 +0000 (00:49 -0300)]
feat: times() syscall (84) — per-process CPU time accounting (utime/stime fields in struct process)

7 weeks agofeat: hard links in diskfs — diskfs_link() with shared storage, nlink tracking, updat...
Tulio A M Mendes [Thu, 12 Feb 2026 03:40:48 +0000 (00:40 -0300)]
feat: hard links in diskfs — diskfs_link() with shared storage, nlink tracking, updated syscall_link_impl

7 weeks agofeat: pmm_alloc_blocks/pmm_free_blocks — contiguous physical page allocation for...
Tulio A M Mendes [Thu, 12 Feb 2026 03:34:23 +0000 (00:34 -0300)]
feat: pmm_alloc_blocks/pmm_free_blocks — contiguous physical page allocation for DMA buffers

7 weeks agofeat: guard pages — 32KB user stack with unmapped guard page below for stack overflow...
Tulio A M Mendes [Thu, 12 Feb 2026 03:30:19 +0000 (00:30 -0300)]
feat: guard pages — 32KB user stack with unmapped guard page below for stack overflow detection

7 weeks agofeat: alarm() syscall (83) — per-process SIGALRM timer via scheduler tick check
Tulio A M Mendes [Thu, 12 Feb 2026 03:25:31 +0000 (00:25 -0300)]
feat: alarm() syscall (83) — per-process SIGALRM timer via scheduler tick check

7 weeks agofeat: RTC driver (CMOS real-time clock) + clock_gettime(CLOCK_REALTIME) uses wall...
Tulio A M Mendes [Thu, 12 Feb 2026 03:20:48 +0000 (00:20 -0300)]
feat: RTC driver (CMOS real-time clock) + clock_gettime(CLOCK_REALTIME) uses wall-clock time

7 weeks agofeat: readv/writev syscalls (81/82) + ulibc sys/uio.h wrappers
Tulio A M Mendes [Thu, 12 Feb 2026 03:14:55 +0000 (00:14 -0300)]
feat: readv/writev syscalls (81/82) + ulibc sys/uio.h wrappers

7 weeks agofeat: ulibc realpath() — resolves '.', '..', relative paths via getcwd
Tulio A M Mendes [Thu, 12 Feb 2026 03:10:44 +0000 (00:10 -0300)]
feat: ulibc realpath() — resolves '.', '..', relative paths via getcwd

7 weeks agofeat: ulibc stdio.h buffered I/O (FILE, fopen/fclose/fread/fwrite/fflush/fgetc/fgets...
Tulio A M Mendes [Thu, 12 Feb 2026 03:07:11 +0000 (00:07 -0300)]
feat: ulibc stdio.h buffered I/O (FILE, fopen/fclose/fread/fwrite/fflush/fgetc/fgets/fputc/fputs/fprintf/vfprintf/feof/ferror, stdin/stdout/stderr)

7 weeks agofeat: sigsuspend syscall (80) — temporarily replace signal mask and block until signa...
Tulio A M Mendes [Thu, 12 Feb 2026 03:02:32 +0000 (00:02 -0300)]
feat: sigsuspend syscall (80) — temporarily replace signal mask and block until signal delivery

7 weeks agofeat: truncate/ftruncate syscalls (78/79) + ulibc wrappers
Tulio A M Mendes [Thu, 12 Feb 2026 02:58:41 +0000 (23:58 -0300)]
feat: truncate/ftruncate syscalls (78/79) + ulibc wrappers

7 weeks agofeat: sigpending, pread/pwrite, access, umask, setuid/setgid syscalls + ulibc wrappers
Tulio A M Mendes [Thu, 12 Feb 2026 02:54:11 +0000 (23:54 -0300)]
feat: sigpending, pread/pwrite, access, umask, setuid/setgid syscalls + ulibc wrappers

- SYSCALL_SIGPENDING (71): returns pending & blocked signal mask
- SYSCALL_PREAD/PWRITE (72/73): positional read/write without altering file offset
- SYSCALL_ACCESS (74): checks file existence (simplified, no real perm check yet)
- SYSCALL_UMASK (75): per-process file creation mask (new umask field in struct process)
- SYSCALL_SETUID/SETGID (76/77): change process uid/gid
- Extract pread/pwrite/access into noinline posix_ext_syscall_dispatch to avoid stack bloat
- ulibc: signal.h sigpending(), unistd.h pread/pwrite/access/setuid/setgid

7 weeks agofeat: O_APPEND support in write() + fcntl F_SETFL
Tulio A M Mendes [Thu, 12 Feb 2026 02:45:09 +0000 (23:45 -0300)]
feat: O_APPEND support in write() + fcntl F_SETFL

7 weeks agofeat: fsync/fdatasync syscall stubs (no-op, POSIX compliance)
Tulio A M Mendes [Thu, 12 Feb 2026 02:40:17 +0000 (23:40 -0300)]
feat: fsync/fdatasync syscall stubs (no-op, POSIX compliance)

7 weeks agofeat: ulibc signal.h with raise(), kill(), sigprocmask() and POSIX signal constants
Tulio A M Mendes [Thu, 12 Feb 2026 02:36:01 +0000 (23:36 -0300)]
feat: ulibc signal.h with raise(), kill(), sigprocmask() and POSIX signal constants

7 weeks agodocs: comprehensive documentation update reflecting all 15 implemented features ...
Tulio A M Mendes [Thu, 12 Feb 2026 02:27:09 +0000 (23:27 -0300)]
docs: comprehensive documentation update reflecting all 15 implemented features (threads, networking, dynamic linking, shell, core utils, permissions, symlinks, PAE+NX, procfs, multi-PTY, VMIN/VTIME)

7 weeks agofeat: dynamic linking infrastructure - PT_INTERP support, ET_DYN validation, elf32_lo...
Tulio A M Mendes [Thu, 12 Feb 2026 02:19:39 +0000 (23:19 -0300)]
feat: dynamic linking infrastructure - PT_INTERP support, ET_DYN validation, elf32_load_interp, ELF dynamic section types, auxiliary vector definitions

7 weeks agofeat: threads (clone/pthread) - SYSCALL_CLONE, SYSCALL_GETTID, SET_THREAD_AREA, proce...
Tulio A M Mendes [Thu, 12 Feb 2026 02:14:24 +0000 (23:14 -0300)]
feat: threads (clone/pthread) - SYSCALL_CLONE, SYSCALL_GETTID, SET_THREAD_AREA, process_clone_create, ulibc pthread stubs

7 weeks agofeat: socket syscalls (socket/bind/listen/accept/connect/send/recv/sendto/recvfrom)
Tulio A M Mendes [Thu, 12 Feb 2026 02:01:31 +0000 (23:01 -0300)]
feat: socket syscalls (socket/bind/listen/accept/connect/send/recv/sendto/recvfrom)

Kernel socket subsystem over lwIP TCP/UDP PCBs with ring-buffer RX,
wait queues for blocking ops, and fd integration via sentinel file
structs (flags=0x534F434B).

Socket dispatch extracted to separate noinline function to prevent
syscall_handler stack overflow that caused heap corruption.

7 weeks agofeat: lwIP TCP/IP stack integration with E1000 netif
Tulio A M Mendes [Wed, 11 Feb 2026 23:43:33 +0000 (20:43 -0300)]
feat: lwIP TCP/IP stack integration with E1000 netif

- third_party/lwip/ added to .gitignore (cloned separately)
- src/net/lwip_port/lwipopts.h: NO_SYS=1 config, IPv4 only, TCP+UDP+ICMP+ARP
- src/net/lwip_port/arch/cc.h: compiler/type defines for lwIP on x86
- src/net/lwip_port/sys_arch.c: sys_now() using kernel tick counter
- src/net/e1000_netif.c: lwIP netif driver bridging E1000 hardware
  net_init() configures IP 10.0.2.15/24, gw 10.0.2.2 (QEMU user-mode)
  net_poll() feeds RX packets to lwIP + processes timeouts
- include/net.h: public API (net_init, net_poll, net_get_netif)
- include/utils.h + src/kernel/utils.c: added memmove, memcmp, strncpy,
  strtol, __memcpy_chk, __ctype_b_loc stubs needed by lwIP
- Makefile: lwIP core+ipv4+ethernet sources compiled with relaxed warnings,
  include paths for lwip_port and lwip/src/include
- src/kernel/main.c: net_poll() in idle loop
- src/kernel/init.c: net_init() after e1000_init()
- 19/19 smoke tests pass, cppcheck clean

7 weeks agofeat: E1000 NIC driver (Intel 82540EM)
Tulio A M Mendes [Wed, 11 Feb 2026 23:33:57 +0000 (20:33 -0300)]
feat: E1000 NIC driver (Intel 82540EM)

- include/e1000.h: register defines, TX/RX descriptor structs, public API
- src/drivers/e1000.c: full E1000 driver implementation
  PCI BAR0 MMIO mapping (128KB at 0xC0230000), bus mastering enabled
  EEPROM MAC address read, TX/RX descriptor ring setup (32 entries each)
  DMA buffer allocation, legacy TX descriptors, interrupt handler
  e1000_send/e1000_recv/e1000_get_mac/e1000_link_up API
- src/arch/x86/arch_platform.c: IOAPIC route IRQ 11 -> vector 43
- src/kernel/init.c: call e1000_init() after pci_init()
- Tested: MAC 52:54:00:12:34:56, IRQ=11, 19/19 smoke tests pass

7 weeks agofeat: PAE paging + NX bit support
Tulio A M Mendes [Wed, 11 Feb 2026 23:21:00 +0000 (20:21 -0300)]
feat: PAE paging + NX bit support

- src/arch/x86/boot.S: complete rewrite for PAE 3-level page tables
  PDPT (4 entries) + 4 PDs (512 entries each) + 8 PTs covering 16MB
  CR4.PAE enabled before paging, recursive mapping via PD[3][508-511]
- src/arch/x86/vmm.c: complete rewrite for 64-bit PAE entries
  New recursive mapping accessors (PD at 0xFFFFC000, PT at 0xFF800000)
  NX bit support (bit 63), VMM_FLAG_NX added
  All address space ops updated: create, clone, destroy, CoW, fault handler
- src/arch/x86/ap_trampoline.S: enable CR4.PAE before paging for APs
- src/arch/x86/elf.c: updated page table check to PAE 64-bit entries
- src/arch/x86/uaccess.c: updated page present/writable checks for PAE
- include/vmm.h: added VMM_FLAG_NX define
- cppcheck clean, 19/19 smoke tests pass

7 weeks agofeat: per-process errno + set_thread_area syscall stub for future TLS
Tulio A M Mendes [Wed, 11 Feb 2026 22:57:23 +0000 (19:57 -0300)]
feat: per-process errno + set_thread_area syscall stub for future TLS

- user/errno.c: documented per-process errno isolation via fork
- include/syscall.h: added SYSCALL_SET_THREAD_AREA(57) for future TLS
- src/kernel/syscall.c: set_thread_area dispatch stub (-ENOSYS)
- True TLS deferred until clone/threads (task #14) is implemented
- cppcheck clean, 19/19 smoke tests pass

7 weeks agofeat: symbolic links (symlink, readlink) and link stub
Tulio A M Mendes [Wed, 11 Feb 2026 22:53:00 +0000 (19:53 -0300)]
feat: symbolic links (symlink, readlink) and link stub

- include/fs.h: added FS_SYMLINK type and symlink_target[128] field to fs_node_t
- include/stat.h: added S_IFLNK define
- include/syscall.h: added SYSCALL_LINK(54), SYSCALL_SYMLINK(55), SYSCALL_READLINK(56)
- src/kernel/fs.c: vfs_lookup follows symlinks with depth limit (max 8)
- src/kernel/tmpfs.c: tmpfs_create_symlink creates FS_SYMLINK nodes
- src/kernel/syscall.c: symlink_impl, readlink_impl, link_impl (stub -ENOSYS)
  stat_from_node reports S_IFLNK for symlink nodes
- cppcheck clean, 19/19 smoke tests pass

7 weeks agofeat: permissions support (uid/gid/mode, chmod, chown, getuid, getgid)
Tulio A M Mendes [Wed, 11 Feb 2026 22:46:34 +0000 (19:46 -0300)]
feat: permissions support (uid/gid/mode, chmod, chown, getuid, getgid)

- include/fs.h: added uid, gid, mode fields to fs_node_t
- include/process.h: added uid, gid fields to struct process
- include/stat.h: added st_uid, st_gid to struct stat, permission bit defines
- include/syscall.h: added SYSCALL_CHMOD(50), SYSCALL_CHOWN(51), SYSCALL_GETUID(52), SYSCALL_GETGID(53)
- src/kernel/syscall.c: chmod_impl, chown_impl, getuid/getgid dispatch; stat_from_node now populates uid/gid/mode
- user/init.c: updated struct stat to match kernel layout
- cppcheck clean, 19/19 smoke tests pass

7 weeks agofeat: /proc per-process directories (/proc/[pid]/status, maps)
Tulio A M Mendes [Wed, 11 Feb 2026 22:39:04 +0000 (19:39 -0300)]
feat: /proc per-process directories (/proc/[pid]/status, maps)

- /proc/[pid]/status: shows Pid, PPid, Pgrp, Session, State, signals, heap
- /proc/[pid]/maps: shows heap range and mmap regions
- /proc root readdir now lists numeric PID entries alongside self/uptime/meminfo
- /proc root finddir resolves numeric names to per-PID directory nodes
- Uses small static pool of fs_node_t (8 slots) for dynamic PID nodes
- proc_find_pid helper iterates ready queue to find process by PID
- cppcheck clean, 19/19 smoke tests pass

7 weeks agorefactor: generic wait queue abstraction (waitqueue.h)
Tulio A M Mendes [Wed, 11 Feb 2026 22:32:55 +0000 (19:32 -0300)]
refactor: generic wait queue abstraction (waitqueue.h)

- include/waitqueue.h: reusable waitqueue_t with wq_init/push/pop/wake_one/wake_all
- src/kernel/tty.c: refactored to use waitqueue_t instead of ad-hoc arrays
- src/kernel/pty.c: refactored to use waitqueue_t instead of ad-hoc arrays
- Removed duplicate waitq_push/waitq_pop/waitq_empty/waitq_wake_one from both files
- cppcheck clean, 19/19 smoke tests pass

7 weeks agofeat: core utilities (cat, ls, mkdir, rm)
Tulio A M Mendes [Wed, 11 Feb 2026 22:27:23 +0000 (19:27 -0300)]
feat: core utilities (cat, ls, mkdir, rm)

- user/cat.c: reads files or stdin, outputs to stdout
- user/ls.c: lists directory entries via getdents syscall
- user/mkdir.c: creates directories via mkdir syscall
- user/rm.c: removes files (unlink) or directories (-r/-d rmdir)
- Makefile: build rules for all four + initrd packaging as /bin/*
- echo already exists as shell builtin
- cppcheck clean, 19/19 smoke tests pass

7 weeks agofeat: add minimal POSIX sh shell (/bin/sh)
Tulio A M Mendes [Wed, 11 Feb 2026 22:22:37 +0000 (19:22 -0300)]
feat: add minimal POSIX sh shell (/bin/sh)

- user/sh.c: minimal sh-compatible shell with:
  - Line reading from stdin
  - Argument parsing (whitespace-delimited)
  - PATH resolution (/bin/, /disk/bin/)
  - Input/output redirection (< and >)
  - Pipeline support (up to 4 stages with |)
  - Builtins: exit, echo
  - External commands via fork+execve+waitpid
- Makefile: added SH_ELF build rule and initrd packaging as /bin/sh
- cppcheck clean, 19/19 smoke tests pass

7 weeks agofeat: VMIN/VTIME termios fields for non-canonical TTY reads
Tulio A M Mendes [Tue, 10 Feb 2026 13:23:13 +0000 (10:23 -0300)]
feat: VMIN/VTIME termios fields for non-canonical TTY reads

- Added c_cc[NCCS] array to struct termios with VMIN/VTIME indices
- TCGETS/TCSETS now get/set c_cc values
- tty_read_kbuf implements full POSIX non-canonical semantics:
  VMIN=0,VTIME=0: poll (return immediately)
  VMIN>0,VTIME=0: block until VMIN chars available
  VMIN=0,VTIME>0: timeout read (tenths of second)
  VMIN>0,VTIME>0: block with inter-char timeout
- tty_read refactored to delegate to tty_read_kbuf (no duplication)
- Default: VMIN=1, VTIME=0 (standard blocking single-char read)
- cppcheck clean, 19/19 smoke tests pass

7 weeks agofeat: multiple PTY pairs (up to 8 dynamic /dev/pts/N)
Tulio A M Mendes [Tue, 10 Feb 2026 12:55:26 +0000 (09:55 -0300)]
feat: multiple PTY pairs (up to 8 dynamic /dev/pts/N)

Refactored PTY subsystem from single global pair to array of up to 8 pairs:
- New pty_pair struct with per-pair buffers, waitqueues, session/pgrp
- pty_alloc_pair() allocates new pairs dynamically
- Inode encoding: masters=100+N, slaves=200+N
- pty_get_master_node()/pty_get_slave_node() return per-pair fs_node_t
- All _idx() variants for indexed pair access
- Old single-pair API preserved as wrappers around pair 0
- devfs /dev/pts/ now lists all active pairs dynamically
- syscall.c poll/nonblock/ioctl updated to use pty_is_master_ino/pty_is_slave_ino
- cppcheck clean, 19/19 smoke tests pass

7 weeks agofeat: add /dev/zero, /dev/random, /dev/urandom, /dev/console device nodes
Tulio A M Mendes [Tue, 10 Feb 2026 12:31:13 +0000 (09:31 -0300)]
feat: add /dev/zero, /dev/random, /dev/urandom, /dev/console device nodes

- /dev/zero: returns zeroes on read, discards writes
- /dev/random: xorshift32 PRNG seeded from PIT tick counter
- /dev/urandom: alias to /dev/random (same PRNG)
- /dev/console: aliases to TTY read/write (like /dev/tty)
- All registered in devfs finddir + readdir (8 devices total)
- cppcheck clean, 19/19 smoke tests pass

7 weeks agodocs: comprehensive documentation update reflecting current AdrOS state
Tulio A M Mendes [Tue, 10 Feb 2026 12:11:52 +0000 (09:11 -0300)]
docs: comprehensive documentation update reflecting current AdrOS state

Updated all 4 documentation files to accurately reflect the current codebase:

POSIX_ROADMAP.md:
- fork now marked as CoW (was 'full copy')
- execve marked complete (was partial)
- brk/sbrk, nanosleep, clock_gettime marked implemented
- O_CLOEXEC, FD_CLOEXEC marked implemented
- Signal defaults marked complete (Ctrl+C/Z/D/\)
- Raw TTY mode, TIOCGWINSZ marked implemented
- PMM now shows spinlock + refcount
- Slab allocator, CoW, shared memory, SMEP marked implemented
- Heap shows dynamic growth to 64MB
- PCI, ATA DMA, LAPIC/IOAPIC, SMP, ACPI, VBE, SYSENTER, CPUID added
- ulibc marked implemented
- procfs partial noted
- Priority roadmap updated: 9 items marked done, remaining reordered

SUPPLEMENTARY_ANALYSIS.md:
- POSIX score updated from ~45% to ~70%
- PMM: spinlock + refcount now implemented
- VMM: CoW + SMEP now implemented
- Heap: slab allocator + dynamic growth now implemented
- Scheduler: O(1) with bitmap now implemented
- TTY: raw mode, signal chars, TIOCGWINSZ now implemented
- Userland: ulibc + SYSENTER now implemented
- Drivers: PCI, ATA DMA, LAPIC/IOAPIC, SMP, ACPI, VBE now implemented
- Advanced: CoW, slab, shmem now implemented
- Part 3 comparison table updated (AdrOS ahead in 5/10 dimensions)
- Part 4 recommendations: 9 items marked completed, remaining reordered

README.md:
- Complete rewrite of Features section with organized subsections
- Added: Memory Management, Process & Scheduling, Syscalls, TTY/PTY,
  Filesystems (6 types), Drivers & Hardware, Userland, Security, Testing
- Updated TODO with near-term/medium-term/long-term priorities
- Updated Directory Structure with new paths

BUILD_GUIDE.md:
- Added expect and sparse to dependencies
- Added Testing section with all make targets
- Increased smoke test timeout from 40s to 60s

7 weeks agodocs: update audit report with fix status for all CRITICAL and HIGH findings
Tulio A M Mendes [Tue, 10 Feb 2026 11:45:57 +0000 (08:45 -0300)]
docs: update audit report with fix status for all CRITICAL and HIGH findings

7 weeks agotest: expand test suite with security unit tests and GDB scripted checks
Tulio A M Mendes [Tue, 10 Feb 2026 11:44:55 +0000 (08:44 -0300)]
test: expand test suite with security unit tests and GDB scripted checks

New test_security.c (19 tests):
- user_range_ok: 11 tests covering NULL, zero-len, valid user addr,
  kernel addr rejection, boundary spanning, overflow, max user range
- bitmap: 4 tests for set/unset/cross-byte/all-bits operations
- eflags sanitization: 4 tests verifying IOPL clearing and IF forcing

New gdb_checks.py:
- Automated GDB script for QEMU+GDB integrity checks
- Verifies heap magic, PMM bitmap frame 0, max_frames, VGA mapping
- Usage: make test-gdb

Updated Makefile:
- test-host now runs both test_utils (28) and test_security (19) = 47 tests
- Added test-gdb target for GDB scripted checks
- Total: 47 host unit tests + 19 smoke checks = 66 automated tests

7 weeks agofix: 5 HIGH severity bugs from audit
Tulio A M Mendes [Tue, 10 Feb 2026 11:38:03 +0000 (08:38 -0300)]
fix: 5 HIGH severity bugs from audit

2.3: Slab allocator now uses kmalloc(PAGE_SIZE) instead of
     pmm_alloc_page + hal_mm_phys_to_virt. The old approach could
     map physical addresses above 16MB to VAs that collide with the
     heap range (0xD0000000+), causing silent memory corruption.

3.3: execve now validates sp against stack base before each write.
     Prevents writing below the user stack page if E2BIG pre-check
     is somehow bypassed. Returns -E2BIG on underflow.

3.4: SMEP (Supervisor Mode Execution Prevention) enabled in CR4
     if CPU supports it. Prevents kernel from executing user-mapped
     pages, blocking a common exploit technique. SMAP detection added
     but not enabled yet (requires STAC/CLAC in uaccess.c first).
     CPUID leaf 7 detection added for SMEP (bit 7) and SMAP (bit 20).

4.1: Kernel heap now grows dynamically from 10MB up to 64MB max.
     When kmalloc can't find a free block, kheap_grow maps new
     physical pages at the end of the heap and creates a new free
     block. Coalesces with tail if adjacent and free.

2.4: process_waitpid circular list traversal now checks for NULL
     before comparing to start, preventing NULL deref if the list
     is broken by concurrent reaping.

7 weeks agofix: 4 CRITICAL security/race bugs from audit
Tulio A M Mendes [Tue, 10 Feb 2026 11:29:36 +0000 (08:29 -0300)]
fix: 4 CRITICAL security/race bugs from audit

3.1: user_range_ok weak default now rejects kernel addresses (>= 0xC0000000)
     Prevents privilege escalation via syscall arguments on non-x86 fallback.

3.2: sigreturn sanitizes eflags — clears IOPL bits, ensures IF set.
     Prevents userspace from gaining port I/O access via crafted sigframe.

2.1: PMM bitmap/refcount now protected by spinlock_t pmm_lock.
     Prevents SMP race where two CPUs could allocate the same physical frame.
     All public PMM functions (alloc, free, mark_region, incref, decref,
     get_refcount) now use spin_lock_irqsave/spin_unlock_irqrestore.

2.2: file->refcount now uses __sync_fetch_and_add / __sync_sub_and_fetch.
     Prevents use-after-free in fork/dup/dup2/dup3/close when timer IRQ
     fires and schedule() runs process_close_all_files_locked concurrently.

7 weeks agofeat: deep code audit + testing infrastructure (sparse, expect, host unit tests)
Tulio A M Mendes [Tue, 10 Feb 2026 11:07:09 +0000 (08:07 -0300)]
feat: deep code audit + testing infrastructure (sparse, expect, host unit tests)

Deep Code Audit (docs/AUDIT_REPORT.md):
- 18 findings across 4 categories: layer violations, logic/race
  conditions, security vulnerabilities, memory management
- CRITICAL: user_range_ok weak default allows kernel addr access
- CRITICAL: sigreturn allows IOPL escalation via eflags
- CRITICAL: PMM bitmap has no locking (SMP race)
- CRITICAL: file refcount manipulation not atomic
- HIGH: slab allocator hal_mm_phys_to_virt can hit heap VA
- HIGH: execve writes to user stack bypassing copy_to_user
- Full summary table with severity, category, location

Testing Infrastructure:
- make check    — cppcheck + sparse (kernel-oriented semantic checker)
- make analyzer — gcc -fanalyzer (interprocedural analysis)
- make test     — QEMU + expect automated smoke test (19 checks)
- make test-1cpu — single-CPU regression (50s timeout)
- make test-host — 28 host-side unit tests for pure functions
                   (itoa, itoa_hex, atoi, path_normalize, align)
- make test-all — all of the above

Testing Plan (docs/TESTING_PLAN.md):
- Layer 1: Static analysis (cppcheck + sparse + gcc -fanalyzer)
- Layer 2: QEMU + expect automated regression
- Layer 3: QEMU + GDB scripted debugging (future)
- Layer 4: Host-side unit tests for pure functions

All tests passing: 19/19 smoke, 28/28 unit, cppcheck clean.

7 weeks agofeat: Fase 9 — ATA Bus Master IDE DMA for read/write
Tulio A M Mendes [Tue, 10 Feb 2026 10:37:38 +0000 (07:37 -0300)]
feat: Fase 9 — ATA Bus Master IDE DMA for read/write

Implement Bus Master IDE DMA as a transparent upgrade over PIO:

- New ata_dma.c: Bus Master IDE DMA driver for PIIX3 IDE controller
  - Finds IDE controller via PCI class 0x01:0x01
  - Reads BAR4 for Bus Master I/O base, enables PCI bus mastering
  - Allocates PRDT and bounce buffer pages at dedicated VAs
    (0xC0220000/0xC0221000) to avoid heap VA collisions
  - Polling-based DMA completion (BSY clear + BM Active clear)
  - IRQ 14 handler with dma_active flag to prevent race between
    IRQ handler and polling loop on ATA status register
  - spin_lock (not irqsave) for serialization — PIIX3 requires
    interrupt delivery for DMA completion signaling

- Modified ata_pio.c: transparent DMA upgrade
  - ata_pio_init_primary_master registers IRQ 14 handler early
    (before IDENTIFY) to prevent INTRQ storm
  - Calls ata_dma_init after IDENTIFY to probe for DMA capability
  - ata_pio_read28/write28 delegate to DMA when available,
    fall back to PIO if DMA init failed

- New include/ata_dma.h: public API header

Key bugs fixed during development:
- hal_mm_phys_to_virt can map PMM pages into heap VA range
  (phys 0x10000000 -> virt 0xD0000000 = heap base) — use
  dedicated VAs instead
- ATA INTRQ must be deasserted by reading status register;
  without IRQ handler, unacknowledged INTRQ causes interrupt storm
- nIEN must be cleared before DMA so device asserts INTRQ
- DMA direction bit must be set before Start bit per ATA spec

Tested: 4-CPU (25s) and 1-CPU (45s) pass all init.elf tests
including diskfs getdents and /persist/counter.

7 weeks agofeat: Fase 8a — Per-CPU data infrastructure with GS-segment access
Tulio A M Mendes [Tue, 10 Feb 2026 09:26:46 +0000 (06:26 -0300)]
feat: Fase 8a — Per-CPU data infrastructure with GS-segment access

New files:
- include/arch/x86/percpu.h — Per-CPU data structure and GS-based
  accessors (percpu_get, percpu_cpu_index, percpu_current, etc.)
- src/arch/x86/percpu.c — Per-CPU init: creates GDT entries for each
  CPU's GS segment pointing to its percpu_data instance

Changes:
- include/arch/x86/smp.h: Split smp_init into smp_enumerate() and
  smp_start_aps() to allow percpu_init between enumeration and SIPI
- src/arch/x86/smp.c: Implement two-phase SMP init
- include/arch/x86/gdt.h: Export gdt_ptr struct, gp variable, and
  gdt_set_gate_ext() for per-CPU GDT entry creation
- src/arch/x86/gdt.c: Expand GDT from 6 to 24 entries (6 base +
  up to 16 per-CPU GS segments). Add gdt_set_gate_ext(). Make gp
  non-static.
- src/arch/x86/arch_platform.c: Call smp_enumerate() -> percpu_init()
  -> percpu_setup_gs(0) -> smp_start_aps() in correct order

Boot sequence for per-CPU setup:
1. smp_enumerate() — populate cpu_info from ACPI MADT
2. percpu_init() — create GDT entries for each CPU's GS segment
3. percpu_setup_gs(0) — BSP loads its own GS selector
4. smp_start_aps() — send INIT-SIPI-SIPI; each AP calls
   percpu_setup_gs(i) during its init

Passes: make, cppcheck, QEMU smoke test (-smp 1 and -smp 4)

7 weeks agofeat: Fase 7 — ACPI MADT parser + SMP AP bootstrap (INIT-SIPI-SIPI)
Tulio A M Mendes [Tue, 10 Feb 2026 09:16:11 +0000 (06:16 -0300)]
feat: Fase 7 — ACPI MADT parser + SMP AP bootstrap (INIT-SIPI-SIPI)

New files:
- include/arch/x86/acpi.h — ACPI RSDP/RSDT/MADT structures and API
- include/arch/x86/smp.h — SMP per-CPU info and bootstrap API
- src/arch/x86/acpi.c — ACPI table parser: find RSDP in EBDA/BIOS ROM,
  parse RSDT, extract MADT entries (LAPIC, IOAPIC, ISO). Uses temporary
  VMM mappings for tables above the 16MB identity-mapped range.
- src/arch/x86/smp.c — SMP bootstrap: copy 16-bit trampoline to 0x8000,
  patch GDT/CR3/stack/entry, send INIT-SIPI-SIPI per AP, wait for ready.
- src/arch/x86/ap_trampoline.S — 16-bit real-mode AP entry point:
  load GDT, enable protected mode, far-jump to 32-bit, load CR3,
  enable paging, jump to C ap_entry().

Changes:
- include/arch/x86/lapic.h: Add lapic_send_ipi(), rdmsr(), wrmsr() decls
- src/arch/x86/lapic.c: Implement lapic_send_ipi() using ICR_HI/ICR_LO
  with delivery-status polling. Make rdmsr/wrmsr non-static.
- include/arch/x86/gdt.h: Export struct gdt_ptr and gp variable
- src/arch/x86/gdt.c: Make gp non-static for AP trampoline access
- src/arch/x86/linker.ld: Include .ap_trampoline section in .rodata
- src/arch/x86/arch_platform.c: Call acpi_init() then smp_init() after
  LAPIC/IOAPIC setup
- src/arch/x86/idt.c: Move IRQ EOI before handler callback (critical fix:
  schedule() in timer handler context-switches away, blocking LAPIC if
  EOI is deferred). Add IDT gate + ISR stub for spurious vector 255.
- src/arch/x86/interrupts.S: Add ISR_NOERRCODE 255 stub

Key design decisions:
- Trampoline at fixed phys 0x8000, data area at 0x8F00
- APs share BSP's page directory (same CR3)
- APs enable their own LAPIC and halt (idle loop for now)
- ACPI tables mapped via temporary VMM window at 0xC0202000
- Each AP gets a 4KB kernel stack from static array

Tested: -smp 1 (single CPU, all init tests OK)
        -smp 4 (4 CPUs started, all init tests OK)
Passes: make, cppcheck, QEMU smoke test

7 weeks agofeat: Fase 6 — LAPIC + IOAPIC drivers, replace legacy PIC 8259
Tulio A M Mendes [Tue, 10 Feb 2026 08:40:28 +0000 (05:40 -0300)]
feat: Fase 6 — LAPIC + IOAPIC drivers, replace legacy PIC 8259

New files:
- include/arch/x86/lapic.h — LAPIC register definitions and API
- include/arch/x86/ioapic.h — IOAPIC register definitions and API
- src/arch/x86/lapic.c — Local APIC driver: init, EOI, MMIO access,
  timer calibration via PIT channel 2, pic_disable()
- src/arch/x86/ioapic.c — I/O APIC driver: init, IRQ routing,
  mask/unmask per-IRQ line

Changes:
- vmm.h: Add VMM_FLAG_PWT, VMM_FLAG_PCD, VMM_FLAG_NOCACHE for MMIO
- vmm.c: Translate PWT/PCD flags to x86 PTE bits in vmm_flags_to_x86
- arch_platform.c: Init LAPIC+IOAPIC after syscall_init, route ISA
  IRQs (timer=32, kbd=33, ATA=46) through IOAPIC, disable PIC only
  after IOAPIC routes are live
- idt.c: Send EOI BEFORE handler callback (critical: schedule() in
  timer handler context-switches away; deferred EOI blocks LAPIC).
  Add IDT gate for spurious vector 255; skip EOI for spurious
  interrupts per Intel spec.
- interrupts.S: Add ISR stub for vector 255 (LAPIC spurious)
- timer.c: Use LAPIC periodic timer when available, fallback to PIT

Key design decisions:
- LAPIC MMIO mapped at 0xC0200000 (above kernel _end, below heap)
- IOAPIC MMIO mapped at 0xC0201000
- Both mapped with PCD+PWT (cache-disable) to prevent MMIO caching
- PIC disabled only AFTER IOAPIC routes configured (avoids IRQ gap)
- EOI sent before handler to prevent LAPIC starvation on context switch
- Spurious vector 255 has IDT entry but no EOI (Intel requirement)
- LAPIC timer calibrated against PIT channel 2 (~10ms measurement)

Bugs fixed during development:
- VA 0xC0100000 overlapped kernel text — moved to 0xC0200000
- pic_disable() inside lapic_init() caused IRQ gap — moved to caller
- EOI after handler blocked LAPIC when schedule() context-switched
- Missing IDT entry for vector 255 caused triple fault on spurious IRQ

Passes: make, cppcheck, QEMU smoke test (all init tests OK).

7 weeks agofix: audit and correct bugs in Fases 1-4
Tulio A M Mendes [Tue, 10 Feb 2026 08:13:16 +0000 (05:13 -0300)]
fix: audit and correct bugs in Fases 1-4

Fase 1 (CPUID):
- cpuid.c: Replace strict-aliasing-violating uint32_t* casts for
  vendor string with memcpy() to avoid UB
- cpuid.c: Increase itoa tmp buffer from 4 to 12 bytes to prevent
  potential overflow with larger APIC IDs

Fase 2 (Spinlock): No bugs found — TTAS + cpu_relax + barriers OK

Fase 3 (SYSENTER):
- sysenter.S: Add 'cld' at entry to clear direction flag. Userspace
  could leave DF=1 and SYSENTER doesn't reset EFLAGS, which would
  corrupt any kernel string/memory operations using rep movsb/stosb

Fase 4 (ulibc):
- stdio.c: Fix PUTC macro underflow — use 'pos+1 < size' instead
  of 'pos < size-1' which underflows to SIZE_MAX when size==0
- stdio.c: Fix vsnprintf(buf, 0, fmt) — was counting raw format
  chars instead of returning 0; now returns 0 immediately
- stdlib.c: Use uintptr_t instead of unsigned int for brk() pointer
  comparison to be correct on all architectures
- unistd.c: Replace 'hlt' with 'nop' in _exit() fallback loop —
  hlt is privileged and causes #GP in ring 3

Passes: make, ulibc build, cppcheck, QEMU smoke test.

7 weeks agofix: correct 5 bugs in shared memory IPC (Fase 5 audit)
Tulio A M Mendes [Tue, 10 Feb 2026 08:04:52 +0000 (05:04 -0300)]
fix: correct 5 bugs in shared memory IPC (Fase 5 audit)

Bugs found and fixed during deep audit of the Fase 5 commit
(implemented during WSL2/GCC instability):

BUG 1 (CRITICAL): vmm_map_page args were inverted in shm_at().
  Signature is vmm_map_page(phys, virt, flags) but code passed
  (virt, phys, flags). Would map physical pages at wrong addresses
  causing memory corruption. Fixed both code paths.

BUG 2 (CRITICAL): shm_dt() used broken heuristic to find segment.
  Matched by npages count — if two segments had same page count,
  wrong one got decremented. Added shmid field to mmap entry struct
  for direct O(1) lookup. Removed dead code loop that computed
  expected_va and discarded it.

BUG 3: shm_at() with shmaddr!=0 didn't register in mmaps[].
  shm_dt() would never find the slot, returning -EINVAL.
  Now always registers in mmap table regardless of shmaddr.

BUG 4: shm_destroy() only cleared 'used' flag, leaving stale
  key/size/npages/nattch. Now memset()s entire struct to zero.

BUG 5: shm_ctl(IPC_STAT) wrote directly to userspace pointer
  while holding spinlock. Page fault under spinlock = deadlock.
  Now copies to local struct, releases lock, then copy_to_user().

Additional fixes:
- Added shmid field to process mmap entry (process.h)
- Initialize mmaps[].shmid = -1 in all 3 process creation paths
  (process_init, process_create_kernel, process_fork_create)
- Set shmid = -1 in syscall_mmap_impl and syscall_munmap_impl
- Fork now copies parent's mmap table to child (with shmid)

Passes: make, cppcheck, QEMU smoke test (all init tests OK).

7 weeks agofeat: implement shared memory IPC (shmget/shmat/shmdt/shmctl)
Tulio A M Mendes [Tue, 10 Feb 2026 07:39:00 +0000 (04:39 -0300)]
feat: implement shared memory IPC (shmget/shmat/shmdt/shmctl)

Add System V-style shared memory IPC subsystem:
- src/kernel/shm.c: kernel-side segment manager with up to 32
  segments, each up to 16 pages (64KB). Physical pages allocated
  via PMM, mapped into user address space via VMM.
- include/shm.h: API + constants (IPC_CREAT, IPC_EXCL, IPC_RMID,
  IPC_PRIVATE)
- Syscalls 46-49: SHMGET, SHMAT, SHMDT, SHMCTL wired in syscall.c
- shm_init() called from kernel_main after kheap_init
- Deferred destruction: IPC_RMID with nattch>0 defers free until
  last detach

Also fixes:
- tty.c: add utils.h include for memset (cross-compiler strictness)
- usermode.c: fix ebp clobber error with cross-compiler by using
  ESI as scratch register instead

Passes: make, cppcheck, QEMU smoke test.

7 weeks agofeat: add ulibc — minimal userspace C library
Tulio A M Mendes [Tue, 10 Feb 2026 07:19:46 +0000 (04:19 -0300)]
feat: add ulibc — minimal userspace C library

Create user/ulibc/ with headers and implementations for:
- crt0.S: _start entry point that calls main() then exit()
- syscall.h: raw INT 0x80 wrappers (_syscall0..5)
- unistd.c/h: POSIX wrappers (read, write, open, close, fork,
  execve, pipe, dup2, brk, getpid, chdir, mkdir, etc)
- string.c/h: memcpy, memset, memmove, strlen, strcmp, strcpy,
  strchr, strcat, etc
- stdlib.c/h: malloc/free (bump allocator via brk), calloc,
  realloc, atoi, exit
- stdio.c/h: printf, puts, putchar, snprintf, vsnprintf with
  format specifiers: %d %i %u %x %X %s %c %p %%
- errno.c/h: errno variable + error codes + __syscall_ret helper

Build system: user/ulibc/Makefile produces libulibc.a static lib.
Main Makefile updated with ULIBC_LIB target.

Future user programs can link against libulibc.a instead of
duplicating syscall wrappers inline.

Passes: make (kernel), ulibc builds clean, QEMU smoke test OK.

7 weeks agofeat: implement SYSENTER/SYSEXIT fast syscall entry for x86-32
Tulio A M Mendes [Tue, 10 Feb 2026 07:07:14 +0000 (04:07 -0300)]
feat: implement SYSENTER/SYSEXIT fast syscall entry for x86-32

Add fast syscall support via SYSENTER/SYSEXIT (SEP), ~10x faster
than INT 0x80 (~30 cycles vs ~300 cycles overhead).

Components:
- src/arch/x86/sysenter.S: assembly entry point that builds a
  struct registers frame compatible with existing syscall_handler()
- src/arch/x86/sysenter_init.c: MSR setup (0x174=CS, 0x175=ESP,
  0x176=EIP), CPUID check for SEP support
- syscall_handler() made non-static so assembly can call it
- tss_set_kernel_stack() now also updates SYSENTER ESP MSR so
  context switches keep the fast path working

Userspace convention:
  push ecx; push edx; push $return; mov esp,ecx; sysenter
  EAX=syscall_no, EBX=arg1, ECX=arg2, EDX=arg3, ESI=arg4, EDI=arg5

INT 0x80 remains as fallback for CPUs without SEP.
QEMU confirms: [SYSENTER] Fast syscall enabled.

Passes: make, cppcheck, QEMU smoke test.

7 weeks agofeat: improve spinlock with per-arch cpu_relax() and memory barriers
Tulio A M Mendes [Tue, 10 Feb 2026 06:57:43 +0000 (03:57 -0300)]
feat: improve spinlock with per-arch cpu_relax() and memory barriers

Spinlock improvements for SMP readiness:
- Add cpu_relax() with per-arch spin-wait hints:
  x86: PAUSE, ARM/AArch64: YIELD, RISC-V: FENCE, MIPS: PAUSE
- Add __sync_synchronize() barrier before lock release in spin_unlock
- Add spin_is_locked() debug helper
- Add spin_trylock() for non-blocking lock attempts
- TTAS (test-and-test-and-set) pattern with cpu_relax() in inner loop
- __sync_lock_test_and_set maps to XCHG (x86), LDREX/STREX (ARM),
  AMOSWAP.W.AQ (RISC-V), LL/SC (MIPS)

Passes: make, cppcheck, QEMU smoke test.

7 weeks agofeat: implement CPUID feature detection + HAL wrapper
Tulio A M Mendes [Tue, 10 Feb 2026 06:53:50 +0000 (03:53 -0300)]
feat: implement CPUID feature detection + HAL wrapper

Add x86 CPUID detection (src/arch/x86/cpuid.c) that queries:
- Leaf 0: vendor string, max leaf
- Leaf 1: feature flags (PAE, NX, APIC, SEP, SSE, SSE2, HTT, etc)
- Leaf 1 EBX: topology (APIC ID, logical CPUs)
- Extended leaves: NX, Long Mode, SYSCALL, brand string

HAL wrapper (include/hal/cpu_features.h):
- hal_cpu_detect_features() / hal_cpu_get_features() / hal_cpu_print_features()
- x86 impl in src/hal/x86/cpu_features.c
- Weak default stub in src/kernel/cpu_features.c

Called from kernel_main() right after console_init().
QEMU output: GenuineIntel, PAE APIC SEP SSE SSE2 FXSR HYPERVISOR.

Passes: make, cppcheck, QEMU smoke test.

7 weeks agorefactor: remove dead shell.c, integrate commands into kconsole
Tulio A M Mendes [Tue, 10 Feb 2026 06:27:16 +0000 (03:27 -0300)]
refactor: remove dead shell.c, integrate commands into kconsole

shell.c became dead code after kconsole_enter() replaced shell_init()
as the fallback when init_start() fails. Nobody called shell_init().

- Integrate useful shell commands into kconsole: ls, cat, clear,
  mem, sleep, ring3 (via arch_platform_usermode_test_start)
- Remove src/kernel/shell.c and include/shell.h
- Remove shell.h include from main.c
- kconsole is now the single kernel-mode interactive console

Passes: make, cppcheck, QEMU smoke test.

7 weeks agorefactor: move x86 uaccess page table walking to src/arch/x86/uaccess.c
Tulio A M Mendes [Tue, 10 Feb 2026 06:19:36 +0000 (03:19 -0300)]
refactor: move x86 uaccess page table walking to src/arch/x86/uaccess.c

The uaccess implementation used x86-specific recursive page table
mapping (0xFFFFF000/0xFFC00000) for user_range_ok, copy_from_user,
copy_to_user, and uaccess_try_recover. This is 100% arch-dependent.

- Move full x86 implementation to src/arch/x86/uaccess.c (no guards)
- Replace src/kernel/uaccess.c with weak stubs (simple memcpy-based)
- The linker picks the arch-specific version when available

Passes: make, cppcheck, QEMU smoke test.

7 weeks agorefactor: make pmm.c fully architecture-independent
Tulio A M Mendes [Tue, 10 Feb 2026 06:13:16 +0000 (03:13 -0300)]
refactor: make pmm.c fully architecture-independent

Extract all Multiboot2 x86-specific code from src/mm/pmm.c into
src/arch/x86/pmm_boot.c as pmm_arch_init().

Design:
- pmm.h now exposes pmm_mark_region(), pmm_set_limits(), pmm_arch_init()
- pmm_init() calls pmm_arch_init() (arch-specific) which discovers
  memory and calls pmm_set_limits() + pmm_mark_region()
- pmm.c provides a weak default pmm_arch_init() for archs without one
- Kernel protection uses hal_mm_virt_to_phys() (no #if MIPS/x86)
- x86 pmm_boot.c handles Multiboot2 parsing, module protection,
  and boot info protection
- Zero #if guards remain in pmm.c

Passes: make, cppcheck, QEMU smoke test.