diff --git a/os/Makefile b/os/Makefile index aa394b0..6289755 100644 --- a/os/Makefile +++ b/os/Makefile @@ -23,6 +23,8 @@ $(bin): kernel asm: $(objdump) -d $(kernel) | less +assembly: + $(objdump) -d $(kernel) > target/os.asm build: $(bin) @@ -34,6 +36,8 @@ qemu: build -machine virt \ -nographic \ -bios default \ - -device loader,file=$(bin),addr=0x80200000 + -device loader,file=$(bin),addr=0x80200000 \ + -drive file=../usr/build/riscv64.img,if=none,format=raw,id=x0 \ + -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 run: build qemu diff --git a/os/src/context.rs b/os/src/context.rs index 5ea0e44..8646109 100644 --- a/os/src/context.rs +++ b/os/src/context.rs @@ -3,6 +3,7 @@ use riscv::register::sstatus; use riscv::register::{scause::Scause, sstatus::Sstatus}; #[repr(C)] +#[derive(Clone)] pub struct TrapFrame { pub x: [usize; 32], // General registers pub sstatus: Sstatus, // Supervisor Status Register @@ -11,7 +12,43 @@ pub struct TrapFrame { pub scause: Scause, // Scause register: record the cause of exception/interrupt/trap } +impl TrapFrame { + /// Constructs TrapFrame for a new kernel thread. + /// + /// The new thread starts at function `entry` with an usize argument `arg`. + /// The stack pointer will be set to `sp`. + fn new_kernel_thread(entry: extern "C" fn(usize) -> !, arg: usize, sp: usize) -> Self { + use core::mem::zeroed; + let mut tf: Self = unsafe { zeroed() }; + tf.x[10] = arg; // a0 + tf.x[2] = sp; + tf.sepc = entry as usize; + tf.sstatus = sstatus::read(); + tf.sstatus.set_spie(true); + tf.sstatus.set_sie(false); + tf.sstatus.set_spp(sstatus::SPP::Supervisor); + tf + } + + /// Constructs TrapFrame for a new user thread. + /// + /// The new thread starts at `entry_addr`. + /// The stack pointer will be set to `sp`. + pub fn new_user_thread(entry_addr: usize, sp: usize) -> Self { + use core::mem::zeroed; + let mut tf: Self = unsafe { zeroed() }; + tf.x[2] = sp; + tf.sepc = entry_addr; + tf.sstatus = sstatus::read(); + tf.sstatus.set_spie(true); + tf.sstatus.set_sie(false); + tf.sstatus.set_spp(sstatus::SPP::User); + tf + } +} + #[repr(C)] +#[derive(Clone)] pub struct Context { pub content_addr: usize, } @@ -31,6 +68,10 @@ impl Context { ContextContent::new_kernel_thread(entry, kstack_top, satp).push_at(kstack_top) } + pub unsafe fn new_fork(tf: &TrapFrame, kstack_top: usize, satp: usize) -> Context { + ContextContent::new_fork(tf, kstack_top, satp) + } + pub unsafe fn append_initial_arguments(&self, args: [usize; 3]) { let contextContent = &mut *(self.content_addr as *mut ContextContent); contextContent.tf.x[10] = args[0]; @@ -98,6 +139,26 @@ impl ContextContent { } } + /// Fork a user process and get the new Context. + /// + /// The stack pointer in kernel mode will be set to `kstack_top`. + /// The SATP register will be set to `satp`. + /// All the other registers are same as the original. + unsafe fn new_fork(tf: &TrapFrame, kstack_top: usize, satp: usize) -> Context { + ContextContent { + ra: __trapret as usize, + satp, + s: [0; 12], + tf: { + let mut tf = tf.clone(); + // fork function's ret value, the new process is 0 + tf.x[10] = 0; // a0 + tf + }, + } + .push_at(kstack_top) + } + unsafe fn push_at(self, stack_top: usize) -> Context { let ptr = (stack_top as *mut ContextContent).sub(1); *ptr = self; diff --git a/os/src/drivers/mod.rs b/os/src/drivers/mod.rs new file mode 100644 index 0000000..2fdb50b --- /dev/null +++ b/os/src/drivers/mod.rs @@ -0,0 +1 @@ +pub mod virtio_disk; diff --git a/os/src/drivers/virtio_disk.rs b/os/src/drivers/virtio_disk.rs new file mode 100644 index 0000000..2a70d61 --- /dev/null +++ b/os/src/drivers/virtio_disk.rs @@ -0,0 +1,434 @@ +use crate::memory::{ + access_pa_via_va, + get_pa_via_va, + paging +}; +use spin::Mutex; +use crate::consts::{ + PAGE_SIZE, + PHYSICAL_MEMORY_OFFSET +}; +use crate::sync::condvar; +use riscv::register::{ + sstatus, + sie +}; +use crate::interrupt::{ + disable_and_store, + restore, + enable_and_wfi, + disable_timer_and_store, + restore_timer, + enable_and_store, +}; + +const VIRTIO_MMIO_0: usize = 0x10001000; +const VIRTIO_MMIO_MAGIC_VALUE: usize = 0x000; +const VIRTIO_MMIO_VERSION: usize = 0x004; +const VIRTIO_MMIO_DEVICE_ID: usize = 0x008; +const VIRTIO_MMIO_VENDOR_ID: usize = 0x00c; +const VIRTIO_MMIO_DEVICE_FEATURES: usize = 0x010; +const VIRTIO_MMIO_DRIVER_FEATURES: usize = 0x020; +const VIRTIO_MMIO_GUEST_PAGE_SIZE: usize = 0x028; +const VIRTIO_MMIO_QUEUE_SEL: usize = 0x030; +const VIRTIO_MMIO_QUEUE_NUM_MAX: usize = 0x034; +const VIRTIO_MMIO_QUEUE_NUM: usize = 0x038; +const VIRTIO_MMIO_QUEUE_ALIGN: usize = 0x03c; +const VIRTIO_MMIO_QUEUE_PFN: usize = 0x040; +const VIRTIO_MMIO_QUEUE_READY: usize = 0x044; +const VIRTIO_MMIO_QUEUE_NOTIFY: usize = 0x050; +const VIRTIO_MMIO_INTERRUPT_STATUS: usize = 0x060; +const VIRTIO_MMIO_INTERRUPT_ACK: usize = 0x064; +const VIRTIO_MMIO_STATUS: usize = 0x070; + +const VIRTIO_MMIO_MAGIC_NUMBER: u32 = 0x74726976; +const VIRTIO_MMIO_VENDOR_NUMBER: u32 = 0x554d4551; + +// VIRTIO_MMIO_STATUS register bits +const VIRTIO_CONFIG_S_ACKNOWLEDGE: u32 = 1; +const VIRTIO_CONFIG_S_DRIVER: u32 = 2; +const VIRTIO_CONFIG_S_DRIVER_OK: u32 = 4; +const VIRTIO_CONFIG_S_FEATURES_OK: u32 = 8; + +// device feature bits +const VIRTIO_BLK_F_RO: usize = 5; +const VIRTIO_BLK_F_SCSI: usize = 7; +const VIRTIO_BLK_F_CONFIG_WCE: usize = 11; +const VIRTIO_BLK_F_MQ: usize = 12; +const VIRTIO_F_ANY_LAYOUT: usize = 27; +const VIRTIO_RING_F_INDIRECT_DESC: usize = 28; +const VIRTIO_RING_F_EVENT_IDX: usize = 29; + +const NUM: usize = 8; + +fn get_reg(offset: usize) -> *mut T { + let p = access_pa_via_va(VIRTIO_MMIO_0 + offset); + p as *mut T +} + +#[no_mangle] +fn reg_read(offset: usize) -> T where T: Copy{ + unsafe { + let p = get_reg::(offset); + *p + } +} + +#[no_mangle] +fn reg_write(offset: usize, v: T) { + unsafe { + let p = get_reg::(offset); + // println!("before reg_write!"); + *p = v; + // println!("after reg_write!"); + } +} + +#[repr(C)] +struct VRingDesc { + addr: u64, + len: u32, + flags: u16, + next: u16, +} +const VRING_DESC_F_NEXT: u16 = 1; +const VRING_DESC_F_WRITE: u16 = 2; + +#[repr(C)] +struct VRingUsedElem { + id: u32, + len: u32, +} + +const VIRTIO_BLK_T_IN: u32 = 0; // read the disk +const VIRTIO_BLK_T_OUT: u32 = 1; // write the disk + +#[repr(C)] +struct UsedArea { + flags: u16, + id: u16, + elems: [VRingUsedElem; NUM], +} + +#[repr(C)] +pub struct Buf { + blockno: u64, + pub data: [u8; 512], + disk: u8, + // sleep_lock: condvar::Condvar, + completed: bool, +} + +impl Buf { + pub fn new(blockno: u64) -> Self { + Buf { + blockno, + data: [0; 512], + disk: 0, + completed: false, + } + } +} + +#[repr(C)] +#[derive(Clone,Copy)] +struct BufInfo { + buf: usize, // va of struct Buf + status: u8, +} + +#[repr(C)] +#[repr(align(4096))] +struct VirtioDisk { + pages: [u8; 2 * PAGE_SIZE], + free: [u8; NUM], + used_idx: u16, + init: bool, + buf_info: [BufInfo; NUM], +} + +#[repr(C)] +struct VirtioBlockOuthdr { + op: u32, + reserved: u32, + sector: u64, +} + +static DISK: Mutex = Mutex::new(VirtioDisk { + pages: [0; 2 * PAGE_SIZE], + free: [0; NUM], + used_idx: 0, + init: false, + buf_info: [BufInfo { buf: 0, status: 0 }; NUM], +}); + +impl VirtioDisk { + fn get_desc_array(&mut self) -> &mut [VRingDesc] { + unsafe { core::slice::from_raw_parts_mut(&mut self.pages[0] as *mut u8 as *mut VRingDesc, NUM) } + } + + fn get_avail_array(&mut self) -> &mut [u16] { + let desc = self.get_desc_array(); + let avail = &mut desc[0] as *mut VRingDesc as usize + NUM * core::mem::size_of::(); + unsafe { core::slice::from_raw_parts_mut(avail as *mut u16, NUM + 2) } + } + + fn get_used(&mut self) -> &mut UsedArea { + let slice = unsafe { core::slice::from_raw_parts_mut(&mut self.pages[PAGE_SIZE] as *mut u8 as *mut UsedArea, 1) }; + &mut slice[0] + } + + fn alloc_desc(&mut self) -> i32 { + let p = (0..NUM).filter(|i| self.free[*i] == 1).next(); + if let Some(a) = p { + self.free[a] = 0; + a as i32 + } else { + -1 + } + } + + fn free_desc(&mut self, i: usize) { + assert!(i < NUM, "free_desc intr 1"); + assert!(self.free[i] == 0, "free_desc intr 2"); + let desc_array = self.get_desc_array(); + desc_array[i].addr = 0; + self.free[i] = 1; + } + + fn free_chain(&mut self, i: usize) { + //let desc_array = self.get_desc_array(); + let desc_array = unsafe { core::slice::from_raw_parts_mut(&mut self.pages[0] as *mut u8 as *mut VRingDesc, NUM) }; + let mut p: usize = i; + loop { + // println!("p = {}", p); + assert!(p < NUM, "free_chain intr 1"); + assert!(self.free[p] == 0, "free_desc intr 2"); + desc_array[p].addr = 0; + self.free[p] = 1; + if desc_array[p].flags & VRING_DESC_F_NEXT != 0 { + p = desc_array[p].next as usize; + } else { + break; + } + } + // println!("free_chain exited!"); + } + + fn alloc_3desc(&mut self, idx: &mut[usize; 3]) -> i32 { + for i in 0..3 { + let id = self.alloc_desc(); + if id < 0 { + for j in 0..i { + self.free_desc(idx[j]); + } + return -1; + } else { + idx[i] = id as usize; + } + } + 0 + } + + #[no_mangle] + fn virtio_disk_issue(&mut self, buf: &mut Buf, write: bool, idx0: &mut usize) { + let sector: u64 = buf.blockno; + let mut idx: [usize; 3] = [0; 3]; + assert!(self.alloc_3desc(&mut idx) == 0); // assuming desc space is sufficient + let virtio_hdr = VirtioBlockOuthdr { + op: if write { VIRTIO_BLK_T_OUT } else { VIRTIO_BLK_T_IN }, + reserved: 0, + sector, + }; + + self.buf_info[idx[0]].buf = buf as *const _ as usize; + self.buf_info[idx[0]].status = 0xf; + let buf_status_va = &self.buf_info[idx[0]].status as *const _ as usize; + + let desc_array = self.get_desc_array(); + desc_array[idx[0]] = VRingDesc { + addr: paging::PageTableImpl::kvmpa(&virtio_hdr as *const _ as usize) as u64, + len: core::mem::size_of::() as u32, + flags: VRING_DESC_F_NEXT, + next: idx[1] as u16 + }; + desc_array[idx[1]] = VRingDesc { + addr: paging::PageTableImpl::kvmpa(&buf.data[0] as *const _ as usize) as u64, + len: 512, + flags: if write { VRING_DESC_F_NEXT } else { VRING_DESC_F_NEXT | VRING_DESC_F_WRITE }, + next: idx[2] as u16 + }; + desc_array[idx[2]] = VRingDesc { + addr: paging::PageTableImpl::kvmpa(buf_status_va) as u64, + len: 1, + flags: VRING_DESC_F_WRITE, + next: 0 + }; + + let avail_array = self.get_avail_array(); + avail_array[2 + avail_array[1] as usize % NUM] = idx[0] as u16; + compiler_memory_barrier(); + avail_array[1] += 1; + compiler_memory_barrier(); + + + buf.disk = 1; + + *idx0 = idx[0]; + reg_write::(VIRTIO_MMIO_QUEUE_NOTIFY, 0x0); + // println!("after writing notify"); + // println!("issue successfully!"); + } + + fn virtio_disk_clean(&mut self, idx: usize) { + self.buf_info[idx].buf = 0; + self.free_chain(idx); + } + + fn virtio_disk_intr(&mut self) { + // println!("into virtio_disk_intr inside!"); + let slice = unsafe { core::slice::from_raw_parts_mut(&mut self.pages[PAGE_SIZE] as *mut u8 as *mut UsedArea, 1) }; + let used = &mut slice[0]; + let num: u16 = NUM as u16; + // println!("self.used_idx = {}", self.used_idx); + // println!("used.id = {}", used.id); + while self.used_idx % num != used.id % num { + let id: usize = used.elems[self.used_idx as usize].id as usize; + // println!("used_length = {}", used.elems[self.used_idx as usize].len); + // println!("status = {}", self.buf_info[id].status); + assert!(self.buf_info[id].status == 0, "virtio_disk_intr status"); + let buf = unsafe { &mut *(self.buf_info[id].buf as *mut u8 as *mut Buf) }; + buf.disk = 0; + // println!("before notify!"); + // buf.sleep_lock.notify(); + buf.completed = true; + // println!("after notify!"); + self.used_idx = (self.used_idx + 1) % num; + } + // println!("exit virtio_disk_intr inside!"); + } +} + +#[no_mangle] +pub fn virtio_disk_rw(buf: &mut Buf, write: bool) { + let status = disable_and_store(); + let stimer = riscv::register::sie::read().stimer(); + unsafe { riscv::register::sie::clear_stimer(); } + // println!("virtio_disk_rw blockno = {}, write = {}", buf.blockno, write); + + let mut idx0: usize = 0; + DISK.lock() + .virtio_disk_issue(buf, write, &mut idx0); + + // buf.sleep_lock.wait(); + + enable_and_store(); + //reg_write::(VIRTIO_MMIO_QUEUE_NOTIFY, 0x0); + + // println!("start waiting..."); + loop { + if buf.completed { + break; + } + } + // println!("end waiting..."); + restore(status); + + DISK.lock() + .virtio_disk_clean(idx0); + + if stimer { + unsafe { riscv::register::sie::set_stimer(); } + } +} + +pub fn init() { + + assert_eq!(reg_read::(VIRTIO_MMIO_MAGIC_VALUE), VIRTIO_MMIO_MAGIC_NUMBER, "magic is wrong!"); + assert_eq!(reg_read::(VIRTIO_MMIO_VERSION), 0x1, "not legacy ver of virtio!"); + assert_eq!(reg_read::(VIRTIO_MMIO_DEVICE_ID), 0x2, "not virtio_blk device!"); + assert_eq!(reg_read::(VIRTIO_MMIO_VENDOR_ID), VIRTIO_MMIO_VENDOR_NUMBER, "vendor id is wrong!"); + println!("virtio_disk found!"); + + let mut status: u32 = 0; + + status |= VIRTIO_CONFIG_S_ACKNOWLEDGE; + reg_write::(VIRTIO_MMIO_STATUS, status); + + status |= VIRTIO_CONFIG_S_DRIVER; + reg_write::(VIRTIO_MMIO_STATUS, status); + + let mut features: u32 = reg_read::(VIRTIO_MMIO_DEVICE_FEATURES); + // println!("features = {:#x}", features); + features &= !(1 << VIRTIO_BLK_F_RO); + features &= !(1 << VIRTIO_BLK_F_SCSI); + features &= !(1 << VIRTIO_BLK_F_CONFIG_WCE); + features &= !(1 << VIRTIO_BLK_F_MQ); + features &= !(1 << VIRTIO_F_ANY_LAYOUT); + features &= !(1 << VIRTIO_RING_F_EVENT_IDX); + features &= !(1 << VIRTIO_RING_F_INDIRECT_DESC); + reg_write::(VIRTIO_MMIO_DEVICE_FEATURES, features); + + status |= VIRTIO_CONFIG_S_FEATURES_OK; + reg_write::(VIRTIO_MMIO_STATUS, status); + + status |= VIRTIO_CONFIG_S_DRIVER_OK; + reg_write::(VIRTIO_MMIO_STATUS, status); + + reg_write::(VIRTIO_MMIO_GUEST_PAGE_SIZE, PAGE_SIZE as u32); + + let mut disk = DISK.lock(); + reg_write::(VIRTIO_MMIO_QUEUE_SEL, 0u32); + let max = reg_read::(VIRTIO_MMIO_QUEUE_NUM_MAX); + assert!(max > 0, "virtio disk has no virtqueue 0!"); + assert!(max >= NUM as u32, "virtqueue max size is too small!"); + // assert!(reg_read::(VIRTIO_MMIO_QUEUE_ALIGN) == 4096, "virtqueue alignment {} != 4096!"); + // println!("virtqueue alignment = {}", reg_read::(VIRTIO_MMIO_QUEUE_ALIGN)); + reg_write::(VIRTIO_MMIO_QUEUE_ALIGN, 4096); + reg_write::(VIRTIO_MMIO_QUEUE_NUM, NUM as u32); + reg_write::(VIRTIO_MMIO_QUEUE_PFN, (get_pa_via_va(&disk.pages[0] as *const u8 as usize) / PAGE_SIZE) as u32); + + for i in 0..NUM { + disk.free[i] = 1; + } + disk.init = true; + println!("++++ setup disk! ++++"); +} + +pub fn virtio_disk_intr() { + // println!("into virtio_disk_intr!"); + DISK.lock().virtio_disk_intr(); +} + +pub fn virtio_disk_test() { + // println!("into virtio_disk_test!"); + let mut buf = Buf { + blockno: 100, + data: [0; 512], + disk: 1, + // sleep_lock: condvar::Condvar::new(), + completed: false, + }; + for i in 0..256 { + buf.data[i] = i as u8; + } + virtio_disk_rw(&mut buf, true); + let mut chk_buf = Buf { + blockno: 100, + data: [0; 512], + disk: 1, + completed: false, + }; + virtio_disk_rw(&mut chk_buf, false); + for i in 0..256 { + assert_eq!(chk_buf.data[i], i as u8); + // println!("data[{}] = {}", i, chk_buf.data[i]); + } + println!("passed the disk I/O test!"); + loop {} +} + +fn compiler_memory_barrier() { + //no re-ordering of reads and writes across this point is allowed + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); +} diff --git a/os/src/fs/device.rs b/os/src/fs/device.rs index ab2c42b..e800c8c 100644 --- a/os/src/fs/device.rs +++ b/os/src/fs/device.rs @@ -1,5 +1,6 @@ use rcore_fs::dev::*; use spin::RwLock; +use crate::drivers::virtio_disk; pub struct MemBuf(RwLock<&'static mut [u8]>); @@ -30,3 +31,44 @@ impl Device for MemBuf { Ok(()) } } + +pub struct Disk; +impl Disk { + pub fn new() -> Self { + Disk {} + } +} + +impl Device for Disk { + fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result { + // println!("read_at offset = {}, buf.len = {}", offset, buf.len()); + let b_sector = offset / 512; + let e_sector = (offset + buf.len() + 511) / 512; + let mut l = offset; + for sector in b_sector..e_sector { + let mut disk_buf = virtio_disk::Buf::new(sector as u64); + virtio_disk::virtio_disk_rw(&mut disk_buf, false); + let mut r = (l / 512 + 1) * 512; + r = core::cmp::min(r, offset + buf.len()); + buf[l - offset..r - offset] + .copy_from_slice(&disk_buf.data[l & 511..{ if r % 512 == 0 { 512 } else {r % 512} }]); + l = r; + } + Ok(buf.len()) + } + fn write_at(&self, offset: usize, buf: &[u8]) -> Result { + // println!("write_at offset = {}, buf.len = {}", offset, buf.len()); + assert!(offset % 512 == 0, "offset alignment panic!"); + assert!(buf.len() % 512 == 0, "buf length alignment panic!"); + for sector in offset / 512..(offset + buf.len()) / 512 { + let mut disk_buf = virtio_disk::Buf::new(sector as u64); + disk_buf.data[0..512] + .copy_from_slice(&buf[sector * 512 - offset..(sector + 1) * 512 - offset]); + virtio_disk::virtio_disk_rw(&mut disk_buf, true); + } + Ok(buf.len()) + } + fn sync(&self) -> Result<()> { + Ok(()) + } +} diff --git a/os/src/fs/mod.rs b/os/src/fs/mod.rs index abfc57b..922cb65 100644 --- a/os/src/fs/mod.rs +++ b/os/src/fs/mod.rs @@ -8,6 +8,7 @@ use rcore_fs_sfs::SimpleFileSystem; lazy_static! { pub static ref ROOT_INODE: Arc = { + /* let device = { extern "C" { fn _user_img_start(); @@ -17,6 +18,8 @@ lazy_static! { let end = _user_img_end as usize; Arc::new(unsafe { device::MemBuf::new(start, end) }) }; + */ + let device = Arc::new(device::Disk::new()); let sfs = SimpleFileSystem::open(device).expect("failed to open SFS"); sfs.root_inode() }; @@ -29,10 +32,12 @@ pub trait INodeExt { impl INodeExt for dyn INode { fn read_as_vec(&self) -> Result> { let size = self.metadata()?.size; + // println!("size = {}", size); let mut buf = Vec::with_capacity(size); unsafe { buf.set_len(size); } + // println!("begin read_at..."); self.read_at(0, buf.as_mut_slice())?; Ok(buf) } diff --git a/os/src/init.rs b/os/src/init.rs index d94231d..9b5e22a 100644 --- a/os/src/init.rs +++ b/os/src/init.rs @@ -14,6 +14,7 @@ pub extern "C" fn rust_main() -> ! { PHYSICAL_MEMORY_END >> 12, ); crate::interrupt::init(); + crate::drivers::virtio_disk::init(); crate::fs::init(); crate::process::init(); crate::timer::init(); diff --git a/os/src/interrupt.rs b/os/src/interrupt.rs index 1edeb16..1916452 100644 --- a/os/src/interrupt.rs +++ b/os/src/interrupt.rs @@ -34,7 +34,8 @@ pub fn init() { pub unsafe fn init_external_interrupt() { let HART0_S_MODE_INTERRUPT_ENABLES: *mut u32 = access_pa_via_va(0x0c00_2080) as *mut u32; const SERIAL: u32 = 0xa; - HART0_S_MODE_INTERRUPT_ENABLES.write_volatile(1 << SERIAL); + const VIRTIO0: u32 = 0x1; + HART0_S_MODE_INTERRUPT_ENABLES.write_volatile(1 << SERIAL | 1 << VIRTIO0); } pub unsafe fn enable_serial_interrupt() { @@ -43,8 +44,20 @@ pub unsafe fn enable_serial_interrupt() { UART16550.add(1).write_volatile(0x01); } +pub fn plic_claim() -> i32 { + let irq = access_pa_via_va(0x0c20_1004) as *const i32; + unsafe { *irq } +} + +pub fn plic_complete(irq: i32) { + unsafe { + *(access_pa_via_va(0x0c20_1004) as *mut i32) = irq; + } +} + #[no_mangle] pub fn rust_trap(tf: &mut TrapFrame) { + // println!("scause = {:#x}", tf.scause.bits()); match tf.scause.cause() { Trap::Exception(Exception::Breakpoint) => breakpoint(&mut tf.sepc), Trap::Interrupt(Interrupt::SupervisorTimer) => super_timer(), @@ -52,7 +65,7 @@ pub fn rust_trap(tf: &mut TrapFrame) { Trap::Exception(Exception::LoadPageFault) => page_fault(tf), Trap::Exception(Exception::StorePageFault) => page_fault(tf), Trap::Exception(Exception::UserEnvCall) => syscall(tf), - Trap::Interrupt(Interrupt::SupervisorExternal) => external(), + Trap::Interrupt(Interrupt::SupervisorExternal) => external(tf), _ => panic!("undefined trap!"), } } @@ -63,9 +76,11 @@ fn breakpoint(sepc: &mut usize) { } fn super_timer() { + // println!("T"); clock_set_next_event(); tick(); } + fn page_fault(tf: &mut TrapFrame) { println!( "{:?} va = {:#x} instruction = {:#x}", @@ -82,8 +97,25 @@ fn syscall(tf: &mut TrapFrame) { tf.x[10] = ret as usize; } -fn external() { - let _ = try_serial(); +fn external(tf: &mut TrapFrame) { + // println!("into external"); + if tf.scause.is_interrupt() && (tf.scause.bits() & 0xff == 0x9) { + // println!("supervisorExternal!"); + let irq = plic_claim(); + // println!("irq = {}", irq); + if irq == 0x01 { + crate::drivers::virtio_disk::virtio_disk_intr(); + } else if irq == 0x0a { + try_serial(); + } else { + // println!("irq = {}", irq); + } + if irq > 0 { + plic_complete(irq); + } + } else { + panic!("unhandled external!"); + } } fn try_serial() -> bool { @@ -99,7 +131,29 @@ fn try_serial() -> bool { None => false, } } - +#[inline(always)] +pub fn disable_timer_and_store() -> usize { + let sie: usize; + let bitmask: usize = 1 << 5; + unsafe { + asm!("csrrc $0, sie, $1" : "=r"(sie) : "r"(bitmask):: "volatile"); + } + sie +} +#[inline(always)] +pub fn restore_timer(sie: usize) { + unsafe { + asm!("csrs sie, $0" :: "r"(sie) :: "volatile"); + } +} +#[inline(always)] +pub fn enable_and_store() -> usize { + let sstatus: usize; + unsafe { + asm!("csrsi sstatus, 1 << 1" : "=r"(sstatus) ::: "volatile"); + } + sstatus +} #[inline(always)] pub fn disable_and_store() -> usize { let sstatus: usize; diff --git a/os/src/lib.rs b/os/src/lib.rs index ad61357..a124c0d 100644 --- a/os/src/lib.rs +++ b/os/src/lib.rs @@ -21,3 +21,4 @@ mod sbi; mod sync; mod syscall; mod timer; +mod drivers; diff --git a/os/src/memory/memory_set/area.rs b/os/src/memory/memory_set/area.rs index b5e0e02..5b6157d 100644 --- a/os/src/memory/memory_set/area.rs +++ b/os/src/memory/memory_set/area.rs @@ -5,10 +5,10 @@ use alloc::boxed::Box; #[derive(Debug, Clone)] pub struct MemoryArea { - start: usize, - end: usize, - handler: Box, - attr: MemoryAttr, + pub start: usize, + pub end: usize, + pub handler: Box, + pub attr: MemoryAttr, } impl MemoryArea { diff --git a/os/src/memory/memory_set/handler.rs b/os/src/memory/memory_set/handler.rs index 3750f24..cf9aa8d 100644 --- a/os/src/memory/memory_set/handler.rs +++ b/os/src/memory/memory_set/handler.rs @@ -11,6 +11,13 @@ pub trait MemoryHandler: Debug + 'static { fn map(&self, pt: &mut PageTableImpl, va: usize, attr: &MemoryAttr); fn unmap(&self, pt: &mut PageTableImpl, va: usize); fn page_copy(&self, pt: &mut PageTableImpl, va: usize, src: usize, length: usize); + fn clone_map( + &self, + pt: &mut PageTableImpl, + src_pt: &mut PageTableImpl, + addr: usize, + attr: &MemoryAttr, + ); } impl Clone for Box { @@ -56,6 +63,15 @@ impl MemoryHandler for Linear { } } } + fn clone_map( + &self, + pt: &mut PageTableImpl, + _src_pt: &mut PageTableImpl, + addr: usize, + attr: &MemoryAttr, + ) { + self.map(pt, addr, attr); + } } #[derive(Debug, Clone)] @@ -79,6 +95,7 @@ impl MemoryHandler for ByFrame { fn unmap(&self, pt: &mut PageTableImpl, va: usize) { pt.unmap(va); } + fn page_copy(&self, pt: &mut PageTableImpl, va: usize, src: usize, length: usize) { let pa = pt.get_entry(va).expect("get pa error!").0.addr().as_usize(); unsafe { @@ -94,4 +111,16 @@ impl MemoryHandler for ByFrame { } } } + + fn clone_map( + &self, + pt: &mut PageTableImpl, + src_pt: &mut PageTableImpl, + addr: usize, + attr: &MemoryAttr, + ) { + self.map(pt, addr, attr); + let data = src_pt.get_page_slice_mut(addr); + pt.get_page_slice_mut(addr).copy_from_slice(data); + } } diff --git a/os/src/memory/memory_set/mod.rs b/os/src/memory/memory_set/mod.rs index 4117256..0ba2263 100644 --- a/os/src/memory/memory_set/mod.rs +++ b/os/src/memory/memory_set/mod.rs @@ -4,11 +4,12 @@ pub mod handler; use crate::consts::*; use crate::memory::access_pa_via_va; -use crate::memory::paging::PageTableImpl; +use crate::memory::paging::{PageRange, PageTableImpl}; use alloc::{boxed::Box, vec::Vec}; use area::MemoryArea; use attr::MemoryAttr; use handler::{Linear, MemoryHandler}; +use riscv::addr::{Page, VirtAddr}; pub struct MemorySet { areas: Vec, @@ -104,8 +105,46 @@ impl MemorySet { Linear::new(offset), None, ); + // PLIC for RISC-V virt machine + self.push_mmio(0x0c00_2000, 0x0c00_3000); + // 16550a UART for RISC-V virt machine + self.push_mmio(0x1000_0000, 0x1000_1000); + // VIRTIO0 for RISC-V virt machine + self.push_mmio(0x1000_1000, 0x1000_2000); + + self.push_mmio(0x0c20_1000, 0x0c20_2000); + } + pub fn push_mmio(&mut self, l: usize, r: usize) { + // check alignment + assert!(l & (PAGE_SIZE - 1) == 0); + assert!(r & (PAGE_SIZE - 1) == 0); + self.push( + access_pa_via_va(l), + access_pa_via_va(r), + MemoryAttr::new(), + Linear::new(PHYSICAL_MEMORY_OFFSET), + None, + ); } pub fn token(&self) -> usize { self.page_table.token() } + pub fn clone(&mut self) -> Self { + let mut new_page_table = PageTableImpl::new_bare(); + let Self { + ref mut page_table, + ref areas, + .. + } = self; + for area in areas.iter() { + for page in PageRange::new(area.start, area.end) { + area.handler + .clone_map(&mut new_page_table, page_table, page, &area.attr); + } + } + MemorySet { + areas: areas.clone(), + page_table: new_page_table, + } + } } diff --git a/os/src/memory/mod.rs b/os/src/memory/mod.rs index c32246b..5cdf737 100644 --- a/os/src/memory/mod.rs +++ b/os/src/memory/mod.rs @@ -39,6 +39,10 @@ fn init_heap() { pub fn access_pa_via_va(pa: usize) -> usize { pa + PHYSICAL_MEMORY_OFFSET } +// only works for pa in main memory +pub fn get_pa_via_va(va: usize) -> usize { + va - PHYSICAL_MEMORY_OFFSET +} pub fn kernel_remap() { let mut memory_set = MemorySet::new(); @@ -54,20 +58,6 @@ pub fn kernel_remap() { Linear::new(PHYSICAL_MEMORY_OFFSET), None, ); - memory_set.push( - access_pa_via_va(0x0c00_2000), - access_pa_via_va(0x0c00_3000), - MemoryAttr::new(), - Linear::new(PHYSICAL_MEMORY_OFFSET), - None, - ); - memory_set.push( - access_pa_via_va(0x1000_0000), - access_pa_via_va(0x1000_1000), - MemoryAttr::new(), - Linear::new(PHYSICAL_MEMORY_OFFSET), - None, - ); unsafe { memory_set.activate(); diff --git a/os/src/memory/paging.rs b/os/src/memory/paging.rs index 9ab62e8..0d7c4d6 100644 --- a/os/src/memory/paging.rs +++ b/os/src/memory/paging.rs @@ -103,6 +103,21 @@ impl PageTableImpl { } } + pub fn kvmpa(va: usize) -> usize { + let root_frame_ppn = Self::active_token() & ((1 << 44) - 1); + let root_frame_pa = root_frame_ppn << 12; + let table = unsafe { &mut *(access_pa_via_va(root_frame_pa) as *mut PageTableEntryArray) }; + let mut pageTable = PageTableImpl { + page_table: Rv39PageTable::new(table, PHYSICAL_MEMORY_OFFSET), + root_frame: Frame::of_addr(PhysAddr::new(root_frame_pa)), + entry: None, + }; + if let Some(pageEntry) = pageTable.get_entry(va) { + return pageEntry.target() + (va & (PAGE_SIZE - 1)); + } + panic!("in kvmpa, given va hasn't been mapped!"); + } + pub fn map(&mut self, va: usize, pa: usize) -> &mut PageEntry { let flags = EF::VALID | EF::READABLE | EF::WRITABLE; let page = Page::of_addr(VirtAddr::new(va)); @@ -157,6 +172,15 @@ impl PageTableImpl { Self::flush_tlb(); } } + + pub fn get_page_slice_mut<'a>(&mut self, addr: usize) -> &'a mut [u8] { + let frame = self + .page_table + .translate_page(Page::of_addr(VirtAddr::new(addr))) + .unwrap(); + let vaddr = frame.start_address().as_usize() + PHYSICAL_MEMORY_OFFSET; + unsafe { core::slice::from_raw_parts_mut(vaddr as *mut u8, 0x1000) } + } } #[derive(Clone, Copy, PartialEq, Eq)] diff --git a/os/src/process/mod.rs b/os/src/process/mod.rs index a97ba07..ee3faab 100644 --- a/os/src/process/mod.rs +++ b/os/src/process/mod.rs @@ -21,19 +21,24 @@ pub fn init() { let idle = Thread::new_kernel(Processor::idle_main as usize); idle.append_initial_arguments([&CPU as *const Processor as usize, 0, 0]); CPU.init(idle, Box::new(thread_pool)); - + // CPU.add_thread(Thread::new_kernel(crate::drivers::virtio_disk::virtio_disk_test as usize)); execute("rust/user_shell", None); println!("++++ setup process! ++++"); } pub fn execute(path: &str, host_tid: Option) -> bool { + // println!("before lookup!"); let find_result = ROOT_INODE.lookup(path); + // println!("after loopup!"); match find_result { Ok(inode) => { + // println!("Ok(inode)!"); let data = inode.read_as_vec().unwrap(); + // println!("ok data!"); let user_thread = unsafe { Thread::new_user(data.as_slice(), host_tid) }; CPU.add_thread(user_thread); + // println!("CPU.add_thread"); true } Err(_) => { @@ -62,6 +67,15 @@ pub fn yield_now() { pub fn wake_up(tid: Tid) { CPU.wake_up(tid); } + pub fn current_tid() -> usize { CPU.current_tid() } + +pub fn add_thread(thread: Box) -> usize { + CPU.add_thread(thread) +} + +pub fn current_thread() -> &'static Box { + CPU.current_thread() +} diff --git a/os/src/process/processor.rs b/os/src/process/processor.rs index 10b76a8..4987599 100644 --- a/os/src/process/processor.rs +++ b/os/src/process/processor.rs @@ -41,8 +41,8 @@ impl Processor { .expect("Processor is not initialized!") } - pub fn add_thread(&self, thread: Box) { - self.inner().pool.add(thread); + pub fn add_thread(&self, thread: Box) -> Tid { + self.inner().pool.add(thread) } pub fn idle_main(&self) -> ! { @@ -70,7 +70,9 @@ impl Processor { pub fn tick(&self) { let inner = self.inner(); if !inner.current.is_none() { + // println!("N"); if inner.pool.tick() { + // println!("P"); let flags = disable_and_store(); inner.current.as_mut().unwrap().1.switch_to(&mut inner.idle); @@ -132,4 +134,8 @@ impl Processor { pub fn current_tid(&self) -> usize { self.inner().current.as_mut().unwrap().0 as usize } + + pub fn current_thread(&self) -> &Box { + &self.inner().current.as_mut().unwrap().1 + } } diff --git a/os/src/process/scheduler.rs b/os/src/process/scheduler.rs index f8c25ef..992c7d0 100644 --- a/os/src/process/scheduler.rs +++ b/os/src/process/scheduler.rs @@ -79,13 +79,9 @@ impl Scheduler for RRScheduler { let tid = self.current; if tid != 0 { self.threads[tid].time -= 1; - if self.threads[tid].time == 0 { - return true; - } else { - return false; - } + return self.threads[tid].time == 0; } - return true; + true } fn exit(&mut self, tid: Tid) { diff --git a/os/src/process/structs.rs b/os/src/process/structs.rs index fbb25aa..aeb87bf 100644 --- a/os/src/process/structs.rs +++ b/os/src/process/structs.rs @@ -1,11 +1,13 @@ use super::{ExitCode, Tid}; use crate::alloc::alloc::{alloc, dealloc, Layout}; use crate::consts::*; -use crate::context::Context; +use crate::context::{Context, ContextContent, TrapFrame}; use crate::memory::memory_set::{attr::MemoryAttr, handler::ByFrame, MemorySet}; use alloc::boxed::Box; +use alloc::sync::Arc; use core::str; use riscv::register::satp; +use spin::Mutex; use xmas_elf::{ header, program::{Flags, SegmentData, Type}, @@ -24,6 +26,7 @@ pub struct Thread { pub context: Context, pub kstack: KernelStack, pub wait: Option, + pub vm: Option>>, } impl Thread { @@ -40,6 +43,7 @@ impl Thread { context: Context::new_kernel_thread(entry, kstack_.top(), satp::read().bits()), kstack: kstack_, wait: None, + vm: None, }) } } @@ -49,6 +53,7 @@ impl Thread { context: Context::null(), kstack: KernelStack::new_empty(), wait: None, + vm: None, }) } @@ -58,13 +63,11 @@ impl Thread { } } - pub unsafe fn new_user(data: &[u8], wait_thread: Option) -> Box { + pub unsafe fn new_user_vm(data: &[u8]) -> (MemorySet, usize, usize) { let elf = ElfFile::new(data).expect("failed to analyse elf!"); match elf.header.pt2.type_().as_type() { - header::Type::Executable => { - // println!("it really a executable!"); - } + header::Type::Executable => {} header::Type::SharedObject => { panic!("shared object is not supported!"); } @@ -87,6 +90,11 @@ impl Thread { ); ustack_top }; + (vm, entry_addr, ustack_top) + } + + pub unsafe fn new_user(data: &[u8], wait_thread: Option) -> Box { + let (vm, entry_addr, ustack_top) = unsafe { Self::new_user_vm(data) }; let kstack = KernelStack::new(); @@ -94,11 +102,27 @@ impl Thread { context: Context::new_user_thread(entry_addr, ustack_top, kstack.top(), vm.token()), kstack: kstack, wait: wait_thread, + vm: Some(Arc::new(Mutex::new(vm))), + }) + } + + /// Fork a new process from current one + pub fn fork(&self, tf: &TrapFrame) -> Box { + let kstack = KernelStack::new(); + let vm = self.vm.as_ref().unwrap().lock().clone(); + let vm_token = vm.token(); + let context = unsafe { Context::new_fork(tf, kstack.top(), vm_token) }; + Box::new(Thread { + context, + kstack, + wait: self.wait.clone(), + vm: Some(Arc::new(Mutex::new(vm))), }) } } pub struct KernelStack(usize); + impl KernelStack { pub fn new() -> Self { let bottom = unsafe { diff --git a/os/src/process/thread_pool.rs b/os/src/process/thread_pool.rs index ade6652..98513fb 100644 --- a/os/src/process/thread_pool.rs +++ b/os/src/process/thread_pool.rs @@ -24,6 +24,7 @@ impl ThreadPool { scheduler, } } + fn alloc_tid(&self) -> Tid { for (i, info) in self.threads.iter().enumerate() { if info.is_none() { @@ -33,13 +34,14 @@ impl ThreadPool { panic!("alloc tid failed!"); } - pub fn add(&mut self, _thread: Box) { + pub fn add(&mut self, _thread: Box) -> Tid { let tid = self.alloc_tid(); self.threads[tid] = Some(ThreadInfo { status: Status::Ready, thread: Some(_thread), }); self.scheduler.push(tid); + return tid; } pub fn acquire(&mut self) -> Option<(Tid, Box)> { diff --git a/os/src/sync/condvar.rs b/os/src/sync/condvar.rs index dc25b8a..169a042 100644 --- a/os/src/sync/condvar.rs +++ b/os/src/sync/condvar.rs @@ -13,6 +13,7 @@ impl Condvar { } pub fn wait(&self) { + // println!("tid_wait = {}", current_tid()); self.wait_queue.lock().push_back(current_tid()); yield_now(); } @@ -20,7 +21,10 @@ impl Condvar { pub fn notify(&self) { let tid = self.wait_queue.lock().pop_front(); if let Some(tid) = tid { + // println!("tid_to_wake_up = {}", tid); wake_up(tid); + } else { + panic!("no threads to wake up!"); } /* yield_now(); */ } diff --git a/os/src/syscall.rs b/os/src/syscall.rs index 97a8ac0..a436e8c 100644 --- a/os/src/syscall.rs +++ b/os/src/syscall.rs @@ -1,13 +1,19 @@ use crate::context::TrapFrame; use crate::process; +use crate::fs::{INodeExt, ROOT_INODE}; +use crate::process::structs::Thread; + +pub const SYS_FORK: usize = 57; +pub const SYS_READ: usize = 63; pub const SYS_WRITE: usize = 64; pub const SYS_EXIT: usize = 93; -pub const SYS_READ: usize = 63; pub const SYS_EXEC: usize = 221; pub fn syscall(id: usize, args: [usize; 3], tf: &mut TrapFrame) -> isize { match id { + SYS_FORK => sys_fork(tf), + SYS_READ => sys_read(args[0], args[1] as *mut u8, args[2]), SYS_WRITE => { print!("{}", args[0] as u8 as char); 0 @@ -16,14 +22,20 @@ pub fn syscall(id: usize, args: [usize; 3], tf: &mut TrapFrame) -> isize { sys_exit(args[0]); 0 } - SYS_READ => sys_read(args[0], args[1] as *mut u8, args[2]), - SYS_EXEC => sys_exec(args[0] as *const u8), + SYS_EXEC => sys_exec(args[0] as *const u8, tf), _ => { panic!("unknown syscall id {}", id); } } } +fn sys_fork(tf: &mut TrapFrame) -> isize { + // println!("forking"); + let new_thread = process::current_thread().fork(tf); + let tid = process::add_thread(new_thread); + tid as isize +} + fn sys_exit(code: usize) { process::exit(code); } @@ -41,10 +53,33 @@ pub unsafe fn from_cstr(s: *const u8) -> &'static str { str::from_utf8(slice::from_raw_parts(s, len)).unwrap() } -fn sys_exec(path: *const u8) -> isize { - let valid = process::execute(unsafe { from_cstr(path) }, Some(process::current_tid())); - if valid { - process::yield_now(); +// fn sys_exec(path: *const u8) -> isize { +// let valid = process::execute(unsafe { from_cstr(path) }, Some(process::current_tid())); +// if valid { +// process::yield_now(); +// } +// return 0; +// } + +fn sys_exec(path: *const u8, tf: &mut TrapFrame) -> isize { + let exec_path = unsafe { from_cstr(path) }; + let find_result = ROOT_INODE.lookup(exec_path); + match find_result { + Ok(inode) => { + let data = inode.read_as_vec().unwrap(); + let (mut vm, entry_addr, ustack_top) = unsafe { Thread::new_user_vm(data.as_slice()) }; + let proc = process::current_thread(); + core::mem::swap(&mut *proc.vm.as_ref().unwrap().lock(), &mut vm); + unsafe { + proc.vm.as_ref().unwrap().lock().activate(); + } + drop(proc); + *tf = TrapFrame::new_user_thread(entry_addr, ustack_top); + 0 + } + Err(_) => { + println!("exec error! cannot find the program {}", exec_path); + -1 + } } - return 0; } diff --git a/usr/Makefile b/usr/Makefile index 09aac95..e54082f 100644 --- a/usr/Makefile +++ b/usr/Makefile @@ -15,7 +15,7 @@ ifeq ($(shell which rcore-fs-fuse),) @cargo install rcore-fs-fuse --git https://github.com/rcore-os/rcore-fs --rev d8d61190 endif -rust: +rust: $(rust_srcs) @cd rust && cargo build @echo targets includes $(rust_targets) @rm -rf $(out_dir)/rust && mkdir -p $(out_dir)/rust diff --git a/usr/rust/src/bin/a_plus_b.rs b/usr/rust/src/bin/a_plus_b.rs new file mode 100644 index 0000000..04e8cd6 --- /dev/null +++ b/usr/rust/src/bin/a_plus_b.rs @@ -0,0 +1,15 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +#[macro_use] +extern crate user; + +#[no_mangle] +pub fn main() -> usize { + let a = 1; + let b = 2; + println!("a + b = {}", a + b); + 0 +} diff --git a/usr/rust/src/bin/exec.rs b/usr/rust/src/bin/exec.rs new file mode 100644 index 0000000..7122129 --- /dev/null +++ b/usr/rust/src/bin/exec.rs @@ -0,0 +1,25 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +#[macro_use] +extern crate user; + +use alloc::string::String; +use user::syscall::{sys_exec, sys_fork}; + +unsafe fn to_cstr(s: &mut String) -> *const u8 { + let ptr = s.as_mut_ptr(); + let len = s.len(); + *ptr.add(len) = 0; + ptr +} + +#[no_mangle] +pub fn main() -> usize { + println!("this is exec_test ^o^"); + sys_exec("/rust/hello_world\0".as_ptr()); + println!("should not arrive here. exec error."); + 0 +} diff --git a/usr/rust/src/bin/fork.rs b/usr/rust/src/bin/fork.rs new file mode 100644 index 0000000..9735115 --- /dev/null +++ b/usr/rust/src/bin/fork.rs @@ -0,0 +1,22 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +#[macro_use] +extern crate user; + +use user::syscall::sys_fork; + +#[no_mangle] +pub fn main() -> usize { + let tid = sys_fork(); + let tid = sys_fork(); + if tid == 0 { + println!("I am child"); + } else { + println!("I am father"); + } + println!("ret tid is: {}", tid); + 0 +} diff --git a/usr/rust/src/bin/user_shell.rs b/usr/rust/src/bin/user_shell.rs index 7c565d7..0fc3072 100644 --- a/usr/rust/src/bin/user_shell.rs +++ b/usr/rust/src/bin/user_shell.rs @@ -11,7 +11,7 @@ const CR: u8 = 0x0du8; use alloc::string::String; use user::io::getc; -use user::syscall::sys_exec; +use user::syscall::{sys_exec, sys_exit, sys_fork}; #[no_mangle] pub fn main() { @@ -25,7 +25,11 @@ pub fn main() { println!(""); if !line.is_empty() { println!("searching for program {}", line); - sys_exec(line.as_ptr()); + if sys_fork() == 0 { + line.push('\0'); + sys_exec(line.as_ptr()); + sys_exit(0); + } line.clear(); } print!(">> "); diff --git a/usr/rust/src/syscall.rs b/usr/rust/src/syscall.rs index afadc6d..6e774f9 100644 --- a/usr/rust/src/syscall.rs +++ b/usr/rust/src/syscall.rs @@ -1,4 +1,5 @@ enum SyscallId { + Fork = 57, Read = 63, Write = 64, Exit = 93, @@ -21,6 +22,10 @@ fn sys_call(syscall_id: SyscallId, arg0: usize, arg1: usize, arg2: usize, arg3: ret } +pub fn sys_fork() -> i64 { + sys_call(SyscallId::Fork, 0, 0, 0, 0) +} + pub fn sys_write(ch: u8) -> i64 { sys_call(SyscallId::Write, ch as usize, 0, 0, 0) }