# TypePHP freestanding OS experiment This is a long-running experimental x86_64 TypePHP operating system. It boots under QEMU and runs an ordinary TypePHP Nano program without linking hosted PHP, libc, or libstdc++. TypePHP implements the startup self-check, a custom Zend class, the prime-number demo, and filesystem services. Small C and assembly layers provide the machine bootstrap, current kernel services, and the temporary freestanding userspace programs. The userspace shell and commands are deliberately written in freestanding C until tpc can target this small userspace ABI. They already use standard `main()`, libc-shaped APIs, and a Linux-style process-entry stack. The architectural rules and staged plan are maintained in [ROADMAP.md](ROADMAP.md). ## Architecture The source tree is split by execution role: ```text boot/ Make-built startup assembly and linker scripts kernel/*.php TypePHP kernel implementation kernel/core/ Native kernel implementation compiled by tpc kernel/core/abi/ Freestanding C/POSIX and C++ compatibility boundary user/ Independently built Ring-3 programs and bootstrap libc ``` Additional TypePHP kernel files stay directly under `kernel/` until a concrete subsystem boundary justifies another directory. There is no kernel-specific tpc mode or reduced PHP Nano profile. The 64-bit payload is compiled with the normal command: ```shell ./bin/tpc.php --nano examples/typephp-os/project.yml ``` This composes the complete php-nano and PHPX source manifests. The TypePHP OS project owns the freestanding boundary under `kernel/core/abi`: implemented C/POSIX and C++ ABI functions live there, while APIs required for linking but not implemented by the kernel are exported as panic stubs. Consequently an unsupported operation fails immediately with its ABI symbol instead of silently returning fabricated data. Sockets are outside the current scope. On a fresh checkout, run `examples/typephp-os/tools/fetch-thirdparty.sh` before invoking tpc directly; `make payload` performs both steps automatically. Cross-project portability uses general feature switches only: - `PHP_NANO_NO_LIBC` asks the embedding host to provide Nano's clock, sleep, and entropy hooks; - `PHPX_NO_EXCEPTION` routes PHPX exception propagation to the host abort hook; - `PHPX_NO_RTTI` selects PHPX's non-RTTI checked cast policy; - `TYPEPHP_NO_MAIN` lets an embedding host provide the process/kernel entry. None of these switches refers to TypePHP OS or to a kernel build. Ordinary Nano and PHPX builds preserve their hosted defaults. The payload uses PHP's original Zend allocator, GC, strings, HashTables, objects, classes, exceptions, and built-in extension registration. Generated projects and built-ins are registered through their ordinary MINIT paths; the only excluded capability is dynamic PHP execution through ZendVM. ## Build and run Required host tools are TypePHP's PHP/Composer dependencies, GCC/G++, GNU binutils, GNU make, `dosfstools`, `mtools`, and `qemu-system-x86_64`. Third-party source is not committed. `make` invokes [`tools/fetch-thirdparty.sh`](tools/fetch-thirdparty.sh), which downloads fixed archives and verifies their SHA-256 checksums according to [`THIRDPARTY.md`](THIRDPARTY.md). From this directory, build and boot with: ```shell make make run ``` The build produces these useful files: - `build/kernel64.elf`: the 64-bit TypePHP + ordinary Nano payload produced by tpc; - `build/typephp-os.elf`: the final Multiboot kernel accepted by QEMU; - `build/typephp-os.img`: a persistent 32 MiB FAT16 disk image containing the independently built shell and command executables; - `build/sh.elf` and the command ELF files: independent ELF64 Ring-3 programs linked with the small userspace bootstrap libc rather than a hosted libc. - `build/tnhello.elf`: a full TypePHP + PHPX + PHP Nano Ring-3 executable generated by tpc from `user/nano/hello.php`. - `build/libtypephp-os.a`: the reusable userspace platform archive linked by every tpc-generated TypePHP program with `-ltypephp-os`. Run the automated serial-output smoke test with: ```shell make test ``` The Makefile compiles the startup sources under `boot/` directly. The 32-bit Multiboot bootstrap cannot participate in the 64-bit payload link; the 64-bit entry object is injected into tpc's final link through the generic `objects` setting. The Makefile also builds the deliberately small freestanding C userspace ELF files and installs them into the FAT16 image with `mcopy`. All ordinary kernel `.php`, `.c`, `.cc`, and `.S` files live under `kernel/`, remain in `project.yml`, and use tpc's generic `c-flags`, `cxx-flags`, and `asm-flags`. The user executables are not linked into the kernel payload. The architecture-changing bootstrap is combined during the final packaging link. After tpc emits `kernel64.elf`, the Makefile uses `objcopy` to turn the payload into a raw binary and then an ELF32 data object. GNU ld combines that object with the 32-bit bootstrap. The bootstrap is loaded through Multiboot v1, identity-maps the first GiB, enters x86_64 long mode, and transfers control to the payload linked at 2 MiB. To invoke QEMU manually: ```shell qemu-system-x86_64 -m 160M \ -kernel examples/typephp-os/build/typephp-os.elf \ -drive file=examples/typephp-os/build/typephp-os.img,format=raw,if=ide,index=0 \ -display none -serial stdio -monitor none -no-reboot -no-shutdown ``` Press `Ctrl+C` to leave headless QEMU. ## Current capabilities The bootstrap passes the Multiboot memory map to the 64-bit kernel. The physical layer reserves the complete kernel image and divides the selected usable region into two pools. Upstream `zend_alloc` obtains aligned 2 MiB chunks from one pool through the project-owned `posix_memalign()`; the other pool is a recyclable 4 KiB physical-page allocator used by page tables, ELF images, stacks, `brk()`, and `mmap()`. C++ global `new` and `delete`, including `std::vector` allocations, continue to use Zend MM. The QEMU smoke test currently verifies: - 64-bit TypePHP scalar and control-flow execution; - real Zend allocation, GC, string, array, object, class, and exception data; - PHPX `Variant`, `Str`, `Array`, custom classes, and a typed `std::vector`; - OpenLibm implementations of the ordinary double-precision math ABI; - RTC-derived UTC time exposed to userspace through `time()`; - Linux-compatible `uname(2)` shared by the `uname` command and PHP's `php_uname()` implementation; - ATA PIO sector I/O, a fixed 128-sector LRU read/write-through cache, and a TypePHP FAT16 implementation with DOS 8.3 files, nested traversal and mutation, including automatic directory-chain growth; - PHP's unchanged plain file stream and `php_stat()` paths, including `file_put_contents()`, `file_get_contents()`, `is_dir()`, `mkdir()`, and `scandir()`, forwarded through the POSIX ABI to TypePHP; - normal PHP output through multi-argument `echo` and `PHPWRITE`; - a TypePHP prime calculation for 0–100; - disk-backed ELF64 loading, Linux-style `argc/argv` startup, and libc-shaped userspace calls, including a complete PHP Nano program generated by tpc; - a separate x86-64 address space for the resident shell and every command, 4 KiB user mappings, a guarded 64 KiB stack, NX/WP enforcement, and ELF `RX`/`RW` segment permissions; - Linux-numbered `brk`, anonymous private `mmap`, `mprotect`, and `munmap` calls backed by recyclable physical pages; - Linux-compatible file metadata and persistence calls (`stat`, `lstat`, `fstat`, `newfstatat`, `access`, `faccessat`, `fsync`, `fdatasync`, `truncate`, and `ftruncate`), exercised from both C and PHP Nano; - Linux-compatible directory descriptors and `getdents64`, exposed to C and PHP Nano through `opendir`, `readdir`, `rewinddir`, and `closedir`; - the first `fcntl` subset (`F_GETFD`, `F_SETFD`, `F_GETFL`, `F_SETFL`), including tracked `FD_CLOEXEC`/`O_NONBLOCK` state and effective `O_APPEND`; - single-task PID/TID and root UID/GID queries plus `gettimeofday`, `clock_gettime`, `clock_getres`, and `exit_group`; - dynamic command discovery and safe rejection of malformed ELF files; - recovery from invalid opcodes, cross-address-space reads, and writes to read-only mappings without losing the resident shell; - user-mode file creation, reading, writing, seeking, closing, removal, and nested directory creation/removal through the TypePHP FAT16 implementation. The 64-bit payload currently occupies about 7 MiB. The first 40 MiB is kept away from both allocators for the kernel payload, bootstrap state, and early host arena. Userspace has a separate 0x40000000–0x50000000 virtual region. Entire Zend chunks are not yet returned to the physical-page pool. The current filesystem deliberately supports only DOS 8.3 names. Reading, directory enumeration, `stat`, executable loading, and file/directory mutation traverse nested FAT16 directory chains. Full directories grow by linking a new cluster. The current ATA cache is deliberately small, synchronous, and write-through; it is not yet a general virtual-filesystem page cache. Rename currently stays within one parent directory; cross-directory rename, replacement semantics, long filenames, timestamps, permissions, and a general block-device layer remain future work. Network sockets, dynamic module loading, `include`/`require`/`eval`, and PHP APIs that execute host commands remain unavailable. ## Single-task userspace After the TypePHP self-check, the kernel opens `/BIN/SH.ELF` from FAT16, validates and loads its `PT_LOAD` segments, installs a 64-bit TSS and an IDT gate, and enters Ring 3 with `iretq`. The resident shell and each transient command receive an independent CR3. Their 1–1.25 GiB virtual window is composed from recyclable 4 KiB pages; the shared identity-mapped kernel remains supervisor-only. The CPU has write protection and no-execute enabled, and the loader applies the final ELF `PF_W` and `PF_X` permissions after copying each segment. An `int 0x80` boundary currently provides synchronous `read`, `write`, `close`, `lseek`, `openat`, `exit`/`exit_group`, `getcwd`, `chdir`, `mkdir`, `rmdir`, `unlink`, file stat/access/persistence/truncation families, fixed identity queries, `time`, `gettimeofday`, `clock_gettime`, `clock_getres`, `brk`, anonymous private `mmap`, `mprotect`, `munmap`, `getdents64`, the initial `fcntl` flag operations, and private spawn and same-directory rename operations. The former private directory-list syscall has been removed; `ls` and PHP Nano use the standard directory ABI. Standard input and output are backed by QEMU's COM1 serial console. Syscall numbers are shared by the kernel and userspace through `typephp_os_syscall.h`. Userspace programs now expose standard C `main(argc, argv)` functions. A shared `crt0.S` consumes a Linux-style initial stack containing `argc`, `argv`, an empty environment, and a terminating auxiliary-vector entry. The normative userspace compatibility rules are recorded in [`user/README.md`](user/README.md): the long-term target is GCC/glibc-compatible userspace, and Linux syscall numbers are reserved for genuinely compatible semantics. The shell synchronously executes separate command ELF files from FAT16. Entering `name` requests `/BIN/.ELF`; the kernel validates the ELF header, program table, file bounds, target address range, and entry point before loading it. There is no compiled-in command whitelist, so a compatible command can be installed in the disk image without relinking the kernel. Commands may reuse the same virtual addresses, but never the shell's mappings: every launch creates and later destroys an independent address space and stack. A page-count invariant detects leaked command pages on both normal exit and faults. Only one user context runs at a time: while a command is active, the kernel keeps the shell register frame and restores it when the command calls `exit`. Because this is a deliberately single-task model, the working directory is a session-global property; a successful `cd` therefore remains visible after control returns to the shell. `fork`, `clone`, `execve`, `wait`, pipes, threads, scheduling signals, and job control are intentional non-goals. The private synchronous shell launch syscall must not be exposed as POSIX `execve()` semantics. `TNHELLO.ELF` demonstrates the intended TypePHP userspace path. It is built by the ordinary tpc Nano pipeline, links the complete php-nano and PHPX manifests, links the reusable `libtypephp-os.a` platform archive, and uses the same ELF loader and Ring-3 syscall boundary as the C commands. Its startup code converts the Linux-style initial stack into `argc` and `argv`, and `php_uname()` calls the standard extension's unchanged `info.c` implementation through a userspace `uname()` ABI that reports TypePHP-OS. Run it from the shell with: ```text tnhello alpha beta ``` The current user ABI intentionally implements only the calls needed by the demonstration. Missing libc/POSIX operations are linkable panic stubs and terminate the command with an explicit `unsupported TypePHP-OS user ABI` message; they do not silently emulate success. Available commands are: ```text ls date uname uname -a pwd echo Hello TypePHP cat HELLO.TXT write NOTE.TXT Hello from Ring 3 cat NOTE.TXT touch EMPTY.TXT mkdir TMP rm NOTE.TXT rmdir TMP mv OLD.TXT NEW.TXT systest cd DOCS pwd ls cd .. ``` The development-only `fault`, `vmfault`, and `wrfault` commands exercise an invalid opcode, an attempted read from the shell's virtual address, and an attempted write to a read-only `mmap()` page. The kernel reports each Ring-3 exception, destroys the faulty address space, and restores the shell. The `memtest` command exercises `sbrk()`, anonymous mapping, protection changes, and unmapping. `systest` covers file metadata, persistence/truncation, fixed single-task identities, and wall/monotonic clock ABIs. User exceptions for divide errors, breakpoints, bounds, invalid opcodes, invalid TSS/segments, stack faults, general-protection faults, and page faults have IDT entries. A fault raised in Ring 0 still causes an immediate kernel panic. Executables now live in the conventional `/BIN` directory and both `ls /BIN` and `cd /BIN` use TypePHP's FAT16 cluster-chain traversal. The same path resolution is used by `cat`, `write`, `touch`, `mkdir`, `rm`, and `rmdir`, so nested directory trees are mutable from userspace.