//! System call use simple_filesystem::{INode, file::File, FileInfo, FileType, FsError}; use core::{slice, str}; use alloc::{sync::Arc, vec::Vec, string::String}; use spin::Mutex; use log::*; use bitflags::bitflags; use crate::arch::interrupt::TrapFrame; use crate::process::*; use crate::thread; use crate::util; /// System call dispatcher pub fn syscall(id: usize, args: [usize; 6], tf: &mut TrapFrame) -> i32 { let ret = match id { // file 100 => sys_open(args[0] as *const u8, args[1]), 101 => sys_close(args[0]), 102 => sys_read(args[0], args[1] as *mut u8, args[2]), 103 => sys_write(args[0], args[1] as *const u8, args[2]), 030 => sys_putc(args[0] as u8 as char), // 104 => sys_seek(), 110 => sys_fstat(args[0], args[1] as *mut Stat), // 111 => sys_fsync(), // 121 => sys_getcwd(), 128 => sys_getdirentry(args[0], args[1] as *mut DirEntry), 130 => sys_dup(args[0], args[1]), // process 001 => sys_exit(args[0] as i32), 002 => sys_fork(tf), 003 => sys_wait(args[0], args[1] as *mut i32), 004 => sys_exec(args[0] as *const u8, args[1] as usize, args[2] as *const *const u8, tf), // 005 => sys_clone(), 010 => sys_yield(), 011 => sys_sleep(args[0]), 012 => sys_kill(args[0]), 017 => sys_get_time(), 018 => sys_getpid(), 255 => sys_lab6_set_priority(args[0]), // memory // 020 => sys_mmap(), // 021 => sys_munmap(), // 022 => sys_shmem(), // 031 => sys_pgdir(), _ => { error!("unknown syscall id: {:#x?}, args: {:x?}", id, args); crate::trap::error(tf); } }; match ret { Ok(code) => code, Err(err) => -(err as i32), } } fn sys_read(fd: usize, base: *mut u8, len: usize) -> SysResult { // TODO: check ptr info!("read: fd: {}, base: {:?}, len: {:#x}", fd, base, len); let slice = unsafe { slice::from_raw_parts_mut(base, len) }; let len = get_file(fd)?.lock().read(slice)?; Ok(len as i32) } fn sys_write(fd: usize, base: *const u8, len: usize) -> SysResult { // TODO: check ptr info!("write: fd: {}, base: {:?}, len: {:#x}", fd, base, len); let slice = unsafe { slice::from_raw_parts(base, len) }; let len = get_file(fd)?.lock().write(slice)?; Ok(len as i32) } fn sys_open(path: *const u8, flags: usize) -> SysResult { // TODO: check ptr let path = unsafe { util::from_cstr(path) }; let flags = VfsFlags::from_ucore_flags(flags); info!("open: path: {:?}, flags: {:?}", path, flags); let (fd, inode) = match path { "stdin:" => (0, crate::fs::STDIN.clone() as Arc), "stdout:" => (1, crate::fs::STDOUT.clone() as Arc), _ => { let fd = (3..).find(|i| !process().files.contains_key(i)).unwrap(); let inode = crate::fs::ROOT_INODE.lookup(path)?; (fd, inode) } }; let file = File::new(inode, flags.contains(VfsFlags::READABLE), flags.contains(VfsFlags::WRITABLE)); process().files.insert(fd, Arc::new(Mutex::new(file))); Ok(fd as i32) } fn sys_close(fd: usize) -> SysResult { info!("close: fd: {:?}", fd); match process().files.remove(&fd) { Some(_) => Ok(0), None => Err(SysError::Badf), } } fn sys_fstat(fd: usize, stat_ptr: *mut Stat) -> SysResult { // TODO: check ptr info!("fstat: {}", fd); let file = get_file(fd)?; let stat = Stat::from(file.lock().info()?); unsafe { stat_ptr.write(stat); } Ok(0) } /// entry_id = dentry.offset / 256 /// dentry.name = entry_name /// dentry.offset += 256 fn sys_getdirentry(fd: usize, dentry_ptr: *mut DirEntry) -> SysResult { // TODO: check ptr info!("getdirentry: {}", fd); let file = get_file(fd)?; let dentry = unsafe { &mut *dentry_ptr }; if !dentry.check() { return Err(SysError::Inval); } let info = file.lock().info()?; if info.type_ != FileType::Dir || info.size <= dentry.entry_id() { return Err(SysError::Inval); } let name = file.lock().get_entry(dentry.entry_id())?; dentry.set_name(name.as_str()); Ok(0) } fn sys_dup(fd1: usize, fd2: usize) -> SysResult { info!("dup: {} {}", fd1, fd2); let file = get_file(fd1)?; if process().files.contains_key(&fd2) { return Err(SysError::Badf); } process().files.insert(fd2, file.clone()); Ok(0) } /// Fork the current process. Return the child's PID. fn sys_fork(tf: &TrapFrame) -> SysResult { let context = process().fork(tf); //memory_set_map_swappable(context.get_memory_set_mut()); let pid = processor().manager().add(context, thread::current().id()); //memory_set_map_swappable(processor.get_context_mut(pid).get_memory_set_mut()); info!("fork: {} -> {}", thread::current().id(), pid); Ok(pid as i32) } /// Wait the process exit. /// Return the PID. Store exit code to `code` if it's not null. fn sys_wait(pid: usize, code: *mut i32) -> SysResult { // TODO: check ptr loop { use alloc::vec; let wait_procs = match pid { 0 => processor().manager().get_children(thread::current().id()), _ => vec![pid], }; if wait_procs.is_empty() { return Ok(-1); } for pid in wait_procs { match processor().manager().get_status(pid) { Some(Status::Exited(exit_code)) => { if !code.is_null() { unsafe { code.write(exit_code as i32); } } processor().manager().remove(pid); info!("wait: {} -> {}", thread::current().id(), pid); return Ok(0); } None => return Ok(-1), _ => {} } } info!("wait: {} -> {}, sleep", thread::current().id(), pid); if pid == 0 { processor().manager().wait_child(thread::current().id()); processor().yield_now(); } else { processor().manager().wait(thread::current().id(), pid); processor().yield_now(); } } } fn sys_exec(name: *const u8, argc: usize, argv: *const *const u8, tf: &mut TrapFrame) -> SysResult { // TODO: check ptr let name = if name.is_null() { "" } else { unsafe { util::from_cstr(name) } }; info!("exec: {:?}, argc: {}, argv: {:?}", name, argc, argv); // Copy args to kernel let args: Vec = unsafe { slice::from_raw_parts(argv, argc).iter() .map(|&arg| String::from(util::from_cstr(arg))) .collect() }; // Read program file let path = args[0].as_str(); let inode = crate::fs::ROOT_INODE.lookup(path)?; let size = inode.info()?.size; let mut buf = Vec::with_capacity(size); unsafe { buf.set_len(size); } inode.read_at(0, buf.as_mut_slice())?; // Make new Context let iter = args.iter().map(|s| s.as_str()); let mut context = Process::new_user(buf.as_slice(), iter); // Activate new page table unsafe { context.memory_set.activate(); } // Modify the TrapFrame *tf = unsafe { context.arch.get_init_tf() }; // Swap Context but keep KStack ::core::mem::swap(&mut process().kstack, &mut context.kstack); ::core::mem::swap(process(), &mut *context); Ok(0) } fn sys_yield() -> SysResult { thread::yield_now(); Ok(0) } /// Kill the process fn sys_kill(pid: usize) -> SysResult { info!("{} killed: {}", thread::current().id(), pid); processor().manager().exit(pid, 0x100); if pid == thread::current().id() { processor().yield_now(); } Ok(0) } /// Get the current process id fn sys_getpid() -> SysResult { Ok(thread::current().id() as i32) } /// Exit the current process fn sys_exit(exit_code: i32) -> SysResult { let pid = thread::current().id(); info!("exit: {}, code: {}", pid, exit_code); processor().manager().exit(pid, exit_code as usize); processor().yield_now(); unreachable!(); } fn sys_sleep(time: usize) -> SysResult { if time >= 1 << 31 { thread::park(); } else { use core::time::Duration; thread::sleep(Duration::from_millis(time as u64 * 10)); } Ok(0) } fn sys_get_time() -> SysResult { unsafe { Ok(crate::trap::TICK as i32) } } fn sys_lab6_set_priority(priority: usize) -> SysResult { let pid = thread::current().id(); processor().manager().set_priority(pid, priority as u8); Ok(0) } fn sys_putc(c: char) -> SysResult { print!("{}", c); Ok(0) } fn get_file(fd: usize) -> Result<&'static Arc>, SysError> { process().files.get(&fd).ok_or(SysError::Badf) } pub type SysResult = Result; #[repr(i32)] #[derive(Debug)] pub enum SysError { // ucore compatible error code, which is a modified version of the ones used in linux // name conversion E_XXXXX -> SysError::Xxxxx // see https://github.com/oscourse-tsinghua/ucore_plus/blob/master/ucore/src/libs-user-ucore/common/error.h // we only add current used errors here Noent = 2,// No such file or directory Badf = 9,// Invaild fd number Nomem = 12,// Out of memory, also used as no device space in ucore Exist = 17,// File exists Xdev = 18,// Cross-device link Notdir = 20,// Fd is not a directory Isdir = 21,// Fd is a directory Inval = 22,// Invalid argument. Unimp = 35,// Not implemented operation #[allow(dead_code)] Unknown = 65535,// A really really unknown error. } impl From for SysError { fn from(error: FsError) -> Self { match error { FsError::NotSupported => SysError::Unimp, FsError::NotFile => SysError::Isdir, FsError::IsDir => SysError::Isdir, FsError::NotDir => SysError::Notdir, FsError::EntryNotFound => SysError::Noent, FsError::EntryExist => SysError::Exist, FsError::NotSameFs => SysError::Xdev, FsError::InvalidParam => SysError::Inval, FsError::NoDeviceSpace => SysError::Nomem, FsError::DirRemoved => SysError::Noent, FsError::DirNotEmpty => SysError::Isdir,// It should be E_NOTEMPTY in linux, but in ucore it is equal to E_Isdir FsError::WrongFs => SysError::Inval, } } } bitflags! { struct VfsFlags: usize { // WARNING: different from origin uCore const READABLE = 1 << 0; const WRITABLE = 1 << 1; /// create file if it does not exist const CREATE = 1 << 2; /// error if O_CREAT and the file exists const EXCLUSIVE = 1 << 3; /// truncate file upon open const TRUNCATE = 1 << 4; /// append on each write const APPEND = 1 << 5; } } impl VfsFlags { fn from_ucore_flags(f: usize) -> Self { assert_ne!(f & 0b11, 0b11); Self::from_bits_truncate(f + 1) } } #[repr(C)] struct DirEntry { offset: u32, name: [u8; 256], } impl DirEntry { fn check(&self) -> bool { self.offset % 256 == 0 } fn entry_id(&self) -> usize { (self.offset / 256) as usize } fn set_name(&mut self, name: &str) { self.name[..name.len()].copy_from_slice(name.as_bytes()); self.name[name.len()] = 0; self.offset += 256; } } #[repr(C)] struct Stat { /// protection mode and file type mode: StatMode, /// number of hard links nlinks: u32, /// number of blocks file is using blocks: u32, /// file size (bytes) size: u32, } bitflags! { struct StatMode: u32 { const NULL = 0; /// ordinary regular file const FILE = 0o10000; /// directory const DIR = 0o20000; /// symbolic link const LINK = 0o30000; /// character device const CHAR = 0o40000; /// block device const BLOCK = 0o50000; } } impl From for Stat { fn from(info: FileInfo) -> Self { Stat { mode: match info.type_ { FileType::File => StatMode::FILE, FileType::Dir => StatMode::DIR, // _ => StatMode::NULL, //Note: we should mark FileType as #[non_exhaustive] // but it is currently not implemented for enum // see rust-lang/rust#44109 }, nlinks: info.nlinks as u32, blocks: info.blocks as u32, size: info.size as u32, } } }