Skip to content

Commit 6526ed6

Browse files
Dietmar Rietschclaude
andcommitted
feat(syscall): pwrite64/pwritev + riscv_flush_icache + io_uring_setup
tsc/eslint on node exercise these beyond what `node --version` did: - pwrite64 (68) + pwritev (70): real positional writes (mirror pread64/preadv, delegated to the host FS protocol) — node uses them heavily emitting files. - riscv_flush_icache (259): no-op (the interpreter re-reads guest memory). - io_uring_setup (425): explicit ENOSYS (intentionally unavailable; callers fall back to the thread-pool/epoll path), like socketpair. docs/syscalls.md + nano-syscalls.json updated (83 → 87). Unblocks the catalog typescript/eslint recipes, which the conformance gate correctly flagged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 5bdf731 commit 6526ed6

3 files changed

Lines changed: 166 additions & 0 deletions

File tree

docs/syscalls.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ Syscalls are handled in two ways:
2222
| 65 | readv | Scatter read |
2323
| 66 | writev | Gather write |
2424
| 67 | pread64 | Positional read (does not change file offset) |
25+
| 68 | pwrite64 | Positional write (does not change file offset) |
2526
| 69 | preadv | Positional scatter read |
27+
| 70 | pwritev | Positional gather write |
2628
| 62 | lseek | SEEK_SET, SEEK_CUR, SEEK_END |
2729
| 61 | getdents64 | Directory listing |
2830
| 79 | newfstatat | File/directory stat |
@@ -147,6 +149,8 @@ Sockets use 16KB ring buffers per slot. Connected sockets have peer indices —
147149
| 90 | capget | Reports all capabilities (root) |
148150
| 278 | getrandom | Uses host Math.random() via emscripten_random |
149151
| 167 | prctl | Stub (returns 0) |
152+
| 259 | riscv_flush_icache | No-op (the interpreter re-reads guest memory each step) |
153+
| 425 | io_uring_setup | Intentionally unavailable (ENOSYS) — callers fall back to thread-pool/epoll |
150154

151155
## Virtual Server Exports
152156

