From 6340433fbe4461110fd37ced177fc0e34c3173b1 Mon Sep 17 00:00:00 2001 From: tianfenghan Date: Fri, 11 Sep 2026 17:26:16 +0800 Subject: [PATCH] feat(kernel): implement syscall-based ioctl support and improve ATA caching - Replace int 0x80 with native x86-64 SYSCALL instruction for faster system calls - Add syscall number 16 for ioctl support with Linux-compatible ABI - Implement TIOCGWINSZ and TIOCNOTTY terminal ioctls for console size detection - Add ENOTTY error code definition for inappropriate ioctl operations - Increase ATA cache capacity from 128 to 16384 entries for better performance - Change ATA cache from LRU eviction to direct mapping using bitwise AND - Remove least recently used tracking from ATA cache entries - --- examples/typephp-os/Makefile | 2 +- examples/typephp-os/README.md | 6 +- examples/typephp-os/ROADMAP.md | 13 ++- examples/typephp-os/THIRDPARTY.md | 33 ++----- examples/typephp-os/kernel/core/abi/error.c | 1 + .../kernel/core/abi/typephp_os_syscall.h | 1 + examples/typephp-os/kernel/core/ata.c | 30 ++----- .../typephp-os/kernel/core/process-entry.S | 16 +++- examples/typephp-os/kernel/core/process.c | 87 +++++++++++++++++-- examples/typephp-os/tools/fetch-mlibc.sh | 33 ------- examples/typephp-os/tools/fetch-thirdparty.sh | 1 - .../typephp-os/tools/mlibc-demo-cross.ini | 14 --- .../typephp-os/tools/mlibc-headers-smoke.c | 26 ------ examples/typephp-os/tools/test-thirdparty.sh | 30 +------ examples/typephp-os/user/cmd/systest.c | 9 ++ examples/typephp-os/user/include/errno.h | 1 + examples/typephp-os/user/include/sys/ioctl.h | 18 ++++ .../typephp-os/user/include/sys/syscall.h | 1 + examples/typephp-os/user/runtime/README.md | 5 +- examples/typephp-os/user/runtime/ioctl.c | 18 ++++ examples/typephp-os/user/runtime/syscall.S | 4 +- 21 files changed, 173 insertions(+), 176 deletions(-) delete mode 100755 examples/typephp-os/tools/fetch-mlibc.sh delete mode 100644 examples/typephp-os/tools/mlibc-demo-cross.ini delete mode 100644 examples/typephp-os/tools/mlibc-headers-smoke.c create mode 100644 examples/typephp-os/user/include/sys/ioctl.h create mode 100644 examples/typephp-os/user/runtime/ioctl.c diff --git a/examples/typephp-os/Makefile b/examples/typephp-os/Makefile index 69735365..73416368 100644 --- a/examples/typephp-os/Makefile +++ b/examples/typephp-os/Makefile @@ -22,7 +22,7 @@ OPENLIBM_SOURCES := $(shell sed -n 's|^\(.*\.c\)$$|thirdparty/openlibm/\1|p' too COMPILER_RT_SOURCES := $(shell awk '$$2 ~ /\.c$$/ { print "thirdparty/compiler-rt/" $$2 }' tools/compiler-rt-builtins-files.sha256) COMPILER_RT_OBJECTS := $(patsubst thirdparty/compiler-rt/lib/builtins/%.c,$(COMPILER_RT_OBJECT_DIR)/%.o,$(COMPILER_RT_SOURCES)) TYPEPHP_OS_LIBRARY_C_SOURCES := user/runtime/posix.c user/runtime/dirent.c \ - user/runtime/fcntl.c kernel/core/abi/error.c kernel/core/abi/string.c \ + user/runtime/fcntl.c user/runtime/ioctl.c kernel/core/abi/error.c kernel/core/abi/string.c \ kernel/core/abi/libc.c $(OPENLIBM_SOURCES) TYPEPHP_OS_LIBRARY_CXX_SOURCES := user/runtime/host.cc kernel/core/abi/cxx.cpp TYPEPHP_OS_LIBRARY_ASM_SOURCES := user/runtime/crt0.S user/runtime/syscall.S \ diff --git a/examples/typephp-os/README.md b/examples/typephp-os/README.md index c46ee1b7..dcb251e2 100644 --- a/examples/typephp-os/README.md +++ b/examples/typephp-os/README.md @@ -99,9 +99,9 @@ Run the automated serial-output smoke test with: make test ``` -To configure the mlibc header porting probe and build the restricted hosted -Toybox applet set, install Meson 1.3 or newer and run `make thirdparty-smoke`. -This is an integration audit, not part of the boot image. +To build and audit the restricted hosted Toybox applet set, run +`make thirdparty-smoke`. This is an integration audit, not part of the boot +image. 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 diff --git a/examples/typephp-os/ROADMAP.md b/examples/typephp-os/ROADMAP.md index c6431049..ea73699f 100644 --- a/examples/typephp-os/ROADMAP.md +++ b/examples/typephp-os/ROADMAP.md @@ -28,10 +28,9 @@ behavior. `wait`, pipes, threads, scheduling signals, and job control will not be implemented. The shell may only launch one synchronous foreground ELF through the private TypePHP-OS service. -- `ioctl` and `termios` are outside the current scope. Third-party programs - must not drive implementation of these low-priority terminal interfaces; - they can be adopted only after their selected build no longer requires the - interfaces or another generally useful subsystem justifies them. +- Keep `ioctl` request support deliberately narrow. The fixed serial/VGA + console provides `TIOCGWINSZ`; a complete termios subsystem remains outside + the current single-task shell requirements. ## Milestones @@ -90,10 +89,8 @@ behavior. C commands and multiple tpc projects link the same platform archive. Unavailable ABI functions remain explicit panic stubs. Selected upstream LLVM compiler-rt builtins now provide 128-bit integer - helper symbols. mlibc 7.0 and a five-applet Toybox build are pinned as - porting probes; neither replaces the working userspace runtime yet because - mlibc exceeds the C++17 baseline and Toybox's common runtime still imports - `ioctl`. + helper symbols. A five-applet Toybox build is pinned as a porting probe but + does not yet replace the working userspace commands. Single-task identity calls report PID/TID 1 for the resident shell, 2 for its synchronous foreground command, and root UID/GID. Linux-compatible wall/monotonic clock structures are exposed at the current one-second RTC diff --git a/examples/typephp-os/THIRDPARTY.md b/examples/typephp-os/THIRDPARTY.md index d7b73291..bbb9098a 100644 --- a/examples/typephp-os/THIRDPARTY.md +++ b/examples/typephp-os/THIRDPARTY.md @@ -28,9 +28,8 @@ Run the upstream integration probes separately: make thirdparty-smoke ``` -This target requires Meson 1.3 or newer for mlibc. It configures a headers-only -mlibc sysroot and builds a deliberately small hosted Toybox. Neither candidate -is installed into the TypePHP-OS disk image by this probe. +This target builds a deliberately small hosted Toybox and audits its undefined +ABI. It is not installed into the TypePHP-OS disk image by this probe. ## OpenLibm @@ -58,27 +57,6 @@ and TypePHP Nano userspace object set. The `builtins.elf` smoke command forces signed and unsigned 128-bit division so the archive is tested as a real linker dependency instead of merely being compiled. -## mlibc - -- Upstream: -- Version: `v7.0.0` -- Commit: `7c2a178142625cc9852e59a1a090468c61a62d3b` -- Archive SHA-256: `56d62366a6a15bc3fd8741bebcb311ca543553f7b734b1e780aeae33691bd621` - -The complete release tree is retained as a libc porting reference. The smoke -target configures mlibc's official Meson build in headers-only/demo-sysdeps -mode and compiles a TypePHP-OS API header probe. mlibc 7.0's implementation -build requires C++23 and GCC 13 or newer, while TypePHP-OS keeps C++17 as its -runtime baseline. Its implementation therefore does not replace the current -small libc yet; doing so also requires a real TypePHP-OS sysdeps port. - -The mlibc 6 series does not have one uniform compiler baseline. Releases 6.0 -and 6.1 request C++20 and do not contain the GCC 13 gate added later, so they -are more practical with an older compiler. Releases 6.2 and 6.3 already -request C++23. None of them satisfies TypePHP-OS's C++17 baseline unchanged; -using 6.0/6.1 would therefore be a separate port/toolchain choice rather than -a transparent downgrade of the pinned reference. - ## Toybox - Upstream: @@ -93,7 +71,6 @@ host ABI is written to `build/thirdparty-smoke/toybox-undefined-symbols.txt`. `ioctl` is a system-call API used by Toybox's shared C support library, not a Toybox command. None of those five applet source files calls it directly, but the common terminal/daemon helpers still leave an `ioctl` reference in the -hosted binary. Toybox is therefore intentionally not linked into TypePHP-OS. -The project will not implement `ioctl` or `termios` merely to satisfy it; -adoption requires pruning the unused common helpers or completing a different -compatible libc/runtime path. +hosted binary. TypePHP-OS now exposes the Linux-numbered syscall and implements +`TIOCGWINSZ` for its fixed console. Toybox still needs a freestanding build and +ABI audit before it can replace any existing command. diff --git a/examples/typephp-os/kernel/core/abi/error.c b/examples/typephp-os/kernel/core/abi/error.c index 7afc1f6e..27aa045b 100644 --- a/examples/typephp-os/kernel/core/abi/error.c +++ b/examples/typephp-os/kernel/core/abi/error.c @@ -26,6 +26,7 @@ char *strerror(int error) case ENOTDIR: return "Not a directory"; case EISDIR: return "Is a directory"; case EINVAL: return "Invalid argument"; + case ENOTTY: return "Inappropriate ioctl for device"; case EMFILE: return "Too many open files"; case EROFS: return "Read-only file system"; case ERANGE: return "Numerical result out of range"; diff --git a/examples/typephp-os/kernel/core/abi/typephp_os_syscall.h b/examples/typephp-os/kernel/core/abi/typephp_os_syscall.h index 628cef87..630701f1 100644 --- a/examples/typephp-os/kernel/core/abi/typephp_os_syscall.h +++ b/examples/typephp-os/kernel/core/abi/typephp_os_syscall.h @@ -16,6 +16,7 @@ enum typephp_os_syscall_number { TYPEPHP_SYS_MPROTECT = 10, TYPEPHP_SYS_MUNMAP = 11, TYPEPHP_SYS_BRK = 12, + TYPEPHP_SYS_IOCTL = 16, TYPEPHP_SYS_ACCESS = 21, TYPEPHP_SYS_GETPID = 39, TYPEPHP_SYS_EXIT = 60, diff --git a/examples/typephp-os/kernel/core/ata.c b/examples/typephp-os/kernel/core/ata.c index 597458c1..86175fbf 100644 --- a/examples/typephp-os/kernel/core/ata.c +++ b/examples/typephp-os/kernel/core/ata.c @@ -33,19 +33,20 @@ enum { ATA_COMMAND_CACHE_FLUSH = 0xe7, ATA_SECTOR_SIZE = 512, ATA_TIMEOUT = 10000000, - ATA_CACHE_CAPACITY = 128, + /* TNHELLO.ELF is currently about 5 MiB. A power-of-two 8 MiB cache keeps + * one complete image resident so a second launch avoids ATA PIO, while a + * direct mapping keeps every sector lookup O(1). */ + ATA_CACHE_CAPACITY = 16384, PCI_CONFIG_ADDRESS = 0xcf8, PCI_CONFIG_DATA = 0xcfc, }; static int ata_initialized; -static uint64_t ata_cache_clock; static uint64_t ata_cache_hit_count; static uint64_t ata_cache_miss_count; typedef struct { uint32_t lba; - uint64_t last_used; int valid; unsigned char data[ATA_SECTOR_SIZE]; } ata_cache_entry; @@ -206,23 +207,9 @@ static int ata_write_sector_raw(uint32_t lba, const unsigned char *data) static ata_cache_entry *ata_cache_slot(uint32_t lba, int *found) { - ata_cache_entry *oldest = &ata_cache[0]; - for (size_t index = 0; index < ATA_CACHE_CAPACITY; ++index) { - ata_cache_entry *entry = &ata_cache[index]; - if (entry->valid && entry->lba == lba) { - *found = 1; - return entry; - } - if (!entry->valid) { - *found = 0; - return entry; - } - if (entry->last_used < oldest->last_used) { - oldest = entry; - } - } - *found = 0; - return oldest; + ata_cache_entry *entry = &ata_cache[lba & (ATA_CACHE_CAPACITY - 1u)]; + *found = entry->valid && entry->lba == lba; + return entry; } int typephp_os_disk_read_sector(uint32_t lba, unsigned char *data) @@ -235,7 +222,6 @@ int typephp_os_disk_read_sector(uint32_t lba, unsigned char *data) entry = ata_cache_slot(lba, &found); if (found) { ++ata_cache_hit_count; - entry->last_used = ++ata_cache_clock; memcpy(data, entry->data, ATA_SECTOR_SIZE); return 1; } @@ -245,7 +231,6 @@ int typephp_os_disk_read_sector(uint32_t lba, unsigned char *data) } entry->valid = 1; entry->lba = lba; - entry->last_used = ++ata_cache_clock; memcpy(entry->data, data, ATA_SECTOR_SIZE); return 1; } @@ -261,7 +246,6 @@ int typephp_os_disk_write_sector(uint32_t lba, const unsigned char *data) (void) found; entry->valid = 1; entry->lba = lba; - entry->last_used = ++ata_cache_clock; memcpy(entry->data, data, ATA_SECTOR_SIZE); return 1; } diff --git a/examples/typephp-os/kernel/core/process-entry.S b/examples/typephp-os/kernel/core/process-entry.S index cfa566bf..9f1abd46 100644 --- a/examples/typephp-os/kernel/core/process-entry.S +++ b/examples/typephp-os/kernel/core/process-entry.S @@ -4,7 +4,21 @@ .global typephp_os_syscall_entry .type typephp_os_syscall_entry, @function .extern typephp_os_syscall_dispatch +.extern typephp_os_syscall_stack +.extern typephp_os_syscall_user_rsp typephp_os_syscall_entry: + # SYSCALL leaves RSP unchanged, stores the user RIP in RCX and stores the + # user RFLAGS in R11. Save the untrusted user stack pointer before moving + # to the kernel-owned syscall stack, then construct the same IRET frame + # used by exceptions and the former interrupt-gate entry. + mov %rsp, typephp_os_syscall_user_rsp(%rip) + lea typephp_os_syscall_stack+65536(%rip), %rsp + pushq $0x1b + pushq typephp_os_syscall_user_rsp(%rip) + push %r11 + pushq $0x23 + push %rcx + # Save every general-purpose register. The saved RAX slot is overwritten # with the syscall result before the frame is restored. push %rbx @@ -120,7 +134,7 @@ typephp_os_exception_common: .type typephp_os_enter_user, @function typephp_os_enter_user: # RDI = ELF entry, RSI = top of the user stack. Interrupts remain masked; - # int 0x80 is synchronous and the first process manager has no scheduler. + # SYSCALL is synchronous and the first process manager has no scheduler. mov $0x1b, %ax mov %ax, %ds mov %ax, %es diff --git a/examples/typephp-os/kernel/core/process.c b/examples/typephp-os/kernel/core/process.c index 57bbaf59..a58a6cc6 100644 --- a/examples/typephp-os/kernel/core/process.c +++ b/examples/typephp-os/kernel/core/process.c @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | TypePHP OS | +----------------------------------------------------------------------+ - | Single foreground process, ELF64 loader and int 0x80 syscall core. | + | Single foreground process, ELF64 loader and x86-64 syscall core. | | SPDX-License-Identifier: BSD-3-Clause | +----------------------------------------------------------------------+ */ @@ -34,7 +34,6 @@ enum { USER_DATA_SELECTOR = 0x1b, KERNEL_CODE_SELECTOR = 0x08, TSS_SELECTOR = 0x28, - IDT_SYSCALL = 0x80, MAX_USER_ARGUMENTS = 8, USER_PATH_MAX = 128, UTSNAME_LENGTH = 65, @@ -59,6 +58,13 @@ typedef struct { long microseconds; } user_timeval; +typedef struct { + uint16_t rows; + uint16_t columns; + uint16_t pixel_width; + uint16_t pixel_height; +} user_winsize; + enum { ELF_PT_LOAD = 1, ELF_PF_X = 1, @@ -215,9 +221,31 @@ extern uint64_t physical_page_available(void); static uint64_t gdt[7] __attribute__((aligned(16))); static idt_gate idt[256] __attribute__((aligned(16))); static task_state_segment tss; -static unsigned char syscall_stack[64u * 1024u] __attribute__((aligned(16))); +unsigned char typephp_os_syscall_stack[64u * 1024u] __attribute__((aligned(16))); +uint64_t typephp_os_syscall_user_rsp; static process_state foreground_process = {.pid = 1, .cwd = "/"}; +enum { + IA32_EFER = 0xc0000080u, + IA32_STAR = 0xc0000081u, + IA32_LSTAR = 0xc0000082u, + IA32_FMASK = 0xc0000084u, +}; + +static uint64_t read_msr(uint32_t index) +{ + uint32_t low; + uint32_t high; + __asm__ volatile("rdmsr" : "=a"(low), "=d"(high) : "c"(index)); + return ((uint64_t) high << 32u) | low; +} + +static void write_msr(uint32_t index, uint64_t value) +{ + __asm__ volatile("wrmsr" : : "c"(index), "a"((uint32_t) value), + "d"((uint32_t) (value >> 32u)) : "memory"); +} + static void panic(const char *message) { typephp_os_write("Process panic: ", sizeof("Process panic: ") - 1); @@ -287,7 +315,8 @@ static void install_descriptor_tables(void) gdt[3] = UINT64_C(0x00cff2000000ffff); gdt[4] = UINT64_C(0x00affa000000ffff); memset(&tss, 0, sizeof(tss)); - tss.rsp0 = (uint64_t) (uintptr_t) (syscall_stack + sizeof(syscall_stack)); + tss.rsp0 = (uint64_t) (uintptr_t) + (typephp_os_syscall_stack + sizeof(typephp_os_syscall_stack)); tss.iomap_base = sizeof(tss); install_tss_descriptor((uint64_t) (uintptr_t) &tss, sizeof(tss) - 1u); @@ -314,10 +343,20 @@ static void install_descriptor_tables(void) install_idt_gate(12, typephp_os_exception_12, 0x8e); install_idt_gate(13, typephp_os_exception_13, 0x8e); install_idt_gate(14, typephp_os_exception_14, 0x8e); - install_idt_gate(IDT_SYSCALL, typephp_os_syscall_entry, 0xee); pointer.limit = sizeof(idt) - 1u; pointer.base = (uint64_t) (uintptr_t) idt; __asm__ volatile("lidt %0" : : "m"(pointer) : "memory"); + + /* STAR[47:32] selects kernel CS 0x08 and its following SS 0x10. + * SYSRET's selector arithmetic starts at 0x10, yielding user SS 0x1b + * and user CS 0x23. This kernel returns with IRETQ so that spawn/exit can + * replace the complete user context, but the entry ABI remains the native + * Linux x86-64 SYSCALL register convention. */ + write_msr(IA32_STAR, + (UINT64_C(0x10) << 48u) | ((uint64_t) KERNEL_CODE_SELECTOR << 32u)); + write_msr(IA32_LSTAR, (uint64_t) (uintptr_t) typephp_os_syscall_entry); + write_msr(IA32_FMASK, UINT64_C(0x700)); /* TF, IF and DF */ + write_msr(IA32_EFER, read_msr(IA32_EFER) | UINT64_C(1)); } static int read_exact(int fd, void *buffer, size_t size) @@ -402,6 +441,13 @@ static int load_user_elf_file( if (segment.type != ELF_PT_LOAD) { continue; } + /* GNU ld can retain an empty PT_LOAD from the linker script when a + * tiny static executable has no data or bss sections. It maps no + * bytes and must not be rejected merely because its placeholder + * virtual address is zero. */ + if (segment.filesz == 0 && segment.memsz == 0) { + continue; + } if (segment.filesz > segment.memsz || !range_inside(segment.offset, segment.filesz, 0, image_size) || !range_inside(segment.vaddr, segment.memsz, region_begin, region_end)) { @@ -770,6 +816,34 @@ static long syscall_uname(user_utsname *result) return 0; } +static long syscall_ioctl(int fd, unsigned long request, void *argument) +{ + enum { + LINUX_TIOCGWINSZ = 0x5413, + LINUX_TIOCNOTTY = 0x5422, + }; + + if (fd < STDIN_FILENO || fd > STDERR_FILENO) { + return -ENOTTY; + } + switch (request) { + case LINUX_TIOCGWINSZ: { + user_winsize *window = (user_winsize *) argument; + if (!user_buffer(window, sizeof(*window))) { + return -EFAULT; + } + window->rows = 25; + window->columns = 80; + window->pixel_width = 0; + window->pixel_height = 0; + return 0; + } + case LINUX_TIOCNOTTY: + default: + return -ENOTTY; + } +} + static unsigned int vm_protection(int protection) { unsigned int flags = 0; @@ -1153,6 +1227,9 @@ long typephp_os_syscall_dispatch(syscall_frame *frame) return syscall_munmap(frame->rdi, frame->rsi); case TYPEPHP_SYS_BRK: return syscall_brk(frame->rdi); + case TYPEPHP_SYS_IOCTL: + return syscall_ioctl((int) frame->rdi, frame->rsi, + (void *) frame->rdx); case TYPEPHP_SYS_ACCESS: return syscall_access_path(AT_FDCWD, (const char *) frame->rdi, (int) frame->rsi, 0); diff --git a/examples/typephp-os/tools/fetch-mlibc.sh b/examples/typephp-os/tools/fetch-mlibc.sh deleted file mode 100755 index 81a6dfd1..00000000 --- a/examples/typephp-os/tools/fetch-mlibc.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env sh - -set -eu - -project_dir=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd) -version=7.0.0 -commit=7c2a178142625cc9852e59a1a090468c61a62d3b -archive_sha256=56d62366a6a15bc3fd8741bebcb311ca543553f7b734b1e780aeae33691bd621 -target="${project_dir}/thirdparty/mlibc" -marker="${target}/.typephp-source-${version}" - -if [ -f "${marker}" ] && [ -f "${target}/meson.build" ]; then - exit 0 -fi - -temporary=$(mktemp -d "${TMPDIR:-/tmp}/typephp-os-mlibc.XXXXXX") -trap 'rm -rf "${temporary}"' EXIT HUP INT TERM -archive="${temporary}/mlibc.tar.gz" -staging="${temporary}/installed" - -echo "Downloading mlibc v${version} (${commit})" -curl -L --fail --retry 3 --silent --show-error -o "${archive}" \ - "https://github.com/managarm/mlibc/archive/refs/tags/v${version}.tar.gz" -printf '%s %s\n' "${archive_sha256}" "${archive}" | sha256sum -c - -mkdir -p "${staging}" -tar -xzf "${archive}" -C "${staging}" --strip-components=1 -test -f "${staging}/LICENSE" - -mkdir -p "${project_dir}/thirdparty" -rm -rf "${target}" -mv "${staging}" "${target}" -printf '%s\n' "${commit}" > "${marker}" -echo "Installed mlibc v${version} in ${target}" diff --git a/examples/typephp-os/tools/fetch-thirdparty.sh b/examples/typephp-os/tools/fetch-thirdparty.sh index e5b466bd..09b32814 100755 --- a/examples/typephp-os/tools/fetch-thirdparty.sh +++ b/examples/typephp-os/tools/fetch-thirdparty.sh @@ -6,5 +6,4 @@ tools_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) "${tools_dir}/fetch-openlibm.sh" "${tools_dir}/fetch-compiler-rt-builtins.sh" -"${tools_dir}/fetch-mlibc.sh" "${tools_dir}/fetch-toybox.sh" diff --git a/examples/typephp-os/tools/mlibc-demo-cross.ini b/examples/typephp-os/tools/mlibc-demo-cross.ini deleted file mode 100644 index 49a20260..00000000 --- a/examples/typephp-os/tools/mlibc-demo-cross.ini +++ /dev/null @@ -1,14 +0,0 @@ -[binaries] -c = 'gcc' -cpp = 'g++' -ar = 'ar' -strip = 'strip' - -[host_machine] -system = 'demo' -cpu_family = 'x86_64' -cpu = 'x86_64' -endian = 'little' - -[properties] -needs_exe_wrapper = true diff --git a/examples/typephp-os/tools/mlibc-headers-smoke.c b/examples/typephp-os/tools/mlibc-headers-smoke.c deleted file mode 100644 index 16248be9..00000000 --- a/examples/typephp-os/tools/mlibc-headers-smoke.c +++ /dev/null @@ -1,26 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -_Static_assert(sizeof(uintptr_t) == 8, "TypePHP-OS requires a 64-bit mlibc ABI"); - -static void typecheck(void) -{ - struct dirent entry; - struct stat status; - struct timespec timestamp; - struct utsname identity; - - (void) entry; - (void) status; - (void) timestamp; - (void) identity; -} diff --git a/examples/typephp-os/tools/test-thirdparty.sh b/examples/typephp-os/tools/test-thirdparty.sh index f237bebb..7610cef2 100755 --- a/examples/typephp-os/tools/test-thirdparty.sh +++ b/examples/typephp-os/tools/test-thirdparty.sh @@ -4,7 +4,6 @@ set -euo pipefail project_dir=$(cd -- "$(dirname -- "$0")/.." && pwd) build_dir="${project_dir}/build/thirdparty-smoke" -meson_command=${MESON:-meson} if [[ ! -f "${project_dir}/build/libcompiler-rt-builtins.a" ]]; then echo "compiler-rt archive is missing; run make compiler-rt-builtins first" >&2 @@ -19,31 +18,6 @@ for symbol in __udivti3 __udivmodti4 __divti3 __modti3 __multi3; do done echo "compiler-rt builtins archive: OK" -if ! command -v "${meson_command}" >/dev/null 2>&1; then - echo "Meson 1.3 or newer is required for the mlibc headers probe" >&2 - exit 1 -fi - -mlibc_build="${build_dir}/mlibc-build" -mlibc_sysroot="${build_dir}/mlibc-sysroot" -rm -rf "${mlibc_build}" "${mlibc_sysroot}" -"${meson_command}" setup \ - "${mlibc_build}" "${project_dir}/thirdparty/mlibc" \ - --cross-file "${project_dir}/tools/mlibc-demo-cross.ini" \ - --prefix=/usr \ - -Dheaders_only=true \ - -Dposix_option=enabled \ - -Dlinux_option=disabled \ - -Dglibc_option=disabled \ - -Dbsd_option=disabled -DESTDIR="${mlibc_sysroot}" "${meson_command}" install --quiet -C "${mlibc_build}" -compiler_headers=$(gcc -print-file-name=include) -gcc -std=c11 -ffreestanding -fsyntax-only -nostdinc \ - -isystem "${mlibc_sysroot}/usr/include" \ - -isystem "${compiler_headers}" \ - "${project_dir}/tools/mlibc-headers-smoke.c" -echo "mlibc public-header ABI probe: OK" - toybox_source="${project_dir}/thirdparty/toybox" toybox_config="${build_dir}/toybox.config" mkdir -p "${build_dir}" @@ -67,8 +41,8 @@ if grep -Eq '^(fork|vfork|execv|execve|execvp|posix_spawn)$' "${toybox_symbols}" fi echo "Toybox selected applets build: OK" if grep -qx ioctl "${toybox_symbols}"; then - echo "Toybox install: deferred (the common runtime still references unsupported ioctl)" + echo "Toybox ABI audit: common runtime imports the supported ioctl entry point" else - echo "Toybox install: ABI audit no longer reports ioctl; reassess target linking" + echo "Toybox ABI audit: common runtime does not import ioctl" fi echo "Toybox ABI report: ${toybox_symbols}" diff --git a/examples/typephp-os/user/cmd/systest.c b/examples/typephp-os/user/cmd/systest.c index 9b4cb418..b5e1d7de 100644 --- a/examples/typephp-os/user/cmd/systest.c +++ b/examples/typephp-os/user/cmd/systest.c @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include #include #include @@ -38,6 +40,7 @@ int main(int argc, char **argv) struct timespec realtime; struct timespec monotonic; struct timespec resolution; + struct winsize window; DIR *directory; struct dirent *entry; int found_hello = 0; @@ -100,6 +103,12 @@ int main(int argc, char **argv) || getuid() != 0 || geteuid() != 0 || getgid() != 0 || getegid() != 0) { return fail("single-task identity"); } + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &window) != 0 + || window.ws_row != 25 || window.ws_col != 80 + || window.ws_xpixel != 0 || window.ws_ypixel != 0 + || ioctl(STDOUT_FILENO, TIOCNOTTY) != -1 || errno != ENOTTY) { + return fail("ioctl console ABI"); + } if (gettimeofday(&wall, NULL) != 0 || wall.tv_sec <= 0 || clock_gettime(CLOCK_REALTIME, &realtime) != 0 || clock_gettime(CLOCK_MONOTONIC, &monotonic) != 0 diff --git a/examples/typephp-os/user/include/errno.h b/examples/typephp-os/user/include/errno.h index 7384d90a..d816f0d0 100644 --- a/examples/typephp-os/user/include/errno.h +++ b/examples/typephp-os/user/include/errno.h @@ -16,6 +16,7 @@ int *__errno_location(void); #define EISDIR 21 #define EINVAL 22 #define EMFILE 24 +#define ENOTTY 25 #define EROFS 30 #define ERANGE 34 #define ENAMETOOLONG 36 diff --git a/examples/typephp-os/user/include/sys/ioctl.h b/examples/typephp-os/user/include/sys/ioctl.h new file mode 100644 index 00000000..3908eb23 --- /dev/null +++ b/examples/typephp-os/user/include/sys/ioctl.h @@ -0,0 +1,18 @@ +#ifndef TYPEPHP_OS_USER_SYS_IOCTL_H +#define TYPEPHP_OS_USER_SYS_IOCTL_H + +/* Linux-compatible terminal request values and winsize layout. */ +struct winsize { + unsigned short ws_row; + unsigned short ws_col; + unsigned short ws_xpixel; + unsigned short ws_ypixel; +}; + +#define TIOCGWINSZ 0x5413UL +#define TIOCSWINSZ 0x5414UL +#define TIOCNOTTY 0x5422UL + +int ioctl(int fd, unsigned long request, ...); + +#endif diff --git a/examples/typephp-os/user/include/sys/syscall.h b/examples/typephp-os/user/include/sys/syscall.h index 5945304b..9701609a 100644 --- a/examples/typephp-os/user/include/sys/syscall.h +++ b/examples/typephp-os/user/include/sys/syscall.h @@ -14,6 +14,7 @@ #define SYS_mprotect TYPEPHP_SYS_MPROTECT #define SYS_munmap TYPEPHP_SYS_MUNMAP #define SYS_brk TYPEPHP_SYS_BRK +#define SYS_ioctl TYPEPHP_SYS_IOCTL #define SYS_access TYPEPHP_SYS_ACCESS #define SYS_getpid TYPEPHP_SYS_GETPID #define SYS_exit TYPEPHP_SYS_EXIT diff --git a/examples/typephp-os/user/runtime/README.md b/examples/typephp-os/user/runtime/README.md index a6f98a1d..3a12a1db 100644 --- a/examples/typephp-os/user/runtime/README.md +++ b/examples/typephp-os/user/runtime/README.md @@ -39,9 +39,8 @@ Selected LLVM compiler-rt 128-bit integer helpers are built separately as Ring-3 `builtins` command deliberately generates signed and unsigned division/remainder helper calls and verifies the results at runtime. -mlibc and Toybox are currently integration probes rather than runtime -dependencies. See `THIRDPARTY.md` for the tested boundary and the generated -Toybox ABI report. +Toybox is currently an integration probe rather than a runtime dependency. +See `THIRDPARTY.md` for the tested boundary and generated ABI report. The archive and each consumer must use the same freestanding ABI flags and PHP Nano/PHPX feature definitions. Those shared settings are maintained in the diff --git a/examples/typephp-os/user/runtime/ioctl.c b/examples/typephp-os/user/runtime/ioctl.c new file mode 100644 index 00000000..19e6db32 --- /dev/null +++ b/examples/typephp-os/user/runtime/ioctl.c @@ -0,0 +1,18 @@ +#include +#include +#include + +int ioctl(int fd, unsigned long request, ...) +{ + void *argument = 0; + + /* TIOCNOTTY has no third argument. The currently supported window-size + * requests use a pointer, as required by the Linux userspace ABI. */ + if (request != TIOCNOTTY) { + va_list arguments; + va_start(arguments, request); + argument = va_arg(arguments, void *); + va_end(arguments); + } + return (int) syscall(SYS_ioctl, fd, request, argument); +} diff --git a/examples/typephp-os/user/runtime/syscall.S b/examples/typephp-os/user/runtime/syscall.S index bd224581..d1deb0f3 100644 --- a/examples/typephp-os/user/runtime/syscall.S +++ b/examples/typephp-os/user/runtime/syscall.S @@ -6,7 +6,7 @@ .extern __errno_location syscall: # Translate the System V variadic-call ABI to Linux x86-64 syscall - # registers. TypePHP-OS currently enters the kernel through int 0x80. + # registers, then use the native 64-bit fast-system-call instruction. mov %rdi, %rax mov %rsi, %rdi mov %rdx, %rsi @@ -14,7 +14,7 @@ syscall: mov %r8, %r10 mov %r9, %r8 mov 8(%rsp), %r9 - int $0x80 + syscall cmp $-4095, %rax jb 1f neg %eax