boot2

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

hello.c (6280B)


      1 /* User-space "hello" for the seed kernel. Static ELF, no libc. */
      2 
      3 typedef long          i64;
      4 typedef unsigned long u64;
      5 
      6 #if defined(__x86_64__)
      7 #define SYS_read        0
      8 #define SYS_write       1
      9 #define SYS_close       3
     10 #define SYS_lseek       8
     11 #define SYS_brk         12
     12 #define SYS_exit_group  60
     13 #define SYS_openat      257
     14 #elif defined(__riscv)
     15 #define SYS_openat      56
     16 #define SYS_close       57
     17 #define SYS_lseek       62
     18 #define SYS_read        63
     19 #define SYS_write       64
     20 #define SYS_exit_group  93
     21 #define SYS_brk         214
     22 #else
     23 #define SYS_openat      56
     24 #define SYS_close       57
     25 #define SYS_lseek       62
     26 #define SYS_read        63
     27 #define SYS_write       64
     28 #define SYS_exit_group  93
     29 #define SYS_brk         214
     30 #endif
     31 
     32 extern i64 syscall6(u64 nr, u64 a, u64 b, u64 c, u64 d, u64 e, u64 f);
     33 
     34 static i64 sys_write(int fd, const void *buf, u64 n) { return syscall6(SYS_write, (u64)fd, (u64)buf, n, 0,0,0); }
     35 static void sys_exit(int c) { syscall6(SYS_exit_group, (u64)c, 0,0,0,0,0); for(;;); }
     36 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); }
     37 static i64 sys_read(int fd, void *b, u64 n) { return syscall6(SYS_read, (u64)fd, (u64)b, n, 0,0,0); }
     38 static i64 sys_close(int fd) { return syscall6(SYS_close, (u64)fd, 0,0,0,0,0); }
     39 static i64 sys_brk(u64 a) { return syscall6(SYS_brk, a, 0,0,0,0,0); }
     40 
     41 /* gcc may emit calls to memset/memcpy for stack zeroing or struct copies. */
     42 void *memset(void *d, int c, u64 n) {
     43     unsigned char *dd = d; for (u64 i = 0; i < n; i++) dd[i] = (unsigned char)c; return d;
     44 }
     45 void *memcpy(void *d, const void *s, u64 n) {
     46     unsigned char *dd = d; const unsigned char *ss = s;
     47     for (u64 i = 0; i < n; i++) dd[i] = ss[i];
     48     return d;
     49 }
     50 
     51 static u64 strlen_(const char *s) { u64 n = 0; while (s[n]) n++; return n; }
     52 static void puts_(const char *s) { sys_write(1, s, strlen_(s)); }
     53 
     54 static void put_d(i64 v) {
     55     char buf[24]; int i = 0;
     56     if (v < 0) { sys_write(1, "-", 1); v = -v; }
     57     if (v == 0) buf[i++] = '0';
     58     while (v) { buf[i++] = '0' + (char)(v % 10); v /= 10; }
     59     while (i--) sys_write(1, &buf[i], 1);
     60 }
     61 
     62 static void put_x(u64 v) {
     63     static const char hex[] = "0123456789abcdef";
     64     sys_write(1, "0x", 2);
     65     for (int i = 60; i >= 0; i -= 4) { char c = hex[(v >> i) & 0xf]; sys_write(1, &c, 1); }
     66 }
     67 
     68 /* aarch64 entry: x0 holds nothing — the SysV stack layout is at sp:
     69  *   [argc][argv[0]]...[argv[argc-1]][NULL][envp...][NULL]
     70  * We read argc/argv off the initial stack pointer in the asm shim
     71  * below, then tail-call into _start_c. */
     72 void _start_c(long argc, char **argv) {
     73     puts_("hello from user space (EL1t, identity-map MMU)\n");
     74     puts_("argc = "); put_d(argc); puts_("\n");
     75     for (long i = 0; i < argc; i++) {
     76         puts_("  argv["); put_d(i); puts_("] = ");
     77         puts_(argv[i]); puts_("\n");
     78     }
     79 
     80     /* Exercise brk: ask current break, push it up by 1 MiB, write+read. */
     81     u64 b0 = (u64)sys_brk(0);
     82     puts_("brk(0) = "); put_x(b0); puts_("\n");
     83     u64 b1 = (u64)sys_brk(b0 + 0x100000);
     84     puts_("brk(+1MB) = "); put_x(b1); puts_("\n");
     85     char *p = (char *)b0;
     86     for (int i = 0; i < 16; i++) p[i] = (char)('A' + i);
     87     sys_write(1, "first 16 bytes of new heap: ", 28);
     88     sys_write(1, p, 16);
     89     puts_("\n");
     90 
     91     /* Exercise tmpfs: open /init (us), read first 4 bytes (ELF magic). */
     92     int fd = (int)sys_openat(-100, "init", 0 /*O_RDONLY*/, 0);
     93     puts_("openat(\"init\") = "); put_d(fd); puts_("\n");
     94     if (fd >= 0) {
     95         unsigned char m[4] = {0};
     96         i64 n = sys_read(fd, m, 4);
     97         puts_("read("); put_d(n); puts_(") magic = ");
     98         static const char hex[] = "0123456789abcdef";
     99         for (int i = 0; i < 4; i++) {
    100             char c[3];
    101             c[0] = hex[m[i] >> 4];
    102             c[1] = hex[m[i] & 0xf];
    103             c[2] = ' ';
    104             sys_write(1, c, 3);
    105         }
    106         puts_("\n");
    107         sys_close(fd);
    108     }
    109 
    110     /* Write a new file and read it back. */
    111     int wfd = (int)sys_openat(-100, "scratch", 0101 /*O_WRONLY|O_CREAT*/, 0644);
    112     if (wfd >= 0) {
    113         const char *msg = "boot2 says hi\n";
    114         sys_write(wfd, msg, strlen_(msg));
    115         sys_close(wfd);
    116         int rfd = (int)sys_openat(-100, "scratch", 0, 0);
    117         char buf[64] = {0};
    118         i64 n = sys_read(rfd, buf, sizeof buf);
    119         puts_("scratch readback ("); put_d(n); puts_(" bytes): ");
    120         sys_write(1, buf, (u64)n);
    121         sys_close(rfd);
    122     }
    123 
    124     puts_("[user] all checks passed, exiting 0\n");
    125     sys_exit(0);
    126 }
    127 
    128 /* Read argc/argv off the initial stack and tail-call into _start_c. The
    129  * kernel sets sp_el0 to point at [argc][argv[0]]... before ERETing.
    130  * Emitted as a plain global symbol with raw asm — no C-compiler-generated
    131  * prologue, since gcc would clobber sp before we read argc. */
    132 /* See forktest.c for the .globl-after-label tcc 0.9.26 quirk. */
    133 #if defined(__x86_64__)
    134 asm(
    135     "_start: mov (%rsp), %rdi\n"
    136     ".globl _start\n"
    137     ".type  _start, @function\n"
    138     "    lea 8(%rsp), %rsi\n"
    139     "    call _start_c\n"
    140     "\n"
    141     "syscall6:\n"
    142     ".globl syscall6\n"
    143     ".type  syscall6, @function\n"
    144     "    mov %rdi, %rax\n"
    145     "    mov %rsi, %rdi\n"
    146     "    mov %rdx, %rsi\n"
    147     "    mov %rcx, %rdx\n"
    148     "    mov %r8,  %r10\n"
    149     "    mov %r9,  %r8\n"
    150     "    mov 8(%rsp), %r9\n"
    151     "    int $0x80\n"
    152     "    ret\n");
    153 #elif defined(__riscv)
    154 asm(
    155     "_start: ld a0, 0(sp)\n"
    156     ".globl _start\n"
    157     ".type  _start, @function\n"
    158     "    addi a1, sp, 8\n"
    159     "    tail _start_c\n"
    160     "\n"
    161     "syscall6:\n"
    162     ".globl syscall6\n"
    163     ".type  syscall6, @function\n"
    164     "    mv a7, a0\n"
    165     "    mv a0, a1\n"
    166     "    mv a1, a2\n"
    167     "    mv a2, a3\n"
    168     "    mv a3, a4\n"
    169     "    mv a4, a5\n"
    170     "    mv a5, a6\n"
    171     "    ecall\n"
    172     "    ret\n");
    173 #else
    174 asm(
    175     "_start: ldr x0, [sp]\n"
    176     ".globl _start\n"
    177     ".type  _start, %function\n"
    178     "    add x1, sp, #8\n"
    179     "    b   _start_c\n"
    180     "\n"
    181     "syscall6:\n"
    182     ".globl syscall6\n"
    183     ".type  syscall6, %function\n"
    184     "    mov x8, x0\n"
    185     "    mov x0, x1\n"
    186     "    mov x1, x2\n"
    187     "    mov x2, x3\n"
    188     "    mov x3, x4\n"
    189     "    mov x4, x5\n"
    190     "    mov x5, x6\n"
    191     "    svc #0\n"
    192     "    ret\n");
    193 #endif