//! First user program: says hello, uses the VFS through syscalls, and checks //! that bad syscall arguments get the right errors. Exit code = failed checks. #![no_std] #![no_main] #[macro_use] #[path = "ulib.rs"] mod ulib; use ulib::*; fn check(failures: &mut i64, ok: bool, what: &str) { if !ok { println!("[hello] CHECK FAILED: {}", what); *failures += 1; } } fn main() -> i64 { let mut failures = 0; println!("Hello from user mode! I'm pid {}.", getpid()); // Read a file from the ramfs. let mut buf = [0u8; 128]; let fd = open("/hello.txt", 0); check(&mut failures, fd >= 3, "open /hello.txt"); let n = read(fd, &mut buf); let text = core::str::from_utf8(&buf[..n.max(0) as usize]).unwrap_or(""); print!("[hello] /hello.txt says: {}", text); check(&mut failures, text.starts_with("Hello from the ramfs!"), "contents of /hello.txt"); check(&mut failures, read(fd, &mut buf) == 0, "read at EOF returns 0"); check(&mut failures, close(fd) == 0, "close"); // Create a file on the second mount (/tmp), then read it back. let fd = open("/tmp/note.txt", O_WRONLY | O_CREAT); check(&mut failures, write(fd, b"written from ring 3") == 19, "write /tmp/note.txt"); close(fd); let fd = open("/tmp/note.txt", 0); let n = read(fd, &mut buf); check(&mut failures, &buf[..n.max(0) as usize] == b"written from ring 3", "read back /tmp/note.txt"); close(fd); println!("[hello] wrote and read back /tmp/note.txt"); // Error cases. check(&mut failures, open("/does/not/exist", 0) == -ENOENT, "ENOENT"); check(&mut failures, read(42, &mut buf) == -EBADF, "EBADF"); check(&mut failures, close(fd) == -EBADF, "double close -> EBADF"); // Ask the kernel to print *kernel* memory for us (0x200000 = kernel code). let r = syscall3(SYS_WRITE, 1, 0x20_0000, 16); check(&mut failures, r == -EFAULT, "kernel pointer -> EFAULT"); check(&mut failures, syscall3(999, 0, 0, 0) == -38, "unknown syscall -> ENOSYS"); if failures == 0 { println!("[hello] all syscall checks passed, exiting with 0"); } failures }