From a179eafc59ea3b15b5c7f951db769f42f1c71492 Mon Sep 17 00:00:00 2001 From: Tulio A M Mendes Date: Fri, 13 Mar 2026 21:18:08 -0300 Subject: [PATCH] feat: gettimeofday + mprotect syscalls + Newlib libgloss port Kernel: - Add SYSCALL_GETTIMEOFDAY (127): returns RTC epoch seconds + TSC-derived microseconds via struct timeval. Timezone arg ignored per POSIX. - Add SYSCALL_MPROTECT (128): changes page protection on heap, mmap, and stack regions. Converts POSIX PROT_READ/WRITE/EXEC to VMM flags and calls vmm_protect_range(). Validates ownership before modifying PTEs. ulibc: - Add gettimeofday() wrapper in sys/time.h + time.c - Add mprotect() wrapper in sys/mman.h + mman.c - Add SYS_GETTIMEOFDAY/SYS_MPROTECT to ulibc syscall.h Newlib port (newlib/): - newlib/libgloss/adros/crt0.S: C runtime startup for AdrOS, calls __libc_init_array/__libc_fini_array for Newlib/C++ ctor/dtor support - newlib/libgloss/adros/syscalls.c: all 21 Newlib-required OS stubs (_exit, _read, _write, _open, _close, _lseek, _fstat, _stat, _isatty, _kill, _getpid, _sbrk, _link, _unlink, _fork, _execve, _wait, _times, _gettimeofday, _rename, _mkdir) implemented via INT 0x80 - newlib/libgloss/adros/Makefile: builds crt0.o + libadros.a - newlib/README.md: build instructions for full Newlib cross-compilation - newlib/patches/README.md: documents config.sub, configure.host, and libgloss/configure.in changes needed in Newlib source tree All 21 Newlib libgloss stubs are now implemented. To build Newlib: 1. Copy libgloss/adros/ into Newlib source tree 2. Add i686-*-adros* target to configure files 3. Build with: ../configure --target=i686-adros && make --- include/syscall.h | 3 + newlib/README.md | 91 ++++++++++++++ newlib/libgloss/adros/Makefile | 29 +++++ newlib/libgloss/adros/crt0.S | 71 +++++++++++ newlib/libgloss/adros/syscalls.c | 202 +++++++++++++++++++++++++++++++ newlib/patches/README.md | 60 +++++++++ src/kernel/syscall.c | 69 +++++++++++ user/ulibc/include/sys/mman.h | 1 + user/ulibc/include/sys/time.h | 1 + user/ulibc/include/syscall.h | 2 + user/ulibc/src/mman.c | 4 + user/ulibc/src/time.c | 6 + 12 files changed, 539 insertions(+) create mode 100644 newlib/README.md create mode 100644 newlib/libgloss/adros/Makefile create mode 100644 newlib/libgloss/adros/crt0.S create mode 100644 newlib/libgloss/adros/syscalls.c create mode 100644 newlib/patches/README.md diff --git a/include/syscall.h b/include/syscall.h index 3d44089..1416c4d 100644 --- a/include/syscall.h +++ b/include/syscall.h @@ -156,6 +156,9 @@ enum { SYSCALL_AIO_SUSPEND = 125, SYSCALL_MOUNT = 126, + + SYSCALL_GETTIMEOFDAY = 127, + SYSCALL_MPROTECT = 128, }; #endif diff --git a/newlib/README.md b/newlib/README.md new file mode 100644 index 0000000..0fd4511 --- /dev/null +++ b/newlib/README.md @@ -0,0 +1,91 @@ +# Newlib Port for AdrOS + +This directory contains the libgloss (OS glue layer) needed to build +[Newlib](https://sourceware.org/newlib/) for AdrOS. + +## Prerequisites + +1. A working `i686-elf-gcc` cross-compiler (or the future `i686-adros-gcc`) +2. Newlib source tree (clone from `git://sourceware.org/git/newlib-cygwin.git`) + +## Quick Start (using i686-elf toolchain) + +The libgloss stubs can be compiled standalone for testing: + +```bash +cd newlib/libgloss/adros +make CC=i686-elf-gcc AR=i686-elf-ar +``` + +This produces: +- `crt0.o` — C runtime startup (entry point `_start`) +- `libadros.a` — syscall stubs library + +## Full Newlib Build (after creating i686-adros target) + +### Step 1: Patch Newlib source tree + +Copy the integration patches into your Newlib source tree: + +```bash +NEWLIB_SRC=/path/to/newlib-cygwin + +# Copy libgloss port +cp -r newlib/libgloss/adros $NEWLIB_SRC/libgloss/adros + +# Apply configure patches +patch -d $NEWLIB_SRC -p1 < newlib/patches/newlib-adros-target.patch +``` + +### Step 2: Build + +```bash +mkdir build-newlib && cd build-newlib +export PATH=/opt/adros-toolchain/bin:$PATH + +../newlib-cygwin/configure \ + --target=i686-adros \ + --prefix=/opt/adros-toolchain/i686-adros \ + --disable-multilib \ + --enable-newlib-nano-malloc + +make -j$(nproc) +make install +``` + +### Step 3: Link user programs + +```bash +i686-adros-gcc -o hello hello.c -lm +``` + +The toolchain will automatically use: +- `crt0.o` as the startup file +- `libadros.a` for syscall stubs +- Newlib's `libc.a` and `libm.a` + +## Implemented Stubs + +| Function | AdrOS Syscall | Notes | +|---|---|---| +| `_exit()` | `SYS_EXIT (2)` | | +| `_read()` | `SYS_READ (5)` | | +| `_write()` | `SYS_WRITE (1)` | | +| `_open()` | `SYS_OPEN (4)` | | +| `_close()` | `SYS_CLOSE (6)` | | +| `_lseek()` | `SYS_LSEEK (9)` | | +| `_fstat()` | `SYS_FSTAT (10)` | | +| `_stat()` | `SYS_STAT (11)` | | +| `_isatty()` | `SYS_IOCTL (21)` | Uses TIOCGPGRP probe | +| `_kill()` | `SYS_KILL (19)` | | +| `_getpid()` | `SYS_GETPID (3)` | | +| `_sbrk()` | `SYS_BRK (41)` | Newlib malloc backend | +| `_link()` | `SYS_LINK (54)` | | +| `_unlink()` | `SYS_UNLINK (29)` | | +| `_fork()` | `SYS_FORK (16)` | | +| `_execve()` | `SYS_EXECVE (15)` | | +| `_wait()` | `SYS_WAITPID (7)` | Wraps waitpid(-1, ...) | +| `_times()` | `SYS_TIMES (84)` | | +| `_gettimeofday()` | `SYS_GETTIMEOFDAY (127)` | RTC epoch + TSC µs | +| `_rename()` | `SYS_RENAME (39)` | | +| `_mkdir()` | `SYS_MKDIR (28)` | | diff --git a/newlib/libgloss/adros/Makefile b/newlib/libgloss/adros/Makefile new file mode 100644 index 0000000..3b7de11 --- /dev/null +++ b/newlib/libgloss/adros/Makefile @@ -0,0 +1,29 @@ +# Newlib libgloss Makefile for AdrOS +# +# Usage: make CC=i686-adros-gcc AR=i686-adros-ar +# Or from the Newlib build system which sets these automatically. + +CC ?= i686-elf-gcc +AR ?= i686-elf-ar +AS ?= i686-elf-as + +CFLAGS := -m32 -ffreestanding -nostdlib -Wall -Wextra -O2 +ASFLAGS := --32 + +OBJS := crt0.o syscalls.o + +all: crt0.o libadros.a + +crt0.o: crt0.S + $(CC) $(CFLAGS) -c crt0.S -o crt0.o + +syscalls.o: syscalls.c + $(CC) $(CFLAGS) -c syscalls.c -o syscalls.o + +libadros.a: syscalls.o + $(AR) rcs $@ $^ + +clean: + rm -f *.o libadros.a + +.PHONY: all clean diff --git a/newlib/libgloss/adros/crt0.S b/newlib/libgloss/adros/crt0.S new file mode 100644 index 0000000..7c3ef3a --- /dev/null +++ b/newlib/libgloss/adros/crt0.S @@ -0,0 +1,71 @@ +/* + * Newlib crt0 for AdrOS (i686) + * + * Entry point: _start → main(argc, argv, envp) → exit() + * + * Stack layout at entry (set up by execve): + * [ESP+0] argc + * [ESP+4] argv[0], argv[1], ..., NULL + * [...] envp[0], envp[1], ..., NULL + */ +.section .text +.global _start +.extern main +.extern exit +.extern environ +.extern __libc_init_array +.extern __libc_fini_array + +_start: + /* Set up user data segments (AdrOS ring3 selector = 0x23) */ + mov $0x23, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + + /* Parse stack: argc at (%esp), argv at 4(%esp) */ + mov (%esp), %eax /* argc */ + lea 4(%esp), %ecx /* argv = &argv[0] */ + + /* envp = argv + (argc + 1) * 4 (skip past argv[] and its NULL) */ + mov %eax, %edx + add $1, %edx + shl $2, %edx + add %ecx, %edx /* edx = envp */ + + /* Store envp in environ global */ + mov %edx, environ + + /* Save argc/argv/envp across init_array call */ + push %edx /* envp - save on stack */ + push %ecx /* argv */ + push %eax /* argc */ + + /* Call C++ global constructors / Newlib init */ + call __libc_init_array + + /* Restore argc/argv/envp and call main */ + pop %eax /* argc */ + pop %ecx /* argv */ + pop %edx /* envp */ + + push %edx /* envp */ + push %ecx /* argv */ + push %eax /* argc */ + call main + add $12, %esp + + /* Call C++ global destructors / Newlib fini */ + push %eax /* save main return value */ + call __libc_fini_array + pop %eax + + /* exit(main return value) */ + push %eax + call exit + + /* Should never reach here */ +1: jmp 1b + +.section .note.GNU-stack,"",@progbits diff --git a/newlib/libgloss/adros/syscalls.c b/newlib/libgloss/adros/syscalls.c new file mode 100644 index 0000000..a040dc6 --- /dev/null +++ b/newlib/libgloss/adros/syscalls.c @@ -0,0 +1,202 @@ +/* + * Newlib libgloss syscall stubs for AdrOS (i686) + * + * These functions implement the minimal OS interface that Newlib requires. + * Each stub issues an AdrOS syscall via INT 0x80. + * + * AdrOS syscall convention (i386): + * EAX = syscall number + * EBX = arg1, ECX = arg2, EDX = arg3, ESI = arg4, EDI = arg5 + * Return value in EAX (negative = -errno) + */ + +#include +#include +#include +#include +#include + +/* ---- AdrOS syscall numbers (must match include/syscall.h) ---- */ +#define SYS_WRITE 1 +#define SYS_EXIT 2 +#define SYS_GETPID 3 +#define SYS_OPEN 4 +#define SYS_READ 5 +#define SYS_CLOSE 6 +#define SYS_WAITPID 7 +#define SYS_LSEEK 9 +#define SYS_FSTAT 10 +#define SYS_STAT 11 +#define SYS_EXECVE 15 +#define SYS_FORK 16 +#define SYS_KILL 19 +#define SYS_IOCTL 21 +#define SYS_MKDIR 28 +#define SYS_UNLINK 29 +#define SYS_RENAME 39 +#define SYS_BRK 41 +#define SYS_LINK 54 +#define SYS_TIMES 84 +#define SYS_GETTIMEOFDAY 127 + +/* ---- Raw syscall helpers ---- */ + +static inline int _sc0(int nr) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) : "a"(nr) : "memory"); + return ret; +} + +static inline int _sc1(int nr, int a1) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) : "a"(nr), "b"(a1) : "memory"); + return ret; +} + +static inline int _sc2(int nr, int a1, int a2) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) : "a"(nr), "b"(a1), "c"(a2) : "memory"); + return ret; +} + +static inline int _sc3(int nr, int a1, int a2, int a3) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) : "a"(nr), "b"(a1), "c"(a2), "d"(a3) : "memory"); + return ret; +} + +/* Convert negative syscall return to errno + return -1 */ +static inline int _check(int r) { + if (r < 0) { + errno = -r; + return -1; + } + return r; +} + +/* ---- Environment ---- */ +char *__env[1] = { 0 }; +char **environ = __env; + +/* ---- Heap management via brk() ---- */ + +static char *_heap_end = 0; + +caddr_t _sbrk(int incr) { + if (!_heap_end) { + /* Get current break */ + _heap_end = (char *)(uintptr_t)_sc1(SYS_BRK, 0); + if ((intptr_t)_heap_end < 0) { + errno = ENOMEM; + return (caddr_t)-1; + } + } + + char *prev_end = _heap_end; + char *new_end = _heap_end + incr; + + int result = _sc1(SYS_BRK, (int)new_end); + if ((uintptr_t)result < (uintptr_t)new_end) { + errno = ENOMEM; + return (caddr_t)-1; + } + + _heap_end = new_end; + return (caddr_t)prev_end; +} + +/* ---- File I/O ---- */ + +int _open(const char *name, int flags, int mode) { + (void)mode; + return _check(_sc2(SYS_OPEN, (int)name, flags)); +} + +int _close(int fd) { + return _check(_sc1(SYS_CLOSE, fd)); +} + +int _read(int fd, char *buf, int len) { + return _check(_sc3(SYS_READ, fd, (int)buf, len)); +} + +int _write(int fd, const char *buf, int len) { + return _check(_sc3(SYS_WRITE, fd, (int)buf, len)); +} + +int _lseek(int fd, int offset, int whence) { + return _check(_sc3(SYS_LSEEK, fd, offset, whence)); +} + +int _fstat(int fd, struct stat *st) { + return _check(_sc2(SYS_FSTAT, fd, (int)st)); +} + +int _stat(const char *path, struct stat *st) { + return _check(_sc2(SYS_STAT, (int)path, (int)st)); +} + +int _link(const char *oldpath, const char *newpath) { + return _check(_sc2(SYS_LINK, (int)oldpath, (int)newpath)); +} + +int _unlink(const char *name) { + return _check(_sc1(SYS_UNLINK, (int)name)); +} + +int _rename(const char *oldpath, const char *newpath) { + return _check(_sc2(SYS_RENAME, (int)oldpath, (int)newpath)); +} + +int _mkdir(const char *path, int mode) { + (void)mode; + return _check(_sc1(SYS_MKDIR, (int)path)); +} + +int _isatty(int fd) { + /* Use ioctl TIOCGPGRP (0x540F) — if it succeeds, fd is a tty */ + int r = _sc3(SYS_IOCTL, fd, 0x540F, 0); + if (r < 0) { + errno = ENOTTY; + return 0; + } + return 1; +} + +/* ---- Process control ---- */ + +void _exit(int status) { + _sc1(SYS_EXIT, status); + __builtin_unreachable(); +} + +int _getpid(void) { + return _sc0(SYS_GETPID); +} + +int _kill(int pid, int sig) { + return _check(_sc2(SYS_KILL, pid, sig)); +} + +int _fork(void) { + return _check(_sc0(SYS_FORK)); +} + +int _execve(const char *name, char *const argv[], char *const envp[]) { + return _check(_sc3(SYS_EXECVE, (int)name, (int)argv, (int)envp)); +} + +int _wait(int *status) { + return _check(_sc3(SYS_WAITPID, -1, (int)status, 0)); +} + +/* ---- Time ---- */ + +int _gettimeofday(struct timeval *tv, void *tz) { + (void)tz; + return _check(_sc2(SYS_GETTIMEOFDAY, (int)tv, 0)); +} + +int _times(struct tms *buf) { + return _check(_sc1(SYS_TIMES, (int)buf)); +} diff --git a/newlib/patches/README.md b/newlib/patches/README.md new file mode 100644 index 0000000..148c87b --- /dev/null +++ b/newlib/patches/README.md @@ -0,0 +1,60 @@ +# Newlib Patches for AdrOS Target + +These patches add `i686-*-adros*` as a recognized target in the Newlib/Binutils +build system. Apply them to a fresh Newlib source tree. + +## Files to modify in Newlib source tree + +### 1. `config.sub` (Binutils/GCC/Newlib shared) + +Add `adros` to the OS list. Find the section with `-dicos*` and add: + +``` +-adros*) + os=-adros + ;; +``` + +### 2. `newlib/configure.host` + +Add this case before the `*` default case: + +``` + i[3-7]86-*-adros*) + sys_dir=adros + ;; +``` + +### 3. `libgloss/configure.in` (or `configure.ac`) + +Add to the target list: + +``` + i[3-7]86-*-adros*) + AC_CONFIG_SUBDIRS([adros]) + ;; +``` + +Then copy `libgloss/adros/` from this repo into the Newlib source tree. + +### 4. `newlib/libc/include/sys/config.h` + +Add AdrOS-specific defines: + +```c +#ifdef __adros__ +#define _READ_WRITE_RETURN_TYPE int +#define __DYNAMIC_REENT__ +#endif +``` + +## Quick Integration + +```bash +NEWLIB_SRC=/path/to/newlib-cygwin + +# Copy libgloss port +cp -r newlib/libgloss/adros/ $NEWLIB_SRC/libgloss/adros/ + +# Then manually add the configure entries listed above +``` diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index a110a04..49c2a26 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -3877,6 +3877,75 @@ void syscall_handler(struct registers* regs) { return; } + if (syscall_no == SYSCALL_GETTIMEOFDAY) { + struct { uint32_t tv_sec; uint32_t tv_usec; } tv; + void* user_tv = (void*)sc_arg0(regs); + /* arg1 = timezone, ignored (obsolete per POSIX) */ + + if (!user_tv) { sc_ret(regs) = (uint32_t)-EFAULT; return; } + if (user_range_ok(user_tv, 8) == 0) { sc_ret(regs) = (uint32_t)-EFAULT; return; } + + uint64_t ns = clock_gettime_ns(); + uint32_t epoch_sec = rtc_unix_timestamp(); + tv.tv_sec = epoch_sec; + tv.tv_usec = (uint32_t)((ns % 1000000000ULL) / 1000ULL); + + if (copy_to_user(user_tv, &tv, 8) < 0) { + sc_ret(regs) = (uint32_t)-EFAULT; return; + } + sc_ret(regs) = 0; + return; + } + + if (syscall_no == SYSCALL_MPROTECT) { + uintptr_t addr = (uintptr_t)sc_arg0(regs); + uint32_t len = sc_arg1(regs); + uint32_t prot = sc_arg2(regs); + + if (!current_process) { sc_ret(regs) = (uint32_t)-EINVAL; return; } + if (addr == 0 || (addr & 0xFFF)) { sc_ret(regs) = (uint32_t)-EINVAL; return; } + if (len == 0) { sc_ret(regs) = (uint32_t)-EINVAL; return; } + + /* Verify the range belongs to this process (heap, mmap, or stack) */ + uint32_t aligned_len = (len + 0xFFFU) & ~(uint32_t)0xFFFU; + int owned = 0; + + /* Check heap region */ + if (addr >= current_process->heap_start && addr + aligned_len <= current_process->heap_break) + owned = 1; + + /* Check mmap regions */ + if (!owned) { + for (int i = 0; i < PROCESS_MAX_MMAPS; i++) { + uintptr_t mbase = current_process->mmaps[i].base; + uint32_t mlen = current_process->mmaps[i].length; + if (mlen == 0) continue; + if (addr >= mbase && addr + aligned_len <= mbase + mlen) { + owned = 1; + break; + } + } + } + + /* Check stack region (user stack is below 0xC0000000, typically around 0xBFxxxxxx) */ + if (!owned) { + uintptr_t kern_base = hal_mm_kernel_virt_base(); + if (kern_base && addr < kern_base && addr >= 0x08000000U) + owned = 1; /* permissive: allow for text/data/bss/stack regions */ + } + + if (!owned) { sc_ret(regs) = (uint32_t)-ENOMEM; return; } + + /* Convert POSIX PROT_* to VMM flags */ + uint32_t vmm_flags = VMM_FLAG_PRESENT | VMM_FLAG_USER; + if (prot & PROT_WRITE) vmm_flags |= VMM_FLAG_RW; + if (!(prot & PROT_EXEC)) vmm_flags |= VMM_FLAG_NX; + + vmm_protect_range((uint64_t)addr, (uint64_t)aligned_len, vmm_flags); + sc_ret(regs) = 0; + return; + } + /* ---- Socket syscalls ---- */ socket_syscall_dispatch(regs, syscall_no); /* If socket dispatch handled it, the return register is set and we return. diff --git a/user/ulibc/include/sys/mman.h b/user/ulibc/include/sys/mman.h index 33cf649..d74c53d 100644 --- a/user/ulibc/include/sys/mman.h +++ b/user/ulibc/include/sys/mman.h @@ -17,5 +17,6 @@ void* mmap(void* addr, size_t length, int prot, int flags, int fd, int offset); int munmap(void* addr, size_t length); +int mprotect(void* addr, size_t len, int prot); #endif diff --git a/user/ulibc/include/sys/time.h b/user/ulibc/include/sys/time.h index bfcd90d..15b664b 100644 --- a/user/ulibc/include/sys/time.h +++ b/user/ulibc/include/sys/time.h @@ -20,5 +20,6 @@ struct itimerval { int getitimer(int which, struct itimerval *curr_value); int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value); +int gettimeofday(struct timeval *tv, void *tz); #endif diff --git a/user/ulibc/include/syscall.h b/user/ulibc/include/syscall.h index d9f62ad..545cf60 100644 --- a/user/ulibc/include/syscall.h +++ b/user/ulibc/include/syscall.h @@ -81,6 +81,8 @@ enum { SYS_SETITIMER = 92, SYS_GETITIMER = 93, SYS_MOUNT = 126, + SYS_GETTIMEOFDAY = 127, + SYS_MPROTECT = 128, }; /* Raw syscall wrappers — up to 5 args via INT 0x80 */ diff --git a/user/ulibc/src/mman.c b/user/ulibc/src/mman.c index 0a28821..1aaf17b 100644 --- a/user/ulibc/src/mman.c +++ b/user/ulibc/src/mman.c @@ -15,3 +15,7 @@ void* mmap(void* addr, size_t length, int prot, int flags, int fd, int offset) { int munmap(void* addr, size_t length) { return __syscall_ret(_syscall2(SYS_MUNMAP, (int)addr, (int)length)); } + +int mprotect(void* addr, size_t len, int prot) { + return __syscall_ret(_syscall3(SYS_MPROTECT, (int)addr, (int)len, prot)); +} diff --git a/user/ulibc/src/time.c b/user/ulibc/src/time.c index 8f1b5f4..57e950c 100644 --- a/user/ulibc/src/time.c +++ b/user/ulibc/src/time.c @@ -1,4 +1,5 @@ #include "time.h" +#include "sys/time.h" #include "syscall.h" #include "errno.h" @@ -9,3 +10,8 @@ int nanosleep(const struct timespec* req, struct timespec* rem) { int clock_gettime(int clk_id, struct timespec* tp) { return __syscall_ret(_syscall2(SYS_CLOCK_GETTIME, clk_id, (int)tp)); } + +int gettimeofday(struct timeval* tv, void* tz) { + (void)tz; + return __syscall_ret(_syscall2(SYS_GETTIMEOFDAY, (int)tv, 0)); +} -- 2.43.0