Flash 247 lines
// Shared syscall ID constants + ABI types — single source of truth for
// the user/kernel ABI boundary.
//
// These IDs index sys_call_table in src/sys.zig (kernel side) and are
// loaded into x8 by the syscall wrappers in user_space/kernel_tests.zig
// (user side). Keeping the numbers here lets both sides import the same
// names so a renumbering becomes a single-file change with compiler
// enforcement, instead of paired edits coordinated by comments.
//
// NR_SYSCALLS in arch/aarch64/asm_defs_common.inc must stay in lockstep with the
// highest ID +1 (it caps the dispatch range via `b.hs` in entry.S).
//
// Pure compile-time constants: no code is emitted, no `linksection`
// attribute is needed, the user_init.o blob is unaffected in size and
// layout.
// Slot 0 is a retired slot — see the retired-slots note below.
pub const SYS_FORK u64 = 1
pub const SYS_EXIT u64 = 2
pub const SYS_WAIT u64 = 3
// Public introspection ABI (stable).
pub const SYS_DUMP_FREE u64 = 4
// Slot 5 is a retired slot — see the retired-slots note below.
pub const SYS_KILL u64 = 6
// File-system ABI. SYS_OPEN_FILE and SYS_SEEK are symbolic
// constants so the dispatch-table writes in src/sys.zig become
// compiler-enforced (a renumber here propagates automatically). The
// former per-kind read/write/close file shims (slots 8, 9, 11) are
// retired — see the retired-slots note below; file I/O now goes
// through the unified (fd, buf, len) ABI at slots 32..35.
pub const SYS_OPEN_FILE u64 = 7
pub const SYS_SEEK u64 = 10
pub const SYS_BRK u64 = 12
pub const SYS_SBRK u64 = 13
// Slots 14..17 are reserved mm stubs (no-op handlers until v1.x).
pub const SYS_MMAP u64 = 14
pub const SYS_MUNMAP u64 = 15
pub const SYS_MLOCK u64 = 16
pub const SYS_MUNLOCK u64 = 17
// Slot 18 = SYS_PIPE; the other end-of-pipe ABI sits past the console
// reservation (slots 23..26) so the console can fill its slots
// without touching the pipe IDs. NR_SYSCALLS in arch/aarch64/asm_defs_common.inc
// must stay one past the highest slot.
pub const SYS_PIPE u64 = 18
// Slots 19..22 are reserved IPC stubs (no-op handlers until v1.x).
pub const SYS_SOCKET u64 = 19
pub const SYS_MSGGET u64 = 20
pub const SYS_SEMGET u64 = 21
pub const SYS_SHMGET u64 = 22
// Console ABI: slots 23..26. The open/read slots (23, 24) are
// retired — see the retired-slots note below; console I/O now goes
// through the unified (fd, buf, len) ABI at slots 32..35, and fd 0/1/2
// are pre-installed at PID-1 bring-up. The two remaining slots are
// inert stubs by contract:
// * SYS_SET_CONSOLE_MODE — toggles the kernel echo flag;
// see CONSOLE_MODE_ECHO below. Full termios later.
// * SYS_CLOSE_CONSOLE — inert (fd-table teardown not yet wired)
pub const SYS_SET_CONSOLE_MODE u64 = 25
pub const SYS_CLOSE_CONSOLE u64 = 26
// Console mode bits for SYS_SET_CONSOLE_MODE. ECHO on => the
// kernel echoes drained printable console bytes (cooked-style); off
// (default) keeps echo in userland readline. /bin/login sets ECHO for the
// username prompt and MASK (below) for the password prompt. A full termios
// flag set is future work.
pub const CONSOLE_MODE_ECHO u64 = 1
// MASK on => the kernel echoes a single '*' per drained printable byte
// instead of the byte itself (password masking). /bin/login sets this around
// the password prompt so typed characters are acknowledged without revealing
// the secret. Mutually exclusive with ECHO in practice; if both are set,
// MASK wins.
pub const CONSOLE_MODE_MASK u64 = 2
// Debug-only — not part of the stable ABI.
// Pushes one byte into the kernel RX ring as if it had arrived on
// the UART. Powers deterministic console-echo coverage on QEMU
// where there is no external input driver. Remove once a real
// host-input driver lands.
pub const SYS_CONSOLE_INJECT u64 = 30
// Path-resolved ELF loader. Streams PT_LOAD segments from an
// open VFS file and lays an argv block on the new user stack; the sole
// exec entry point since the legacy blob loader (slot 5) was retired.
// Entry contract is `x0 = argc`, `x1 = argv` (AAPCS64).
pub const SYS_EXECVE u64 = 31
// Unified fd-table ABI. Slots 32..35 dispatch by the fd's
// kind tag in the unified `fds` table (console / pipe / file) and are
// the sole entry point for all console / pipe / file I/O. The legacy
// per-kind shims they replaced were retired — see the retired-slots
// note below.
pub const SYS_READ u64 = 32
pub const SYS_WRITE u64 = 33
pub const SYS_CLOSE u64 = 34
pub const SYS_DUP2 u64 = 35
// Retired slots — legacy per-kind shims removed after the unified fd ABI
// (slots 31-35) replaced them: 0 (write_str), 5 (exec), 8 (readFile),
// 9 (writeFile), 11 (closeFile), 23 (openConsole), 24 (readConsole),
// 27 (pipe_read), 28 (pipe_write), 29 (pipe_close). The numbers stay
// reserved: the dispatch table routes them to a -1 stub, and they must
// never be reassigned to new syscalls.
// Working-directory ABI. Slot 36 — sys_chdir(path)
// normalises a path against the task's `cwd` (TaskStruct.cwd) and stores
// the result. Relative paths at the open/execve boundary are joined
// against `cwd` before vfs.resolve runs (still absolute-only). No real
// directory existence check — best-effort, deferred to sys_readdir.
pub const SYS_CHDIR u64 = 36
// Directory-enumeration ABI. Slot 37 —
// sys_readdir(path, index, *Dirent) is a stateless index walk: it fills
// the `index`-th entry of the directory at `path` and returns 0, or -1
// at end-of-directory / bad path / wild pointer. No fd cursor, no
// opendir handle — the POSIX handle shape is a future portable-
// userland revisit. initramfs synthesises directories from path
// prefixes; FAT32 renders 8.3 root entries (Pi-only).
pub const SYS_READDIR u64 = 37
// Kernel-log ABI. Slot 38 — sys_klog_read(buf, len) snapshots
// the most-recent min(len, retained) bytes of the kernel byte-ring
// (src/klog_ring.zig) into the caller's UVA, oldest-first, and returns
// the count (0 when the ring is empty, -1 on a wild buffer). Stateless
// and consume-free: every call sees the live log; /bin/dmesg sizes its
// buffer to KLOG_SIZE to capture the whole retained log in one call.
pub const SYS_KLOG_READ u64 = 38
// Process-credential ABI. Slots 39..44 — the identity layer for
// the login/auth flow. get* report the calling task's real / effective
// uid / gid (the four ids now carried in TaskStruct); set* mutate them
// under a root-gated policy: euid 0 sets any id (real + effective), a
// dropped process may only reset to an id it already holds, else -1
// (EPERM-lite). i64 return so the failure sentinel is representable.
// Inherited across fork, preserved across execve, so the privilege drop
// /bin/login performs survives the shell exec.
pub const SYS_GETUID u64 = 39
pub const SYS_GETEUID u64 = 40
pub const SYS_GETGID u64 = 41
pub const SYS_GETEGID u64 = 42
pub const SYS_SETUID u64 = 43
pub const SYS_SETGID u64 = 44
// Authentication ABI. Slot 45 — sys_authenticate(user_ptr,
// user_len, pass_ptr, pass_len) reads the active shadow database
// in-kernel (/mnt/shadow first, the initramfs /etc/shadow seed as
// fallback), runs PBKDF2-HMAC-SHA256 over the password with the stored
// salt + iteration count, and constant-time-compares to the stored
// verifier. Returns 0 on match, -1 otherwise. The KDF lives in the
// kernel; userland (/bin/login) sees only pass/fail, never a salt or
// hash.
pub const SYS_AUTHENTICATE u64 = 45
// Password-change ABI. Slot 46 — sys_passwd(user_ptr, user_len,
// old_ptr, old_len, new_ptr, new_len) rewrites `user`'s record in the
// writable FAT32 shadow (/mnt/shadow) with a fresh kernel-minted salt and
// a PBKDF2 re-hash of the new password. Authorization: root (euid 0) may
// reset any record without the old password; everyone else only their own
// record (uid -> name via /etc/passwd) and only with the correct old
// password (-EACCES otherwise). Returns 0 on success, -1 when there is no
// writable shadow (QEMU virt / fresh card), the user is absent, or the
// rewrite would change the record length. Six register args (x0..x5).
pub const SYS_PASSWD u64 = 46
// Machine-reset ABI. Slot 47 — sys_reboot() resets the board and does
// not return. The reset itself is board-specific (PSCI SYSTEM_RESET via
// SMC on QEMU virt; the BCM2711 watchdog full-reset on rpi4b) and lives
// in src/board/<board>/power.zig behind the board.zig facade. The kernel
// performs it because EL0 cannot issue the SMC or touch the power-manager
// MMIO. No privilege gate yet: any logged-in session may reboot.
pub const SYS_REBOOT u64 = 47
// Working-directory readback ABI. Slot 48 — sys_getcwd(buf, len)
// copies the calling task's NUL-terminated `cwd` (TaskStruct.cwd) into
// the user buffer, then returns the path length excluding the NUL, or -1
// on a wild buffer UVA or a `len` too small to hold the path plus its
// terminator. The readback half of the slot-36 chdir store — `cwd` is a
// plain TaskStruct field, so this allocates nothing. `pwd` is the sole
// consumer. Appended past SYS_REBOOT — see the NR_SYSCALLS note below.
pub const SYS_GETCWD u64 = 48
// Hardware-monitoring ABI. Slots 49..52 — four argument-free `() -> u64`
// reads that back the cpuinfo/sysinfo tools. SYS_MEMTOTAL (49) returns
// the post-reserve allocatable pool size in pages (the boot free-page
// baseline; a tool subtracts SYS_DUMP_FREE for "used"). SYS_UPTIME (50)
// returns seconds since boot from the architectural counter. SYS_CPU_TEMP
// (51) returns the SoC temperature in milli-degrees Celsius and SYS_CPU_FREQ
// (52) the ARM clock in Hz, both over the VideoCore mailbox; each reports
// 0 = unknown on a board without the firmware (virt), which the tools render
// as `n/a`. Appended past SYS_GETCWD — see the NR_SYSCALLS note below.
pub const SYS_MEMTOTAL u64 = 49
pub const SYS_UPTIME u64 = 50
pub const SYS_CPU_TEMP u64 = 51
pub const SYS_CPU_FREQ u64 = 52
// FAT32 metadata ABI: SYS_CREATE (53) makes a new empty file and returns a
// writable fd (creat); SYS_UNLINK (54) removes a file; SYS_RENAME (55)
// renames it within the same directory. Files only, /mnt only.
pub const SYS_CREATE u64 = 53
pub const SYS_UNLINK u64 = 54
pub const SYS_RENAME u64 = 55
// Highest slot + 1; equals the `#define NR_SYSCALLS` literal in
// arch/aarch64/asm_defs_common.inc. Adding a new SYS_* constant past
// the current top bumps this automatically; the comptime guard in
// src/sys.flash catches divergence from the asm-side literal at build
// time.
pub const NR_SYSCALLS usize = SYS_RENAME + 1
// Kernel-log ring capacity in bytes. Shared here because both
// the kernel ring (src/klog_ring.zig sizes `KlogRing` to it) and userland
// `dmesg` (sizes its read buffer to it) must agree — an ABI-visible
// constant, like Dirent. 16 KiB holds a full interactive boot log
// (firmware marker → `Reached target Shell`); a longer in-harness log wraps,
// keeping the most recent 16 KiB.
pub const KLOG_SIZE u64 = 16 * 1024
// Errno surface. Historically every syscall failure returned a
// bare -1; the VFS permission layer needs a distinguishable code so a
// denied open / write / execve reads as "permission denied" rather than
// a generic miss. Syscalls return the NEGATED value (-EACCES == -13).
// Only EACCES exists so far — it is the lone failure any syscall reports
// besides -1. The numeric value matches the conventional Unix errno so a
// future libc errno table needs no remapping.
pub const EACCES i32 = 13
// ---- Shared ABI types ----
//
// Types (not IDs) that cross the user/kernel boundary by pointer, so
// both sides must import one definition. The first is the directory
// entry filled by sys_readdir (slot 37).
// d_type values for Dirent. A flat two-value set: the cpio and FAT32
// backends only ever surface regular files and directories — no
// symlinks, devices, or FIFOs to enumerate.
pub const DT_REG u8 = 0 // regular file
pub const DT_DIR u8 = 1 // directory
// One directory entry. extern struct because it crosses the
// sys_readdir vtable boundary by pointer and is copy_to_user'd into the
// caller's UVA — the layout must be fixed and identical both sides.
// `name` is 32 bytes because initramfs basenames are full cpio names
// (e.g. `flibc_demo.elf`), not 8.3; FAT32 fills <= 12 rendered 8.3
// chars. A `size` field is deferred to the `ls -l` fsh-v2 work. _pad
// keeps the struct 8-byte aligned at 40 bytes.
pub const Dirent = extern struct {
name [32]u8 = [_]u8{0} ** 32, // NUL-terminated basename
d_type u8 = DT_REG,
_pad [7]u8 = [_]u8{0} ** 7,
}