boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs | README

commit 7a1635af1afbf2c4008f2b43cf3762acc215f10e
parent dcd943c71dc2e62e286ab77c062d55083b6921ff
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Tue,  5 May 2026 12:03:20 -0700

seed-kernel/user: factor sysc into shared syscall6 toplevel-asm thunk

hello/forktest/child each carried the same x8/x0..x5 inline-asm sysc
helper. Hoist it into a single syscall6 entry in the per-program
toplevel asm() block, declared extern in C — same codegen, prepares
the user programs for tcc3 self-host (tcc 0.9.26 silently drops
register-asm constraints; toplevel-asm avoids that path entirely).

The label-before-.globl ordering inside each asm() block works around
a tcc 0.9.26 quirk where ".globl name; name:" leaves the symbol UND.

Diffstat:
Mseed-kernel/user/child.c | 32+++++++++++++++++++-------------
Mseed-kernel/user/forktest.c | 47++++++++++++++++++++++++++---------------------
Mseed-kernel/user/hello.c | 52++++++++++++++++++++++++++++------------------------
3 files changed, 73 insertions(+), 58 deletions(-)

diff --git a/seed-kernel/user/child.c b/seed-kernel/user/child.c @@ -7,17 +7,10 @@ typedef unsigned long u64; #define SYS_write 64 #define SYS_exit_group 93 -static i64 sysc(u64 nr, u64 a, u64 b, u64 c) { - register u64 x8 asm("x8") = nr; - register u64 x0 asm("x0") = a; - register u64 x1 asm("x1") = b; - register u64 x2 asm("x2") = c; - asm volatile("svc #0" : "+r"(x0) : "r"(x8), "r"(x1), "r"(x2) : "memory", "cc"); - return (i64)x0; -} +extern i64 syscall6(u64 nr, u64 a, u64 b, u64 c, u64 d, u64 e, u64 f); -static i64 sys_write(int fd, const void *buf, u64 n) { return sysc(SYS_write, (u64)fd, (u64)buf, n); } -static void sys_exit(int c) { sysc(SYS_exit_group, (u64)c, 0, 0); for(;;); } +static i64 sys_write(int fd, const void *buf, u64 n) { return syscall6(SYS_write, (u64)fd, (u64)buf, n, 0,0,0); } +static void sys_exit(int c) { syscall6(SYS_exit_group, (u64)c, 0,0,0,0,0); for(;;); } void *memset(void *d, int c, u64 n) { unsigned char *dd = d; for (u64 i = 0; i < n; i++) dd[i] = (unsigned char)c; return d; @@ -42,10 +35,23 @@ void _start_c(long argc, char **argv) { sys_exit(42); } +/* See forktest.c for the .globl-after-label tcc 0.9.26 quirk. */ asm( + "_start: ldr x0, [sp]\n" ".globl _start\n" ".type _start, %function\n" - "_start:\n" - " ldr x0, [sp]\n" " add x1, sp, #8\n" - " b _start_c\n"); + " b _start_c\n" + "\n" + "syscall6:\n" + ".globl syscall6\n" + ".type syscall6, %function\n" + " mov x8, x0\n" + " mov x0, x1\n" + " mov x1, x2\n" + " mov x2, x3\n" + " mov x3, x4\n" + " mov x4, x5\n" + " mov x5, x6\n" + " svc #0\n" + " ret\n"); diff --git a/seed-kernel/user/forktest.c b/seed-kernel/user/forktest.c @@ -17,25 +17,12 @@ typedef int i32; #define SYS_waitid 95 #define SYS_spawn 1024 -static i64 sysc(u64 nr, u64 a, u64 b, u64 c, u64 d, u64 e, u64 f) { - register u64 x8 asm("x8") = nr; - register u64 x0 asm("x0") = a; - register u64 x1 asm("x1") = b; - register u64 x2 asm("x2") = c; - register u64 x3 asm("x3") = d; - register u64 x4 asm("x4") = e; - register u64 x5 asm("x5") = f; - asm volatile("svc #0" - : "+r"(x0) - : "r"(x8), "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x5) - : "memory", "cc"); - return (i64)x0; -} +extern i64 syscall6(u64 nr, u64 a, u64 b, u64 c, u64 d, u64 e, u64 f); -static i64 sys_write(int fd, const void *buf, u64 n) { return sysc(SYS_write, (u64)fd, (u64)buf, n, 0,0,0); } -static void sys_exit(int c) { sysc(SYS_exit_group, (u64)c, 0,0,0,0,0); for(;;); } -static i64 sys_spawn(const char *p, char **argv) { return sysc(SYS_spawn, (u64)p, (u64)argv, 0, 0, 0, 0); } -static i64 sys_waitid(int id, int pid, void *info, int opts) { return sysc(SYS_waitid, (u64)id, (u64)pid, (u64)info, (u64)opts, 0, 0); } +static i64 sys_write(int fd, const void *buf, u64 n) { return syscall6(SYS_write, (u64)fd, (u64)buf, n, 0,0,0); } +static void sys_exit(int c) { syscall6(SYS_exit_group, (u64)c, 0,0,0,0,0); for(;;); } +static i64 sys_spawn(const char *p, char **argv) { return syscall6(SYS_spawn, (u64)p, (u64)argv, 0, 0, 0, 0); } +static i64 sys_waitid(int id, int pid, void *info, int opts) { return syscall6(SYS_waitid, (u64)id, (u64)pid, (u64)info, (u64)opts, 0, 0); } void *memset(void *d, int c, u64 n) { unsigned char *dd = d; for (u64 i = 0; i < n; i++) dd[i] = (unsigned char)c; return d; @@ -70,10 +57,28 @@ void _start_c(long argc, char **argv) { sys_exit(0); } +/* tcc 0.9.26 quirk: in a toplevel asm() block, `.globl name` followed by + * `name:` leaves the symbol UND in the .o symtab. Putting the label + * first and `.globl name` after makes tcc register it as defined. gcc + * accepts both orderings. */ asm( + "_start: ldr x0, [sp]\n" ".globl _start\n" ".type _start, %function\n" - "_start:\n" - " ldr x0, [sp]\n" " add x1, sp, #8\n" - " b _start_c\n"); + " b _start_c\n" + "\n" + /* syscall6(nr, a..f) — args land in x0..x6 by SysV ABI. Linux + * arm64 wants the syscall number in x8 and a..f in x0..x5. */ + "syscall6:\n" + ".globl syscall6\n" + ".type syscall6, %function\n" + " mov x8, x0\n" + " mov x0, x1\n" + " mov x1, x2\n" + " mov x2, x3\n" + " mov x3, x4\n" + " mov x4, x5\n" + " mov x5, x6\n" + " svc #0\n" + " ret\n"); diff --git a/seed-kernel/user/hello.c b/seed-kernel/user/hello.c @@ -12,27 +12,14 @@ typedef unsigned long u64; #define SYS_brk 214 #define SYS_exit_group 93 -static i64 sysc(u64 nr, u64 a, u64 b, u64 c, u64 d, u64 e, u64 f) { - register u64 x8 asm("x8") = nr; - register u64 x0 asm("x0") = a; - register u64 x1 asm("x1") = b; - register u64 x2 asm("x2") = c; - register u64 x3 asm("x3") = d; - register u64 x4 asm("x4") = e; - register u64 x5 asm("x5") = f; - asm volatile("svc #0" - : "+r"(x0) - : "r"(x8), "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x5) - : "memory", "cc"); - return (i64)x0; -} +extern i64 syscall6(u64 nr, u64 a, u64 b, u64 c, u64 d, u64 e, u64 f); -static i64 sys_write(int fd, const void *buf, u64 n) { return sysc(SYS_write, (u64)fd, (u64)buf, n, 0,0,0); } -static void sys_exit(int c) { sysc(SYS_exit_group, (u64)c, 0,0,0,0,0); for(;;); } -static i64 sys_openat(int dfd, const char *p, int fl, int mo) { return sysc(SYS_openat, (u64)dfd, (u64)p, (u64)fl, (u64)mo, 0,0); } -static i64 sys_read(int fd, void *b, u64 n) { return sysc(SYS_read, (u64)fd, (u64)b, n, 0,0,0); } -static i64 sys_close(int fd) { return sysc(SYS_close, (u64)fd, 0,0,0,0,0); } -static i64 sys_brk(u64 a) { return sysc(SYS_brk, a, 0,0,0,0,0); } +static i64 sys_write(int fd, const void *buf, u64 n) { return syscall6(SYS_write, (u64)fd, (u64)buf, n, 0,0,0); } +static void sys_exit(int c) { syscall6(SYS_exit_group, (u64)c, 0,0,0,0,0); for(;;); } +static i64 sys_openat(int dfd, const char *p, int fl, int mo) { return syscall6(SYS_openat, (u64)dfd, (u64)p, (u64)fl, (u64)mo, 0,0); } +static i64 sys_read(int fd, void *b, u64 n) { return syscall6(SYS_read, (u64)fd, (u64)b, n, 0,0,0); } +static i64 sys_close(int fd) { return syscall6(SYS_close, (u64)fd, 0,0,0,0,0); } +static i64 sys_brk(u64 a) { return syscall6(SYS_brk, a, 0,0,0,0,0); } /* gcc may emit calls to memset/memcpy for stack zeroing or struct copies. */ void *memset(void *d, int c, u64 n) { @@ -91,8 +78,12 @@ void _start_c(long argc, char **argv) { unsigned char m[4] = {0}; i64 n = sys_read(fd, m, 4); puts_("read("); put_d(n); puts_(") magic = "); + static const char hex[] = "0123456789abcdef"; for (int i = 0; i < 4; i++) { - char c[3] = { "0123456789abcdef"[m[i]>>4], "0123456789abcdef"[m[i]&0xf], ' ' }; + char c[3]; + c[0] = hex[m[i] >> 4]; + c[1] = hex[m[i] & 0xf]; + c[2] = ' '; sys_write(1, c, 3); } puts_("\n"); @@ -121,10 +112,23 @@ void _start_c(long argc, char **argv) { * kernel sets sp_el0 to point at [argc][argv[0]]... before ERETing. * Emitted as a plain global symbol with raw asm — no C-compiler-generated * prologue, since gcc would clobber sp before we read argc. */ +/* See forktest.c for the .globl-after-label tcc 0.9.26 quirk. */ asm( + "_start: ldr x0, [sp]\n" ".globl _start\n" ".type _start, %function\n" - "_start:\n" - " ldr x0, [sp]\n" " add x1, sp, #8\n" - " b _start_c\n"); + " b _start_c\n" + "\n" + "syscall6:\n" + ".globl syscall6\n" + ".type syscall6, %function\n" + " mov x8, x0\n" + " mov x0, x1\n" + " mov x1, x2\n" + " mov x2, x3\n" + " mov x3, x4\n" + " mov x4, x5\n" + " mov x5, x6\n" + " svc #0\n" + " ret\n");