src/syscall.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ const SYS_PREAD64: u64 = 67;
3333
const SYS_PWRITE64: u64 = 68;
3434
const SYS_PREADV: u64 = 69;
3535
const SYS_PWRITEV: u64 = 70;
36+
const SYS_RISCV_FLUSH_ICACHE: u64 = 259; // RISC-V: flush icache (no-op on the interpreter)
37+
const SYS_IO_URING_SETUP: u64 = 425; // io_uring: intentionally unavailable (callers fall back)
3638
const SYS_READLINKAT: u64 = 78;
3739
const SYS_NEWFSTATAT: u64 = 79;
3840
const SYS_FSTAT: u64 = 80;
@@ -506,6 +508,8 @@ pub unsafe fn handle(vm: &mut Vm) {
506508
SYS_READV => sys_readv(vm, a0 as i32, a1, a2 as u32),
507509
SYS_PREAD64 => sys_pread64(vm, a0 as i32, a1, a2 as u32, a3),
508510
SYS_PREADV => sys_preadv(vm, a0 as i32, a1, a2 as u32, a3),
511+
SYS_PWRITE64 => sys_pwrite64(vm, a0 as i32, a1, a2 as u32, a3),
512+
SYS_PWRITEV => sys_pwritev(vm, a0 as i32, a1, a2 as u32, a3),
509513

510514
SYS_OPENAT => {
511515
sys_fs_request(vm, SYS_OPENAT as i32, a0 as i32, a1, a2 as i64, a3 as i64);
@@ -651,6 +655,13 @@ pub unsafe fn handle(vm: &mut Vm) {
651655
SYS_RECVFROM => sys_recvfrom(vm, a0 as i32, a1, a2 as u32, a3 as i32),
652656
SYS_SOCKETPAIR => ENOSYS, // not supported
653657

658+
// RISC-V icache flush after JIT codegen — the interpreter re-reads guest
659+
// memory every step, so there is no separate icache to flush. Succeed.
660+
SYS_RISCV_FLUSH_ICACHE => 0,
661+
// io_uring probe at startup (node, V8). Intentionally unavailable so the
662+
// caller falls back to the thread-pool / epoll path. Acknowledged, ENOSYS.
663+
SYS_IO_URING_SETUP => ENOSYS,
664+
654665
_ => {
655666
// Unknown syscall - return ENOSYS
656667
ENOSYS
@@ -889,6 +900,64 @@ unsafe fn sys_pread64(vm: &mut Vm, fd: i32, buf: u64, count: u32, offset: u64) -
889900
/// preadv(fd, iov, iovcnt, offset) - read into scatter buffers at explicit offset.
890901
/// For file FDs, dispatches each iovec buffer as an FS read with the given offset.
891902
/// Does NOT update the file position (unlike readv).
903+
// Positional write — the mirror of sys_pread64. Writes at an explicit offset
904+
// without moving the FD cursor (node uses this heavily when emitting files).
905+
unsafe fn sys_pwrite64(vm: &mut Vm, fd: i32, buf: u64, count: u32, offset: u64) -> i64 {
906+
if fd < 0 || fd >= MAX_FDS as i32 {
907+
return EBADF;
908+
}
909+
let fd_type = vm.fd_table[fd as usize].fd_type;
910+
911+
match fd_type {
912+
FD_TYPE_FILE => {
913+
// Delegate to host via FS protocol with SYS_PWRITE64 marker.
914+
// arg1 = count, arg2 = explicit offset (the FD cursor is left alone).
915+
vm.fs_request.syscall_nr = SYS_PWRITE64 as i32;
916+
vm.fs_request.fd = fd;
917+
vm.fs_request.buf_ptr = buf as u32;
918+
vm.fs_request.buf_len = count;
919+
vm.fs_request.arg1 = count as i64;
920+
vm.fs_request.arg2 = offset as i64;
921+
vm.status = STATUS_FS_PENDING;
922+
0
923+
}
924+
FD_TYPE_DEVNULL => count as i64, // /dev/null: pretend the whole buffer was written
925+
_ => EBADF,
926+
}
927+
}
928+
929+
// Positional vectored write — the mirror of sys_preadv (first non-empty iovec).
930+
unsafe fn sys_pwritev(vm: &mut Vm, fd: i32, iov: u64, iovcnt: u32, offset: u64) -> i64 {
931+
if fd < 0 || fd >= MAX_FDS as i32 {
932+
return EBADF;
933+
}
934+
let fd_type = vm.fd_table[fd as usize].fd_type;
935+
936+
match fd_type {
937+
FD_TYPE_FILE => {
938+
let mut i: u32 = 0;
939+
while i < iovcnt {
940+
let iov_base = mem::read_u64(vm.ram_base, iov + (i as u64) * 16);
941+
let iov_len = mem::read_u64(vm.ram_base, iov + (i as u64) * 16 + 8) as u32;
942+
if iov_len > 0 {
943+
vm.fs_request.syscall_nr = SYS_PWRITEV as i32;
944+
vm.fs_request.fd = fd;
945+
vm.fs_request.buf_ptr = iov_base as u32;
946+
vm.fs_request.buf_len = iov_len;
947+
vm.fs_request.arg1 = iov_len as i64;
948+
vm.fs_request.arg2 = offset as i64;
949+
vm.status = STATUS_FS_PENDING;
950+
return 0; // host fills the result
951+
}
952+
i += 1;
953+
}
954+
0
955+
}
956+
FD_TYPE_DEVNULL => 0,
957+
_ => EBADF,
958+
}
959+
}
960+
892961
unsafe fn sys_preadv(vm: &mut Vm, fd: i32, iov: u64, iovcnt: u32, offset: u64) -> i64 {
893962
if fd < 0 || fd >= MAX_FDS as i32 {
894963
return EBADF;

wasm/nano-syscalls.json

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
{
2+
"nano_version": "0.1.0",
3+
"source": "docs/syscalls.md",
4+
"supported": [
5+
17,
6+
19,
7+
20,
8+
21,
9+
22,
10+
23,
11+
24,
12+
25,
13+
29,
14+
34,
15+
35,
16+
48,
17+
49,
18+
56,
19+
57,
20+
59,
21+
61,
22+
62,
23+
63,
24+
64,
25+
65,
26+
66,
27+
67,
28+
68,
29+
69,
30+
70,
31+
73,
32+
78,
33+
79,
34+
80,
35+
85,
36+
86,
37+
88,
38+
90,
39+
93,
40+
94,
41+
96,
42+
98,
43+
101,
44+
113,
45+
114,
46+
123,
47+
124,
48+
129,
49+
130,
50+
131,
51+
132,
52+
134,
53+
135,
54+
160,
55+
166,
56+
167,
57+
172,
58+
173,
59+
174,
60+
175,
61+
176,
62+
177,
63+
178,
64+
179,
65+
198,
66+
199,
67+
200,
68+
201,
69+
202,
70+
203,
71+
204,
72+
205,
73+
206,
74+
207,
75+
208,
76+
209,
77+
210,
78+
214,
79+
215,
80+
216,
81+
220,
82+
222,
83+
226,
84+
233,
85+
242,
86+
259,
87+
261,
88+
276,
89+
278,
90+
291,
91+
425
92+
]
93+
}

0 commit comments

Comments
 (0)