Skip to main content

test_syscalls/
syscalls.rs

1#![no_std]
2#![no_main]
3
4use core::panic::PanicInfo;
5use strat9_syscall::{
6    call, data, error::Error, number, number::*, syscall1, syscall2, syscall3, syscall4, syscall6,
7};
8
9const PAGE_SIZE: usize = 4096;
10
11const PROT_READ: usize = 1;
12const PROT_WRITE: usize = 2;
13const MAP_PRIVATE: usize = 1 << 1;
14const MAP_ANON: usize = 1 << 5;
15const MREMAP_MAYMOVE: usize = 1 << 0;
16const SEEK_SET: usize = 0;
17
18const O_READ: usize = 1 << 0;
19const O_WRITE: usize = 1 << 1;
20const O_CREATE: usize = 1 << 2;
21const O_TRUNC: usize = 1 << 3;
22const O_DIRECTORY: usize = 1 << 5;
23
24struct Ctx {
25    pass: u64,
26    fail: u64,
27}
28
29/// Writes fd.
30fn write_fd(fd: usize, msg: &str) {
31    let _ = call::write(fd, msg.as_bytes());
32}
33
34/// Implements log.
35fn log(msg: &str) {
36    write_fd(1, msg);
37}
38
39/// Implements log err.
40fn log_err(msg: &str) {
41    write_fd(2, msg);
42}
43
44/// Implements log u64.
45fn log_u64(mut value: u64) {
46    let mut buf = [0u8; 21];
47    if value == 0 {
48        log("0");
49        return;
50    }
51    let mut i = buf.len();
52    while value > 0 {
53        i -= 1;
54        buf[i] = b'0' + (value % 10) as u8;
55        value /= 10;
56    }
57    let s = unsafe { core::str::from_utf8_unchecked(&buf[i..]) };
58    log(s);
59}
60
61/// Implements log hex u64.
62fn log_hex_u64(mut value: u64) {
63    let mut buf = [0u8; 16];
64    for i in (0..16).rev() {
65        let nibble = (value & 0xF) as u8;
66        buf[i] = if nibble < 10 {
67            b'0' + nibble
68        } else {
69            b'a' + (nibble - 10)
70        };
71        value >>= 4;
72    }
73    log("0x");
74    let s = unsafe { core::str::from_utf8_unchecked(&buf) };
75    log(s);
76}
77
78/// Implements section.
79fn section(title: &str) {
80    log("\n============================================================\n");
81    log("[test_syscalls] ");
82    log(title);
83    log("\n============================================================\n");
84}
85
86/// Implements ok.
87fn ok(ctx: &mut Ctx, label: &str, value: usize) {
88    ctx.pass += 1;
89    log("[OK] ");
90    log(label);
91    log(" -> ");
92    log_u64(value as u64);
93    log(" (");
94    log_hex_u64(value as u64);
95    log(")\n");
96}
97
98/// Implements fail.
99fn fail(ctx: &mut Ctx, label: &str, err: Error) {
100    ctx.fail += 1;
101    log_err("[FAIL] ");
102    log_err(label);
103    log_err(" -> ");
104    log_err(err.name());
105    log_err("\n");
106}
107
108/// Implements check ok.
109fn check_ok(ctx: &mut Ctx, label: &str, res: core::result::Result<usize, Error>) -> Option<usize> {
110    match res {
111        Ok(v) => {
112            ok(ctx, label, v);
113            Some(v)
114        }
115        Err(e) => {
116            fail(ctx, label, e);
117            None
118        }
119    }
120}
121
122/// Implements check expect one of.
123fn check_expect_one_of(
124    ctx: &mut Ctx,
125    label: &str,
126    res: core::result::Result<usize, Error>,
127    e1: Error,
128    e2: Error,
129) {
130    match res {
131        Ok(v) => {
132            if e1 == Error::Unknown(0) || e2 == Error::Unknown(0) {
133                ok(ctx, label, v);
134            } else {
135                ctx.fail += 1;
136                log_err("[FAIL] ");
137                log_err(label);
138                log_err(" -> expected error but got OK=");
139                log_u64(v as u64);
140                log_err("\n");
141            }
142        }
143        Err(e) => {
144            if e == e1 || e == e2 {
145                ok(ctx, label, 0);
146            } else {
147                fail(ctx, label, e);
148            }
149        }
150    }
151}
152
153/// Implements check expect err.
154fn check_expect_err(
155    ctx: &mut Ctx,
156    label: &str,
157    res: core::result::Result<usize, Error>,
158    expected: Error,
159) {
160    match res {
161        Ok(v) => {
162            ctx.fail += 1;
163            log_err("[FAIL] ");
164            log_err(label);
165            log_err(" -> expected ");
166            log_err(expected.name());
167            log_err(" got OK=");
168            log_u64(v as u64);
169            log_err("\n");
170        }
171        Err(e) => {
172            if e == expected {
173                ok(ctx, label, 0);
174            } else {
175                fail(ctx, label, e);
176            }
177        }
178    }
179}
180
181/// Implements test process and ids.
182fn test_process_and_ids(ctx: &mut Ctx) {
183    section("Process IDs / Session / Group / Credentials");
184
185    let _pid = check_ok(ctx, "getpid()", call::getpid()).unwrap_or(0);
186    let _ = check_ok(ctx, "getppid()", call::getppid());
187    let _ = check_ok(ctx, "gettid()", call::gettid());
188    let _ = check_ok(ctx, "getpgid(0)", call::getpgid(0));
189    let _ = check_ok(ctx, "getsid(0)", call::getsid(0));
190    let _ = check_ok(ctx, "setpgid(0,0)", call::setpgid(0, 0));
191    check_expect_one_of(
192        ctx,
193        "setsid()",
194        call::setsid(),
195        Error::PermissionDenied,
196        Error::InvalidArgument,
197    );
198
199    let _ = check_ok(ctx, "raw SYS_GETUID", unsafe { syscall1(SYS_GETUID, 0) });
200    let _ = check_ok(ctx, "raw SYS_GETEUID", unsafe { syscall1(SYS_GETEUID, 0) });
201    let _ = check_ok(ctx, "raw SYS_GETGID", unsafe { syscall1(SYS_GETGID, 0) });
202    let _ = check_ok(ctx, "raw SYS_GETEGID", unsafe { syscall1(SYS_GETEGID, 0) });
203    let cur_uid = unsafe { syscall1(SYS_GETUID, 0) }.unwrap_or(0);
204    let cur_gid = unsafe { syscall1(SYS_GETGID, 0) }.unwrap_or(0);
205    let _ = check_ok(ctx, "raw SYS_SETUID(current uid)", unsafe {
206        syscall1(SYS_SETUID, cur_uid)
207    });
208    let _ = check_ok(ctx, "raw SYS_SETGID(current gid)", unsafe {
209        syscall1(SYS_SETGID, cur_gid)
210    });
211}
212
213/// Implements test memory.
214fn test_memory(ctx: &mut Ctx) {
215    section("Memory: brk / mmap / mprotect / mremap / munmap");
216
217    let base = check_ok(ctx, "brk(0)", call::brk(0)).unwrap_or(0);
218    let grow = base + PAGE_SIZE * 2;
219    let _ = check_ok(ctx, "brk(grow)", call::brk(grow));
220    let _ = check_ok(ctx, "brk(shrink)", call::brk(base));
221
222    let mapped = check_ok(ctx, "SYS_MMAP anon private RW 2 pages", unsafe {
223        syscall6(
224            number::SYS_MMAP,
225            0,
226            PAGE_SIZE * 2,
227            PROT_READ | PROT_WRITE,
228            MAP_PRIVATE | MAP_ANON,
229            0,
230            0,
231        )
232    })
233    .unwrap_or(0);
234
235    if mapped != 0 {
236        let ptr = mapped as *mut u8;
237        unsafe {
238            core::ptr::write_volatile(ptr, 0xAA);
239            core::ptr::write_volatile(ptr.add(PAGE_SIZE), 0xBB);
240        }
241    }
242
243    let _ = check_ok(ctx, "SYS_MPROTECT RO", unsafe {
244        syscall3(number::SYS_MPROTECT, mapped, PAGE_SIZE * 2, PROT_READ)
245    });
246    let _ = check_ok(ctx, "SYS_MPROTECT RW", unsafe {
247        syscall3(
248            number::SYS_MPROTECT,
249            mapped,
250            PAGE_SIZE * 2,
251            PROT_READ | PROT_WRITE,
252        )
253    });
254    let remapped = check_ok(ctx, "SYS_MREMAP grow to 3 pages (MAYMOVE)", unsafe {
255        syscall4(
256            number::SYS_MREMAP,
257            mapped,
258            PAGE_SIZE * 2,
259            PAGE_SIZE * 3,
260            MREMAP_MAYMOVE,
261        )
262    })
263    .unwrap_or(mapped);
264    let _ = check_ok(ctx, "SYS_MREMAP shrink back to 2 pages", unsafe {
265        syscall4(
266            number::SYS_MREMAP,
267            remapped,
268            PAGE_SIZE * 3,
269            PAGE_SIZE * 2,
270            MREMAP_MAYMOVE,
271        )
272    });
273    let _ = check_ok(ctx, "SYS_MUNMAP final", unsafe {
274        syscall2(number::SYS_MUNMAP, remapped, PAGE_SIZE * 2)
275    });
276}
277
278/// Implements test fs.
279fn test_fs(ctx: &mut Ctx) {
280    section("Filesystem and CWD syscalls");
281
282    const DIR: &str = "/tmp/iso_syscalls_suite";
283    const FILE: &str = "/tmp/iso_syscalls_suite/data.txt";
284
285    let _ = unsafe { syscall2(SYS_UNLINK, FILE.as_ptr() as usize, FILE.len()) };
286    let _ = unsafe { syscall2(SYS_RMDIR, DIR.as_ptr() as usize, DIR.len()) };
287
288    let _ = check_ok(ctx, "SYS_MKDIR /tmp/iso_syscalls_suite", unsafe {
289        syscall3(SYS_MKDIR, DIR.as_ptr() as usize, DIR.len(), 0o755)
290    });
291
292    let file_fd = check_ok(ctx, "SYS_OPEN create RW file", unsafe {
293        syscall3(
294            number::SYS_OPEN,
295            FILE.as_ptr() as usize,
296            FILE.len(),
297            O_READ | O_WRITE | O_CREATE | O_TRUNC,
298        )
299    })
300    .unwrap_or(usize::MAX);
301
302    if file_fd != usize::MAX {
303        let msg = b"syscall-iso-test\n";
304        let _ = check_ok(ctx, "SYS_WRITE file", unsafe {
305            syscall3(number::SYS_WRITE, file_fd, msg.as_ptr() as usize, msg.len())
306        });
307        let _ = check_ok(ctx, "SYS_LSEEK file -> 0", unsafe {
308            syscall3(number::SYS_LSEEK, file_fd, 0, SEEK_SET)
309        });
310        let mut buf = [0u8; 32];
311        let _ = check_ok(ctx, "SYS_READ file", unsafe {
312            syscall3(
313                number::SYS_READ,
314                file_fd,
315                buf.as_mut_ptr() as usize,
316                msg.len(),
317            )
318        });
319        let mut st = data::FileStat::zeroed();
320        let _ = check_ok(ctx, "SYS_FSTAT file", unsafe {
321            syscall2(
322                number::SYS_FSTAT,
323                file_fd,
324                &mut st as *mut data::FileStat as usize,
325            )
326        });
327        check_expect_err(
328            ctx,
329            "SYS_FCHMOD expected ENOSYS",
330            unsafe { syscall2(SYS_FCHMOD, file_fd, 0o644) },
331            Error::NotImplemented,
332        );
333        check_expect_err(
334            ctx,
335            "SYS_FTRUNCATE expected ENOSYS",
336            unsafe { syscall2(SYS_FTRUNCATE, file_fd, 4) },
337            Error::NotImplemented,
338        );
339    }
340
341    let dir_fd = check_ok(ctx, "SYS_OPEN dir O_DIRECTORY", unsafe {
342        syscall3(
343            number::SYS_OPEN,
344            DIR.as_ptr() as usize,
345            DIR.len(),
346            O_READ | O_DIRECTORY,
347        )
348    })
349    .unwrap_or(usize::MAX);
350
351    let _ = check_ok(ctx, "SYS_CHDIR dir", unsafe {
352        syscall2(SYS_CHDIR, DIR.as_ptr() as usize, DIR.len())
353    });
354    let mut cwd = [0u8; 128];
355    let _ = check_ok(ctx, "SYS_GETCWD", unsafe {
356        syscall2(SYS_GETCWD, cwd.as_mut_ptr() as usize, cwd.len())
357    });
358    if dir_fd != usize::MAX {
359        let _ = check_ok(ctx, "SYS_FCHDIR back", unsafe {
360            syscall1(SYS_FCHDIR, dir_fd)
361        });
362    }
363
364    let _ = check_ok(ctx, "SYS_UMASK set 022", unsafe {
365        syscall1(SYS_UMASK, 0o022)
366    });
367    check_expect_err(
368        ctx,
369        "SYS_CHMOD expected ENOSYS",
370        unsafe { syscall3(SYS_CHMOD, FILE.as_ptr() as usize, FILE.len(), 0o644) },
371        Error::NotImplemented,
372    );
373    check_expect_err(
374        ctx,
375        "SYS_RENAME expected ENOSYS",
376        unsafe {
377            syscall4(
378                SYS_RENAME,
379                FILE.as_ptr() as usize,
380                FILE.len(),
381                DIR.as_ptr() as usize,
382                DIR.len(),
383            )
384        },
385        Error::NotImplemented,
386    );
387    check_expect_err(
388        ctx,
389        "SYS_LINK expected ENOSYS",
390        unsafe {
391            syscall4(
392                SYS_LINK,
393                FILE.as_ptr() as usize,
394                FILE.len(),
395                DIR.as_ptr() as usize,
396                DIR.len(),
397            )
398        },
399        Error::NotImplemented,
400    );
401    check_expect_err(
402        ctx,
403        "SYS_SYMLINK expected ENOSYS",
404        unsafe {
405            syscall4(
406                SYS_SYMLINK,
407                FILE.as_ptr() as usize,
408                FILE.len(),
409                DIR.as_ptr() as usize,
410                DIR.len(),
411            )
412        },
413        Error::NotImplemented,
414    );
415    let mut rl = [0u8; 64];
416    check_expect_err(
417        ctx,
418        "SYS_READLINK expected ENOSYS",
419        unsafe {
420            syscall4(
421                SYS_READLINK,
422                FILE.as_ptr() as usize,
423                FILE.len(),
424                rl.as_mut_ptr() as usize,
425                rl.len(),
426            )
427        },
428        Error::NotImplemented,
429    );
430
431    if file_fd != usize::MAX {
432        let _ = check_ok(ctx, "SYS_CLOSE file", unsafe {
433            syscall1(number::SYS_CLOSE, file_fd)
434        });
435    }
436    if dir_fd != usize::MAX {
437        let _ = check_ok(ctx, "SYS_CLOSE dir", unsafe {
438            syscall1(number::SYS_CLOSE, dir_fd)
439        });
440    }
441    let _ = check_ok(ctx, "SYS_UNLINK file", unsafe {
442        syscall2(SYS_UNLINK, FILE.as_ptr() as usize, FILE.len())
443    });
444    let _ = check_ok(ctx, "SYS_RMDIR dir", unsafe {
445        syscall2(SYS_RMDIR, DIR.as_ptr() as usize, DIR.len())
446    });
447}
448
449/// Implements test handles.
450fn test_handles(ctx: &mut Ctx) {
451    section("Handle syscalls via semaphore handle");
452
453    let sem = check_ok(ctx, "SYS_SEM_CREATE initial=1", unsafe {
454        syscall1(number::SYS_SEM_CREATE, 1)
455    })
456    .unwrap_or(usize::MAX);
457    if sem == usize::MAX {
458        return;
459    }
460
461    let mut info = data::HandleInfo {
462        resource_type: 0,
463        permissions: 0,
464        resource: 0,
465    };
466    let _ = check_ok(ctx, "handle_info(sem)", call::handle_info(sem, &mut info));
467    log("[info] resource_type=");
468    log_u64(info.resource_type as u64);
469    log(" perms=");
470    log_hex_u64(info.permissions as u64);
471    log(" resource=");
472    log_hex_u64(info.resource);
473    log("\n");
474
475    let _ = check_ok(
476        ctx,
477        "handle_wait(sem immediate)",
478        call::handle_wait_timeout(sem, 0),
479    );
480    let _ = check_ok(ctx, "SYS_SEM_WAIT consume token", unsafe {
481        syscall1(number::SYS_SEM_WAIT, sem)
482    });
483    check_expect_err(
484        ctx,
485        "handle_wait_timeout(sem empty, 1ms) -> ETIMEDOUT",
486        call::handle_wait_timeout(sem, 1_000_000),
487        Error::TimedOut,
488    );
489    let _ = check_ok(ctx, "SYS_SEM_POST produce token", unsafe {
490        syscall1(number::SYS_SEM_POST, sem)
491    });
492    let _ = check_ok(
493        ctx,
494        "handle_wait(sem after post)",
495        call::handle_wait_timeout(sem, 1_000_000),
496    );
497
498    let self_pid = call::getpid().unwrap_or(0);
499    let dup = check_ok(
500        ctx,
501        "handle_grant(self_pid)",
502        call::handle_grant(sem, self_pid),
503    )
504    .unwrap_or(usize::MAX);
505    if dup != usize::MAX {
506        let _ = check_ok(ctx, "handle_revoke(dup)", call::handle_revoke(dup));
507    }
508    match unsafe { syscall1(number::SYS_SEM_CLOSE, sem) } {
509        Ok(v) => ok(ctx, "SYS_SEM_CLOSE(original)", v),
510        Err(Error::NotFound) | Err(Error::BadHandle) => {
511            ok(ctx, "SYS_SEM_CLOSE(original already cleaned)", 0)
512        }
513        Err(e) => fail(ctx, "SYS_SEM_CLOSE(original)", e),
514    }
515}
516
517#[panic_handler]
518/// Implements panic.
519fn panic(_info: &PanicInfo) -> ! {
520    log_err("[test_syscalls] PANIC\n");
521    call::exit(250)
522}
523
524#[no_mangle]
525/// Implements start.
526pub extern "C" fn _start() -> ! {
527    let mut ctx = Ctx { pass: 0, fail: 0 };
528
529    section("Strat9 syscall ISO test suite (verbose + broad coverage)");
530    test_process_and_ids(&mut ctx);
531    test_memory(&mut ctx);
532    test_fs(&mut ctx);
533    test_handles(&mut ctx);
534
535    section("Summary");
536    log("[summary] pass=");
537    log_u64(ctx.pass);
538    log(" fail=");
539    log_u64(ctx.fail);
540    log("\n");
541
542    if ctx.fail == 0 {
543        call::exit(0);
544    } else {
545        call::exit(1);
546    }
547}