//! A tiny "libc" for user programs: syscall wrappers, print!/println!, //! the `_start` entry point and a panic handler. //! //! Each user program includes it with //! #[macro_use] #[path = "ulib.rs"] mod ulib; //! and defines `fn main() -> i64` (the exit code). //! //! User programs are built for the `x86_64-unknown-none` target, which has //! SSE/floating point disabled. That matters: the kernel doesn't save SSE //! registers on a context switch. #![allow(dead_code)] use core::arch::asm; use core::fmt; pub const SYS_READ: u64 = 0; pub const SYS_WRITE: u64 = 1; pub const SYS_OPEN: u64 = 2; pub const SYS_CLOSE: u64 = 3; pub const SYS_SCHED_YIELD: u64 = 24; pub const SYS_GETPID: u64 = 39; pub const SYS_EXIT: u64 = 60; pub const O_CREAT: u64 = 0o100; pub const ENOENT: i64 = 2; pub const EBADF: i64 = 9; pub const EFAULT: i64 = 14; /// Raw system call: number in rax, args in rdi/rsi/rdx, result in rax. /// The `syscall` instruction overwrites rcx and r11. pub fn syscall3(nr: u64, a1: u64, a2: u64, a3: u64) -> i64 { let ret: i64; unsafe { asm!( "syscall", inlateout("rax") nr as i64 => ret, in("rdi") a1, in("rsi") a2, in("rdx") a3, lateout("rcx") _, lateout("r11") _, options(nostack), ); } ret } pub fn read(fd: i64, buf: &mut [u8]) -> i64 { syscall3(SYS_READ, fd as u64, buf.as_mut_ptr() as u64, buf.len() as u64) } pub fn write(fd: i64, buf: &[u8]) -> i64 { syscall3(SYS_WRITE, fd as u64, buf.as_ptr() as u64, buf.len() as u64) } pub fn open(path: &str, flags: u64) -> i64 { syscall3(SYS_OPEN, path.as_ptr() as u64, path.len() as u64, flags) } pub fn close(fd: i64) -> i64 { syscall3(SYS_CLOSE, fd as u64, 0, 0) } pub fn sched_yield() { syscall3(SYS_SCHED_YIELD, 0, 0, 0); } pub fn getpid() -> i64 { syscall3(SYS_GETPID, 0, 0, 0) } pub fn exit(code: i64) -> ! { syscall3(SYS_EXIT, code as u64, 0, 0); loop {} // not reached } pub struct Stdout; impl fmt::Write for Stdout { fn write_str(&mut self, s: &str) -> fmt::Result { write(1, s.as_bytes()); Ok(()) } } macro_rules! print { ($($arg:tt)*) => {{ use core::fmt::Write; let _ = write!($crate::ulib::Stdout, $($arg)*); }}; } macro_rules! println { ($($arg:tt)*) => {{ use core::fmt::Write; let _ = writeln!($crate::ulib::Stdout, $($arg)*); }}; } /// The kernel jumps here (see process.rs). Run main, exit with its result. #[unsafe(no_mangle)] pub extern "C" fn _start() -> ! { exit(crate::main()) } #[panic_handler] fn panic(info: &core::panic::PanicInfo) -> ! { println!("[user panic] {}", info); exit(101) }