aboutsummaryrefslogtreecommitdiff
path: root/src/boot/port_wasi.c
blob: ba8da8cf60b712d2be2f3ee21daf50c9c30cd6fd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/* WASI port of the microcore, for the bootstrap seed (seed/zc.wasm). reads the
   whole of stdin, runs the genome inside, writes stdout. needs no libc: the only
   imports are wasi_snapshot_preview1 fd_read, fd_write and proc_exit, so the same
   module runs under node (src/boot/run.mjs) and wasmtime. build with
   src/boot/mkseed.sh. */
#include "micro_port.h"

#define IMPORT(n) __attribute__((import_module("wasi_snapshot_preview1"), import_name(n)))
typedef struct { void *buf; unsigned long len; } Iov;
IMPORT("fd_read") int wasi_fd_read(int fd, Iov *iov, int n, unsigned long *got);
IMPORT("fd_write") int wasi_fd_write(int fd, Iov *iov, int n, unsigned long *put);
IMPORT("path_open") int wasi_path_open(int dir, int dirflags, const char *path, unsigned long len, int oflags, unsigned long long rights, unsigned long long inherit, int fdflags, int *fd);
IMPORT("fd_close") int wasi_fd_close(int fd);
IMPORT("proc_exit") _Noreturn void wasi_proc_exit(int code);

static unsigned char ibuf[65536], obuf[65536];
static int in_n, in_p, in_end, on;

static void fill(void)
{
	Iov v = { ibuf, sizeof ibuf }; unsigned long got = 0;
	in_p = 0; in_n = 0;
	if (in_end) return;
	if (wasi_fd_read(0, &v, 1, &got) || got == 0) { in_end = 1; return; }
	in_n = (int)got;
}
static void flush_fd(int fd, unsigned char *b, int n)
{
	while (n > 0) {
		Iov v = { b, (unsigned long)n }; unsigned long put = 0;
		if (wasi_fd_write(fd, &v, 1, &put) || put == 0) return;
		b += put; n -= (int)put;
	}
}

static unsigned char *mbase;
static UCell mcap;
Cell *micro_mem_alloc(UCell words)
{
	UCell need, pages;
	if (words > ((UCell)-1) / sizeof(Cell)) return 0;
	if (!mbase) mbase = (unsigned char *)((UCell)__builtin_wasm_memory_size(0) * 65536u);
	need = words * sizeof(Cell);
	if (need <= mcap) return (Cell *)mbase;
	pages = (need - mcap + 65535u) / 65536u;
	/* grow in large steps: some node releases crash in a gc that many small grows provoke; the module
	   starts with 128 MB, more than a compiler build needs, so a build never grows */
	if (pages < 1024) pages = 1024;
	if (__builtin_wasm_memory_grow(0, (int)pages) < 0) return 0;
	mcap += pages * 65536u;
	return (Cell *)mbase;
}

int micro_eof(void) { if (in_p >= in_n) fill(); return in_p >= in_n; }
int micro_getc(void) { return micro_eof() ? -1 : ibuf[in_p++]; }
void micro_putc(int c) { if (on == sizeof obuf) { flush_fd(1, obuf, on); on = 0; } obuf[on++] = (unsigned char)c; }
void micro_msg(const char *s) { int n = 0; while (s[n]) n++; flush_fd(2, (unsigned char *)s, n); }

Cell micro_syscall(Cell *mem, Cell op, Cell a, Cell b, Cell c)
{
	switch (op) {
	case 9:
		if (a == 0) return micro_mem_words();
		if (a == 1) return HEAP_LO;
		if (a == 2) return HEAP_HI;
		if (a == 3) return (Cell)micro_mem_words() * 8;
		return -1;
	case 1: {	/* open for reading, relative to the directory preopened as fd 3 (embed reads the library this way) */
		char path[1024]; int n = 0, fd = -1;
		if (b != 0) return -1;
		while (n < 1023 && a + n < (Cell)micro_mem_words() && mem[a + n]) { path[n] = (char)mem[a + n]; n++; }
		path[n] = 0;
		if (wasi_path_open(3, 1, path, (unsigned long)n, 0, 2 /* fd_read */, 0, 0, &fd)) return -1;
		return fd;
	}
	case 2: return a > 2 ? (wasi_fd_close((int)a) ? -1 : 0) : -1;
	case 3: {
		unsigned char t[512]; Cell n = 0; int i;
		if (c < 0 || b < 0 || b + c > (Cell)micro_mem_words()) return -1;
		while (n < c) {
			Iov v = { t, (unsigned long)(c - n > 512 ? 512 : c - n) }; unsigned long got = 0;
			if (a == 0) { while (n < c && !micro_eof()) mem[b + n++] = micro_getc(); break; }
			if (wasi_fd_read((int)a, &v, 1, &got) || got == 0) break;
			for (i = 0; i < (int)got; i++) mem[b + n + i] = t[i];
			n += (Cell)got;
		}
		return n;
	}
	case 4: {
		unsigned char t[512]; Cell n = 0; int i, k;
		if (c < 0 || b < 0 || b + c > (Cell)micro_mem_words() || (a != 1 && a != 2)) return -1;
		if (a == 1) { flush_fd(1, obuf, on); on = 0; }
		while (n < c) { k = c - n > 512 ? 512 : (int)(c - n); for (i = 0; i < k; i++) t[i] = (unsigned char)mem[b + n + i]; flush_fd((int)a, t, k); n += k; }
		return n;
	}
	case 21: return 1;
	case 23: return 0;
	}
	return -1;
}

void __multi3(unsigned long long *r, unsigned long long al, unsigned long long ah, unsigned long long bl, unsigned long long bh)
{
	unsigned long long a0 = al & 0xffffffffu, a1 = al >> 32, b0 = bl & 0xffffffffu, b1 = bl >> 32;
	unsigned long long p00 = a0 * b0, p01 = a0 * b1, p10 = a1 * b0, p11 = a1 * b1;
	unsigned long long mid = (p00 >> 32) + (p01 & 0xffffffffu) + (p10 & 0xffffffffu);
	r[0] = (p00 & 0xffffffffu) | (mid << 32);
	r[1] = p11 + (p01 >> 32) + (p10 >> 32) + (mid >> 32) + al * bh + ah * bl;
}

/* an input that starts with "ZCIMAGE\n" and a little-endian 64-bit byte count carries
   the image to boot ahead of the program: the bootstrap uses it to run each stage's
   compiler in this same module. any other input boots the embedded seed genome. */
int micro_port_image(Cell *mem, UCell org, UCell end)
{
	static const char tag[] = "ZCIMAGE\n";
	UCell n = 0, i, w; int k;
	if (micro_eof() || in_n - in_p < 16) return 0;
	for (k = 0; k < 8; k++) if (ibuf[in_p + k] != (unsigned char)tag[k]) return 0;
	in_p += 8;
	for (k = 0; k < 8; k++) n |= (UCell)micro_getc() << (8 * k);
	if (n % 8 || org + n / 8 > end) return -1;
	for (i = 0; i < n / 8; i++) {
		w = 0;
		for (k = 0; k < 8; k++) { int c = micro_getc(); if (c < 0) return -1; w |= (UCell)c << (8 * k); }
		mem[org + i] = (Cell)w;
	}
	return 1;
}

void _start(void)
{
	int r = micro_run();
	flush_fd(1, obuf, on); on = 0;
	if (r) wasi_proc_exit(r);
}