SYSCALL_AIO_SUSPEND = 125,
SYSCALL_MOUNT = 126,
+
+ SYSCALL_GETTIMEOFDAY = 127,
+ SYSCALL_MPROTECT = 128,
};
#endif
--- /dev/null
+# 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)` | |
--- /dev/null
+# 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/times.h>
+#include <errno.h>
+#include <stdint.h>
+
+/* ---- 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));
+}
--- /dev/null
+# 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
+```
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.
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
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
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 */
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));
+}
#include "time.h"
+#include "sys/time.h"
#include "syscall.h"
#include "errno.h"
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));
+}