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