diff --git a/.github/workflows/doc-and-test.yml b/.github/workflows/doc-and-test.yml index 9fee30e..f4027c2 100644 --- a/.github/workflows/doc-and-test.yml +++ b/.github/workflows/doc-and-test.yml @@ -4,7 +4,7 @@ on: [push] env: CARGO_TERM_COLOR: always - rust_toolchain: nightly-2025-02-18 + rust_toolchain: nightly-2024-01-18 jobs: build-doc: @@ -49,10 +49,7 @@ jobs: - name: Install QEMU run: | sudo apt-get update - sudo apt-get install autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev \ - gawk build-essential bison flex texinfo gperf libtool patchutils bc \ - zlib1g-dev libexpat-dev pkg-config libglib2.0-dev libpixman-1-dev libsdl2-dev libslirp-dev \ - python3 python3-pip ninja-build -y + sudo apt-get install ninja-build -y if [ ! -d qemu-7.0.0 ]; then wget https://download.qemu.org/qemu-7.0.0.tar.xz tar -xf qemu-7.0.0.tar.xz @@ -68,4 +65,4 @@ jobs: - name: Run usertests run: cd os && make run TEST=1 timeout-minutes: 10 - + diff --git a/.gitignore b/.gitignore index 7e20e83..45fffb0 100644 --- a/.gitignore +++ b/.gitignore @@ -3,22 +3,14 @@ !.vscode/settings.json !.devcontainer/devcontainer.json -.idea -**/Cargo.lock **/target/ +**/Cargo.lock os/src/link_app.S os/src/linker.ld os/last-* -os/Cargo.lock os/.gdb_history -user/build -user/target/* -user/.idea/* -user/Cargo.lock -easy-fs/Cargo.lock -easy-fs/target/* -easy-fs-fuse/Cargo.lock -easy-fs-fuse/target/* +os/virt.out tools/ pushall.sh +.vscode/*.log diff --git a/README.md b/README.md index 767cb14..2df01b0 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Here we manually compile and install Qemu 7.0.0. For example, on Ubuntu 18.04: # install dependency packages $ sudo apt install autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev \ gawk build-essential bison flex texinfo gperf libtool patchutils bc \ - zlib1g-dev libexpat-dev pkg-config libglib2.0-dev libpixman-1-dev git tmux python3 python3-pip + zlib1g-dev libexpat-dev pkg-config libglib2.0-dev libpixman-1-dev git tmux python3 python3-pip ninja-build # download Qemu source code $ wget https://download.qemu.org/qemu-7.0.0.tar.xz # extract to qemu-7.0.0/ @@ -64,9 +64,7 @@ $ make -j$(nproc) Then, add following contents to `~/.bashrc`(please adjust these paths according to your environment): ``` -export PATH=$PATH:/home/shinbokuow/Downloads/built/qemu-7.0.0 -export PATH=$PATH:/home/shinbokuow/Downloads/built/qemu-7.0.0/riscv64-softmmu -export PATH=$PATH:/home/shinbokuow/Downloads/built/qemu-7.0.0/riscv64-linux-user +export PATH=$PATH:/path/to/qemu-7.0.0/build ``` Finally, update the current shell: diff --git a/easy-fs-fuse/.gitignore b/easy-fs-fuse/.gitignore deleted file mode 100644 index 79f5db6..0000000 --- a/easy-fs-fuse/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.idea/ -target/ -Cargo.lock diff --git a/easy-fs-fuse/Cargo.toml b/easy-fs-fuse/Cargo.toml index 5c5e68d..d8d4e7f 100644 --- a/easy-fs-fuse/Cargo.toml +++ b/easy-fs-fuse/Cargo.toml @@ -13,4 +13,4 @@ rand = "0.8.0" # [features] # board_qemu = [] -# board_k210 = [] \ No newline at end of file +# board_k210 = [] diff --git a/easy-fs-fuse/src/main.rs b/easy-fs-fuse/src/main.rs index 39b9eaf..dceec15 100644 --- a/easy-fs-fuse/src/main.rs +++ b/easy-fs-fuse/src/main.rs @@ -23,6 +23,10 @@ impl BlockDevice for BlockFile { .expect("Error when seeking!"); assert_eq!(file.write(buf).unwrap(), BLOCK_SZ, "Not a complete block!"); } + + fn handle_irq(&self) { + unimplemented!(); + } } fn main() { @@ -55,11 +59,11 @@ fn easy_fs_pack() -> std::io::Result<()> { .write(true) .create(true) .open(format!("{}{}", target_path, "fs.img"))?; - f.set_len(16 * 2048 * 512).unwrap(); + f.set_len(32 * 2048 * 512).unwrap(); f }))); - // 16MiB, at most 4095 files - let efs = EasyFileSystem::create(block_file, 16 * 2048, 1); + // 32MiB, at most 4095 files + let efs = EasyFileSystem::create(block_file, 32 * 2048, 1); let root_inode = Arc::new(EasyFileSystem::root_inode(&efs)); let apps: Vec<_> = read_dir(src_path) .unwrap() @@ -149,59 +153,3 @@ fn efs_test() -> std::io::Result<()> { Ok(()) } - -#[test] -fn mac_test() -> std::io::Result<()> { - const BLOCK_SZ: usize = 512; - - let block_file = Arc::new(BlockFile(Mutex::new({ - let f = OpenOptions::new() - .read(true) - .write(true) - .create(true) - .open("target/fs_mac.img")?; - f.set_len(8192 * BLOCK_SZ).unwrap(); - f - }))); - - EasyFileSystem::create(block_file.clone(), 4096, 1); - let efs = EasyFileSystem::open(block_file.clone()); - let root_inode = EasyFileSystem::root_inode(&efs); - - root_inode.create("root_file"); - root_inode.create("public_file"); - - let secret_inode = root_inode.find("root_file").unwrap(); - secret_inode.write_at(0, b"TOP SECRET: root only!"); - let public_inode = root_inode.find("public_file").unwrap(); - public_inode.write_at(0, b"This file is public."); - - let check_permission = |user: &str, filename: &str| -> bool { - if filename == "root_file" && user != "root" { - false - } else { - true - } - }; - - let users = ["root", "nonroot"]; - for user in users.iter() { - println!("{} task:", user); - - for filename in ["root_file", "public_file"].iter() { - if check_permission(user, filename) { - let inode = root_inode.find(filename).unwrap(); - let mut buf = [0u8; 128]; - let len = inode.read_at(0, &mut buf); - let content = core::str::from_utf8(&buf[..len]).unwrap(); - println!("{}: Opened successfully"); - } else { - println!("{}: Permission denied"); - } - } - - println!(); - } - - Ok(()) -} diff --git a/easy-fs/src/bitmap.rs b/easy-fs/src/bitmap.rs index 287e848..2cea613 100644 --- a/easy-fs/src/bitmap.rs +++ b/easy-fs/src/bitmap.rs @@ -1,16 +1,16 @@ use super::{get_block_cache, BlockDevice, BLOCK_SZ}; use alloc::sync::Arc; -/// A bitmap block + type BitmapBlock = [u64; 64]; -/// Number of bits in a block + const BLOCK_BITS: usize = BLOCK_SZ * 8; -/// A bitmap + pub struct Bitmap { start_block_id: usize, blocks: usize, } -/// Decompose bits into (block_pos, bits64_pos, inner_pos) +/// Return (block_pos, bits64_pos, inner_pos) fn decomposition(mut bit: usize) -> (usize, usize, usize) { let block_pos = bit / BLOCK_BITS; bit %= BLOCK_BITS; @@ -18,14 +18,13 @@ fn decomposition(mut bit: usize) -> (usize, usize, usize) { } impl Bitmap { - /// A new bitmap from start block id and number of blocks pub fn new(start_block_id: usize, blocks: usize) -> Self { Self { start_block_id, blocks, } } - /// Allocate a new block from a block device + pub fn alloc(&self, block_device: &Arc) -> Option { for block_id in 0..self.blocks { let pos = get_block_cache( @@ -53,7 +52,7 @@ impl Bitmap { } None } - /// Deallocate a block + pub fn dealloc(&self, block_device: &Arc, bit: usize) { let (block_pos, bits64_pos, inner_pos) = decomposition(bit); get_block_cache(block_pos + self.start_block_id, Arc::clone(block_device)) @@ -63,7 +62,7 @@ impl Bitmap { bitmap_block[bits64_pos] -= 1u64 << inner_pos; }); } - /// Get the max number of allocatable blocks + pub fn maximum(&self) -> usize { self.blocks * BLOCK_BITS } diff --git a/easy-fs/src/block_cache.rs b/easy-fs/src/block_cache.rs index 786f9e0..1b3b969 100644 --- a/easy-fs/src/block_cache.rs +++ b/easy-fs/src/block_cache.rs @@ -1,61 +1,15 @@ use super::{BlockDevice, BLOCK_SZ}; -use alloc::boxed::Box; use alloc::collections::VecDeque; use alloc::sync::Arc; -use core::alloc::Layout; -use core::mem::ManuallyDrop; -use core::ptr::{addr_of, addr_of_mut}; -use core::slice; +use alloc::vec; +use alloc::vec::Vec; use lazy_static::*; use spin::Mutex; -/// Use `ManuallyDrop` to ensure data is deallocated with an alignment of `BLOCK_SZ` -struct CacheData(ManuallyDrop>); - -impl CacheData { - pub fn new() -> Self { - let data = unsafe { - let raw = alloc::alloc::alloc(Self::layout()); - Box::from_raw(raw as *mut [u8; BLOCK_SZ]) - }; - Self(ManuallyDrop::new(data)) - } - - fn layout() -> Layout { - Layout::from_size_align(BLOCK_SZ, BLOCK_SZ).unwrap() - } -} - -impl Drop for CacheData { - fn drop(&mut self) { - let ptr = self.0.as_mut_ptr(); - unsafe { alloc::alloc::dealloc(ptr, Self::layout()) }; - } -} - -impl AsRef<[u8]> for CacheData { - fn as_ref(&self) -> &[u8] { - let ptr = self.0.as_ptr() as *const u8; - unsafe { slice::from_raw_parts(ptr, BLOCK_SZ) } - } -} - -impl AsMut<[u8]> for CacheData { - fn as_mut(&mut self) -> &mut [u8] { - let ptr = self.0.as_mut_ptr() as *mut u8; - unsafe { slice::from_raw_parts_mut(ptr, BLOCK_SZ) } - } -} - -/// Cached block inside memory pub struct BlockCache { - /// cached block data - cache: CacheData, - /// underlying block id + cache: Vec, block_id: usize, - /// underlying block device block_device: Arc, - /// whether the block is dirty modified: bool, } @@ -63,8 +17,8 @@ impl BlockCache { /// Load a new BlockCache from disk. pub fn new(block_id: usize, block_device: Arc) -> Self { // for alignment and move effciency - let mut cache = CacheData::new(); - block_device.read_block(block_id, cache.as_mut()); + let mut cache = vec![0u8; BLOCK_SZ]; + block_device.read_block(block_id, &mut cache); Self { cache, block_id, @@ -72,13 +26,9 @@ impl BlockCache { modified: false, } } - /// Get the address of an offset inside the cached block data - fn addr_of_offset(&self, offset: usize) -> *const u8 { - addr_of!(self.cache.as_ref()[offset]) - } - fn addr_of_offset_mut(&mut self, offset: usize) -> *mut u8 { - addr_of_mut!(self.cache.as_mut()[offset]) + fn addr_of_offset(&self, offset: usize) -> usize { + &self.cache[offset] as *const _ as usize } pub fn get_ref(&self, offset: usize) -> &T @@ -87,8 +37,8 @@ impl BlockCache { { let type_size = core::mem::size_of::(); assert!(offset + type_size <= BLOCK_SZ); - let addr = self.addr_of_offset(offset) as *const T; - unsafe { &*addr } + let addr = self.addr_of_offset(offset); + unsafe { &*(addr as *const T) } } pub fn get_mut(&mut self, offset: usize) -> &mut T @@ -98,8 +48,8 @@ impl BlockCache { let type_size = core::mem::size_of::(); assert!(offset + type_size <= BLOCK_SZ); self.modified = true; - let addr = self.addr_of_offset_mut(offset) as *mut T; - unsafe { &mut *addr } + let addr = self.addr_of_offset(offset); + unsafe { &mut *(addr as *mut T) } } pub fn read(&self, offset: usize, f: impl FnOnce(&T) -> V) -> V { @@ -113,8 +63,7 @@ impl BlockCache { pub fn sync(&mut self) { if self.modified { self.modified = false; - self.block_device - .write_block(self.block_id, self.cache.as_ref()); + self.block_device.write_block(self.block_id, &self.cache); } } } @@ -124,7 +73,7 @@ impl Drop for BlockCache { self.sync() } } -/// Use a block cache of 16 blocks + const BLOCK_CACHE_SIZE: usize = 16; pub struct BlockCacheManager { @@ -172,11 +121,10 @@ impl BlockCacheManager { } lazy_static! { - /// The global block cache manager pub static ref BLOCK_CACHE_MANAGER: Mutex = Mutex::new(BlockCacheManager::new()); } -/// Get the block cache corresponding to the given block id and block device + pub fn get_block_cache( block_id: usize, block_device: Arc, @@ -185,7 +133,7 @@ pub fn get_block_cache( .lock() .get_block_cache(block_id, block_device) } -/// Sync all block cache to block device + pub fn block_cache_sync_all() { let manager = BLOCK_CACHE_MANAGER.lock(); for (_, cache) in manager.queue.iter() { diff --git a/easy-fs/src/block_dev.rs b/easy-fs/src/block_dev.rs index 555954a..eb39fbd 100644 --- a/easy-fs/src/block_dev.rs +++ b/easy-fs/src/block_dev.rs @@ -1,9 +1,7 @@ use core::any::Any; -/// Trait for block devices -/// which reads and writes data in the unit of blocks + pub trait BlockDevice: Send + Sync + Any { - ///Read data form block to buffer fn read_block(&self, block_id: usize, buf: &mut [u8]); - ///Write data from buffer to block fn write_block(&self, block_id: usize, buf: &[u8]); + fn handle_irq(&self); } diff --git a/easy-fs/src/efs.rs b/easy-fs/src/efs.rs index e28f6b2..82e95ae 100644 --- a/easy-fs/src/efs.rs +++ b/easy-fs/src/efs.rs @@ -5,22 +5,18 @@ use super::{ use crate::BLOCK_SZ; use alloc::sync::Arc; use spin::Mutex; -///An easy file system on block + pub struct EasyFileSystem { - ///Real device pub block_device: Arc, - ///Inode bitmap pub inode_bitmap: Bitmap, - ///Data bitmap pub data_bitmap: Bitmap, inode_area_start_block: u32, data_area_start_block: u32, } type DataBlock = [u8; BLOCK_SZ]; -/// An easy fs over a block device + impl EasyFileSystem { - /// A data block of block size pub fn create( block_device: Arc, total_blocks: u32, @@ -36,7 +32,7 @@ impl EasyFileSystem { let data_bitmap_blocks = (data_total_blocks + 4096) / 4097; let data_area_blocks = data_total_blocks - data_bitmap_blocks; let data_bitmap = Bitmap::new( - (1 + inode_total_blocks) as usize, + (1 + inode_bitmap_blocks + inode_area_blocks) as usize, data_bitmap_blocks as usize, ); let mut efs = Self { @@ -81,7 +77,7 @@ impl EasyFileSystem { block_cache_sync_all(); Arc::new(Mutex::new(efs)) } - /// Open a block device as a filesystem + pub fn open(block_device: Arc) -> Arc> { // read SuperBlock get_block_cache(0, Arc::clone(&block_device)) @@ -103,7 +99,7 @@ impl EasyFileSystem { Arc::new(Mutex::new(efs)) }) } - /// Get the root inode of the filesystem + pub fn root_inode(efs: &Arc>) -> Inode { let block_device = Arc::clone(&efs.lock().block_device); // acquire efs lock temporarily @@ -111,7 +107,7 @@ impl EasyFileSystem { // release efs lock Inode::new(block_id, block_offset, Arc::clone(efs), block_device) } - /// Get inode by id + pub fn get_disk_inode_pos(&self, inode_id: u32) -> (u32, usize) { let inode_size = core::mem::size_of::(); let inodes_per_block = (BLOCK_SZ / inode_size) as u32; @@ -121,20 +117,20 @@ impl EasyFileSystem { (inode_id % inodes_per_block) as usize * inode_size, ) } - /// Get data block by id + pub fn get_data_block_id(&self, data_block_id: u32) -> u32 { self.data_area_start_block + data_block_id } - /// Allocate a new inode + pub fn alloc_inode(&mut self) -> u32 { self.inode_bitmap.alloc(&self.block_device).unwrap() as u32 } - /// Allocate a data block + /// Return a block ID not ID in the data area. pub fn alloc_data(&mut self) -> u32 { self.data_bitmap.alloc(&self.block_device).unwrap() as u32 + self.data_area_start_block } - /// Deallocate a data block + pub fn dealloc_data(&mut self, block_id: u32) { get_block_cache(block_id as usize, Arc::clone(&self.block_device)) .lock() diff --git a/easy-fs/src/layout.rs b/easy-fs/src/layout.rs index 6daccaf..618484c 100644 --- a/easy-fs/src/layout.rs +++ b/easy-fs/src/layout.rs @@ -3,24 +3,16 @@ use alloc::sync::Arc; use alloc::vec::Vec; use core::fmt::{Debug, Formatter, Result}; -/// Magic number for sanity check const EFS_MAGIC: u32 = 0x3b800001; -/// The max number of direct inodes const INODE_DIRECT_COUNT: usize = 28; -/// The max length of inode name const NAME_LENGTH_LIMIT: usize = 27; -/// The max number of indirect1 inodes const INODE_INDIRECT1_COUNT: usize = BLOCK_SZ / 4; -/// The max number of indirect2 inodes const INODE_INDIRECT2_COUNT: usize = INODE_INDIRECT1_COUNT * INODE_INDIRECT1_COUNT; -/// The upper bound of direct inode index const DIRECT_BOUND: usize = INODE_DIRECT_COUNT; -/// The upper bound of indirect1 inode index const INDIRECT1_BOUND: usize = DIRECT_BOUND + INODE_INDIRECT1_COUNT; -/// The upper bound of indirect2 inode indexs #[allow(unused)] const INDIRECT2_BOUND: usize = INDIRECT1_BOUND + INODE_INDIRECT2_COUNT; -/// Super block of a filesystem + #[repr(C)] pub struct SuperBlock { magic: u32, @@ -44,7 +36,6 @@ impl Debug for SuperBlock { } impl SuperBlock { - /// Initialize a super block pub fn initialize( &mut self, total_blocks: u32, @@ -62,23 +53,20 @@ impl SuperBlock { data_area_blocks, } } - /// Check if a super block is valid using efs magic pub fn is_valid(&self) -> bool { self.magic == EFS_MAGIC } } -/// Type of a disk inode + #[derive(PartialEq)] pub enum DiskInodeType { File, Directory, } -/// A indirect block type IndirectBlock = [u32; BLOCK_SZ / 4]; -/// A data block type DataBlock = [u8; BLOCK_SZ]; -/// A disk inode + #[repr(C)] pub struct DiskInode { pub size: u32, @@ -89,8 +77,7 @@ pub struct DiskInode { } impl DiskInode { - /// Initialize a disk inode, as well as all direct inodes under it - /// indirect1 and indirect2 block are allocated only when they are needed + /// indirect1 and indirect2 block are allocated only when they are needed. pub fn initialize(&mut self, type_: DiskInodeType) { self.size = 0; self.direct.iter_mut().for_each(|v| *v = 0); @@ -98,11 +85,9 @@ impl DiskInode { self.indirect2 = 0; self.type_ = type_; } - /// Whether this inode is a directory pub fn is_dir(&self) -> bool { self.type_ == DiskInodeType::Directory } - /// Whether this inode is a file #[allow(unused)] pub fn is_file(&self) -> bool { self.type_ == DiskInodeType::File @@ -131,12 +116,10 @@ impl DiskInode { } total as u32 } - /// Get the number of data blocks that have to be allocated given the new size of data pub fn blocks_num_needed(&self, new_size: u32) -> u32 { assert!(new_size >= self.size); Self::total_blocks(new_size) - Self::total_blocks(self.size) } - /// Get id of block given inner id pub fn get_block_id(&self, inner_id: u32, block_device: &Arc) -> u32 { let inner_id = inner_id as usize; if inner_id < INODE_DIRECT_COUNT { @@ -161,7 +144,6 @@ impl DiskInode { }) } } - /// Inncrease the size of current disk inode pub fn increase_size( &mut self, new_size: u32, @@ -236,6 +218,7 @@ impl DiskInode { } /// Clear size to zero and return blocks that should be deallocated. + /// /// We will clear the block contents to zero later. pub fn clear_size(&mut self, block_device: &Arc) -> Vec { let mut v: Vec = Vec::new(); @@ -308,7 +291,6 @@ impl DiskInode { self.indirect2 = 0; v } - /// Read data from current disk inode pub fn read_at( &self, offset: usize, @@ -348,8 +330,7 @@ impl DiskInode { } read_size } - /// Write data into current disk inode - /// size must be adjusted properly beforehand + /// File size must be adjusted before. pub fn write_at( &mut self, offset: usize, @@ -388,24 +369,22 @@ impl DiskInode { write_size } } -/// A directory entry + #[repr(C)] pub struct DirEntry { name: [u8; NAME_LENGTH_LIMIT + 1], inode_number: u32, } -/// Size of a directory entry + pub const DIRENT_SZ: usize = 32; impl DirEntry { - /// Create an empty directory entry pub fn empty() -> Self { Self { name: [0u8; NAME_LENGTH_LIMIT + 1], inode_number: 0, } } - /// Crate a directory entry from name and inode number pub fn new(name: &str, inode_number: u32) -> Self { let mut bytes = [0u8; NAME_LENGTH_LIMIT + 1]; bytes[..name.len()].copy_from_slice(name.as_bytes()); @@ -414,20 +393,16 @@ impl DirEntry { inode_number, } } - /// Serialize into bytes pub fn as_bytes(&self) -> &[u8] { unsafe { core::slice::from_raw_parts(self as *const _ as usize as *const u8, DIRENT_SZ) } } - /// Serialize into mutable bytes pub fn as_bytes_mut(&mut self) -> &mut [u8] { unsafe { core::slice::from_raw_parts_mut(self as *mut _ as usize as *mut u8, DIRENT_SZ) } } - /// Get name of the entry pub fn name(&self) -> &str { let len = (0usize..).find(|i| self.name[*i] == 0).unwrap(); core::str::from_utf8(&self.name[..len]).unwrap() } - /// Get inode number of the entry pub fn inode_number(&self) -> u32 { self.inode_number } diff --git a/easy-fs/src/lib.rs b/easy-fs/src/lib.rs index 822c237..fa36e6b 100644 --- a/easy-fs/src/lib.rs +++ b/easy-fs/src/lib.rs @@ -1,14 +1,14 @@ -//!An easy file system isolated from the kernel #![no_std] -#![deny(missing_docs)] + extern crate alloc; + mod bitmap; mod block_cache; mod block_dev; mod efs; mod layout; mod vfs; -/// Use a block size of 512 bytes + pub const BLOCK_SZ: usize = 512; use bitmap::Bitmap; use block_cache::{block_cache_sync_all, get_block_cache}; diff --git a/easy-fs/src/vfs.rs b/easy-fs/src/vfs.rs index 9f90c50..7290fa4 100644 --- a/easy-fs/src/vfs.rs +++ b/easy-fs/src/vfs.rs @@ -6,7 +6,7 @@ use alloc::string::String; use alloc::sync::Arc; use alloc::vec::Vec; use spin::{Mutex, MutexGuard}; -/// Virtual filesystem layer over easy-fs + pub struct Inode { block_id: usize, block_offset: usize, @@ -15,7 +15,7 @@ pub struct Inode { } impl Inode { - /// Create a vfs inode + /// We should not acquire efs lock here. pub fn new( block_id: u32, block_offset: usize, @@ -29,19 +29,19 @@ impl Inode { block_device, } } - /// Call a function over a disk inode to read it + fn read_disk_inode(&self, f: impl FnOnce(&DiskInode) -> V) -> V { get_block_cache(self.block_id, Arc::clone(&self.block_device)) .lock() .read(self.block_offset, f) } - /// Call a function over a disk inode to modify it + fn modify_disk_inode(&self, f: impl FnOnce(&mut DiskInode) -> V) -> V { get_block_cache(self.block_id, Arc::clone(&self.block_device)) .lock() .modify(self.block_offset, f) } - /// Find inode under a disk inode by name + fn find_inode_id(&self, name: &str, disk_inode: &DiskInode) -> Option { // assert it is a directory assert!(disk_inode.is_dir()); @@ -58,7 +58,7 @@ impl Inode { } None } - /// Find inode under current inode by name + pub fn find(&self, name: &str) -> Option> { let fs = self.fs.lock(); self.read_disk_inode(|disk_inode| { @@ -73,7 +73,7 @@ impl Inode { }) }) } - /// Increase the size of a disk inode + fn increase_size( &self, new_size: u32, @@ -90,16 +90,16 @@ impl Inode { } disk_inode.increase_size(new_size, v, &self.block_device); } - /// Create inode under current inode by name + pub fn create(&self, name: &str) -> Option> { let mut fs = self.fs.lock(); - let op = |root_inode: &DiskInode| { + let op = |root_inode: &mut DiskInode| { // assert it is a directory assert!(root_inode.is_dir()); // has the file been created? self.find_inode_id(name, root_inode) }; - if self.read_disk_inode(op).is_some() { + if self.modify_disk_inode(op).is_some() { return None; } // create a new file @@ -138,7 +138,7 @@ impl Inode { ))) // release efs lock automatically by compiler } - /// List inodes under current inode + pub fn ls(&self) -> Vec { let _fs = self.fs.lock(); self.read_disk_inode(|disk_inode| { @@ -155,12 +155,12 @@ impl Inode { v }) } - /// Read data from current inode + pub fn read_at(&self, offset: usize, buf: &mut [u8]) -> usize { let _fs = self.fs.lock(); self.read_disk_inode(|disk_inode| disk_inode.read_at(offset, buf, &self.block_device)) } - /// Write data to current inode + pub fn write_at(&self, offset: usize, buf: &[u8]) -> usize { let mut fs = self.fs.lock(); let size = self.modify_disk_inode(|disk_inode| { @@ -170,7 +170,7 @@ impl Inode { block_cache_sync_all(); size } - /// Clear the data in current inode + pub fn clear(&self) { let mut fs = self.fs.lock(); self.modify_disk_inode(|disk_inode| { diff --git a/figures/logo.png b/figures/logo.png new file mode 100644 index 0000000..84af081 Binary files /dev/null and b/figures/logo.png differ diff --git a/os/Cargo.toml b/os/Cargo.toml index d621557..fde4b6c 100644 --- a/os/Cargo.toml +++ b/os/Cargo.toml @@ -2,7 +2,7 @@ name = "os" version = "0.1.0" authors = ["Yifan Wu "] -edition = "2024" +edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -12,8 +12,12 @@ lazy_static = { version = "1.4.0", features = ["spin_no_std"] } buddy_system_allocator = "0.6" bitflags = "1.2.1" xmas-elf = "0.7.0" +volatile = "0.3" virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "4ee80e5" } +lose-net-stack = { git = "https://github.com/yfblock/lose-net-stack", rev = "db42380" } easy-fs = { path = "../easy-fs" } +embedded-graphics = "0.7.1" +tinybmp = "0.3.1" log = "0.4" sbi-rt = { version = "0.0.2", features = ["legacy"] } diff --git a/os/Makefile b/os/Makefile index ff109f0..b0c3463 100644 --- a/os/Makefile +++ b/os/Makefile @@ -12,6 +12,12 @@ BOARD := qemu SBI ?= rustsbi BOOTLOADER := ../bootloader/$(SBI)-$(BOARD).bin +# GUI +GUI ?= off +ifeq ($(GUI), off) + GUI_OPTION := -display none +endif + # Building mode argument ifeq ($(MODE), release) MODE_ARG := --release @@ -62,17 +68,27 @@ disasm: kernel disasm-vim: kernel @$(OBJDUMP) $(DISASM) $(KERNEL_ELF) > $(DISASM_TMP) - @vim $(DISASM_TMP) + @nvim $(DISASM_TMP) @rm $(DISASM_TMP) run: run-inner QEMU_ARGS := -machine virt \ - -nographic \ -bios $(BOOTLOADER) \ + -serial stdio \ + $(GUI_OPTION) \ -device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) \ -drive file=$(FS_IMG),if=none,format=raw,id=x0 \ - -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 + -device virtio-blk-device,drive=x0 \ + -device virtio-gpu-device \ + -device virtio-keyboard-device \ + -device virtio-mouse-device \ + -device virtio-net-device,netdev=net0 \ + -netdev user,id=net0,hostfwd=udp::6200-:2000,hostfwd=tcp::6201-:80 + +fdt: + @qemu-system-riscv64 -M 128m -machine virt,dumpdtb=virt.out + fdtdump virt.out QEMU_NAME := qemu-system-riscv64 qemu-version-check: @@ -93,4 +109,4 @@ gdbserver: qemu-version-check build gdbclient: @riscv64-unknown-elf-gdb -ex 'file $(KERNEL_ELF)' -ex 'set arch riscv:rv64' -ex 'target remote localhost:1234' -.PHONY: build env kernel clean disasm disasm-vim run-inner fs-img gdbserver gdbclient qemu-version-check +.PHONY: build env kernel clean disasm disasm-vim run-inner fs-img gdbserver gdbclient fdt qemu-version-check diff --git a/os/scripts/qemu-ver-check.sh b/os/scripts/qemu-ver-check.sh old mode 100755 new mode 100644 index 40ef766..8c20456 --- a/os/scripts/qemu-ver-check.sh +++ b/os/scripts/qemu-ver-check.sh @@ -13,7 +13,7 @@ then exit 1 else QEMU_VERSION=$($1 --version|head -n 1|awk '{print $4}') - MAJOR_VERSION=$(echo $QEMU_VERSION | awk -F '.' '{print $1}') + MAJOR_VERSION=$(echo $QEMU_VERSION|cut -c1-1) if [ $MAJOR_VERSION -lt $MINIMUM_MAJOR_VERSION ] then echo "${RED}Error: Required major version of QEMU is ${MINIMUM_MAJOR_VERSION}, " \ diff --git a/os/src/assert/desktop.bmp b/os/src/assert/desktop.bmp new file mode 100644 index 0000000..a754f7a Binary files /dev/null and b/os/src/assert/desktop.bmp differ diff --git a/os/src/assert/file.bmp b/os/src/assert/file.bmp new file mode 100644 index 0000000..4a4e83b Binary files /dev/null and b/os/src/assert/file.bmp differ diff --git a/os/src/assert/folder.bmp b/os/src/assert/folder.bmp new file mode 100644 index 0000000..c8384cb Binary files /dev/null and b/os/src/assert/folder.bmp differ diff --git a/os/src/assert/mouse.bmp b/os/src/assert/mouse.bmp new file mode 100644 index 0000000..22a4613 Binary files /dev/null and b/os/src/assert/mouse.bmp differ diff --git a/os/src/boards/qemu.rs b/os/src/boards/qemu.rs index 275de4d..5daf541 100644 --- a/os/src/boards/qemu.rs +++ b/os/src/boards/qemu.rs @@ -3,7 +3,53 @@ pub const MEMORY_END: usize = 0x8800_0000; pub const MMIO: &[(usize, usize)] = &[ (0x0010_0000, 0x00_2000), // VIRT_TEST/RTC in virt machine - (0x1000_1000, 0x00_1000), // Virtio Block in virt machine + (0x2000000, 0x10000), // core local interrupter (CLINT) + (0xc000000, 0x210000), // VIRT_PLIC in virt machine + (0x10000000, 0x9000), // VIRT_UART0 with GPU in virt machine ]; pub type BlockDeviceImpl = crate::drivers::block::VirtIOBlock; +pub type CharDeviceImpl = crate::drivers::chardev::NS16550a; + +pub const VIRT_PLIC: usize = 0xC00_0000; +pub const VIRT_UART: usize = 0x1000_0000; +#[allow(unused)] +pub const VIRTGPU_XRES: u32 = 1280; +#[allow(unused)] +pub const VIRTGPU_YRES: u32 = 800; + +use crate::drivers::block::BLOCK_DEVICE; +use crate::drivers::chardev::{CharDevice, UART}; +use crate::drivers::plic::{IntrTargetPriority, PLIC}; +use crate::drivers::{KEYBOARD_DEVICE, MOUSE_DEVICE}; + +pub fn device_init() { + use riscv::register::sie; + let mut plic = unsafe { PLIC::new(VIRT_PLIC) }; + let hart_id: usize = 0; + let supervisor = IntrTargetPriority::Supervisor; + let machine = IntrTargetPriority::Machine; + plic.set_threshold(hart_id, supervisor, 0); + plic.set_threshold(hart_id, machine, 1); + //irq nums: 5 keyboard, 6 mouse, 8 block, 10 uart + for intr_src_id in [5usize, 6, 8, 10] { + plic.enable(hart_id, supervisor, intr_src_id); + plic.set_priority(intr_src_id, 1); + } + unsafe { + sie::set_sext(); + } +} + +pub fn irq_handler() { + let mut plic = unsafe { PLIC::new(VIRT_PLIC) }; + let intr_src_id = plic.claim(0, IntrTargetPriority::Supervisor); + match intr_src_id { + 5 => KEYBOARD_DEVICE.handle_irq(), + 6 => MOUSE_DEVICE.handle_irq(), + 8 => BLOCK_DEVICE.handle_irq(), + 10 => UART.handle_irq(), + _ => panic!("unsupported IRQ {}", intr_src_id), + } + plic.complete(0, IntrTargetPriority::Supervisor, intr_src_id); +} diff --git a/os/src/config.rs b/os/src/config.rs index f9a2b7f..d9b9b71 100644 --- a/os/src/config.rs +++ b/os/src/config.rs @@ -1,14 +1,12 @@ -//! Constants used in rCore #[allow(unused)] pub const USER_STACK_SIZE: usize = 4096 * 2; pub const KERNEL_STACK_SIZE: usize = 4096 * 2; -pub const KERNEL_HEAP_SIZE: usize = 0x20_0000; - +pub const KERNEL_HEAP_SIZE: usize = 0x100_0000; pub const PAGE_SIZE: usize = 0x1000; pub const PAGE_SIZE_BITS: usize = 0xc; pub const TRAMPOLINE: usize = usize::MAX - PAGE_SIZE + 1; -pub const TRAP_CONTEXT: usize = TRAMPOLINE - PAGE_SIZE; +pub const TRAP_CONTEXT_BASE: usize = TRAMPOLINE - PAGE_SIZE; pub use crate::board::{CLOCK_FREQ, MEMORY_END, MMIO}; diff --git a/os/src/console.rs b/os/src/console.rs index 5435310..085637b 100644 --- a/os/src/console.rs +++ b/os/src/console.rs @@ -1,5 +1,5 @@ -//! SBI console driver, for text output -use crate::sbi::console_putchar; +use crate::drivers::chardev::CharDevice; +use crate::drivers::chardev::UART; use core::fmt::{self, Write}; struct Stdout; @@ -7,7 +7,7 @@ struct Stdout; impl Write for Stdout { fn write_str(&mut self, s: &str) -> fmt::Result { for c in s.chars() { - console_putchar(c as usize); + UART.write(c as u8); } Ok(()) } @@ -18,17 +18,15 @@ pub fn print(args: fmt::Arguments) { } #[macro_export] -/// print string macro macro_rules! print { ($fmt: literal $(, $($arg: tt)+)?) => { - $crate::console::print(format_args!($fmt $(, $($arg)+)?)); + $crate::console::print(format_args!($fmt $(, $($arg)+)?)) } } #[macro_export] -/// println string macro macro_rules! println { ($fmt: literal $(, $($arg: tt)+)?) => { - $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)); + $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)) } } diff --git a/os/src/drivers/block/virtio_blk.rs b/os/src/drivers/block/virtio_blk.rs index 5097831..fb89084 100644 --- a/os/src/drivers/block/virtio_blk.rs +++ b/os/src/drivers/block/virtio_blk.rs @@ -1,83 +1,87 @@ use super::BlockDevice; -use crate::mm::{ - FrameTracker, PageTable, PhysAddr, PhysPageNum, StepByOne, VirtAddr, frame_alloc, - frame_dealloc, kernel_token, -}; -use crate::sync::UPSafeCell; -use alloc::vec::Vec; -use lazy_static::*; -use virtio_drivers::{Hal, VirtIOBlk, VirtIOHeader}; +use crate::drivers::bus::virtio::VirtioHal; +use crate::sync::{Condvar, UPIntrFreeCell}; +use crate::task::schedule; +use crate::DEV_NON_BLOCKING_ACCESS; +use alloc::collections::BTreeMap; +use virtio_drivers::{BlkResp, RespStatus, VirtIOBlk, VirtIOHeader}; #[allow(unused)] -const VIRTIO0: usize = 0x10001000; +const VIRTIO0: usize = 0x10008000; -pub struct VirtIOBlock(UPSafeCell>); - -lazy_static! { - static ref QUEUE_FRAMES: UPSafeCell> = unsafe { UPSafeCell::new(Vec::new()) }; +pub struct VirtIOBlock { + virtio_blk: UPIntrFreeCell>, + condvars: BTreeMap, } impl BlockDevice for VirtIOBlock { fn read_block(&self, block_id: usize, buf: &mut [u8]) { - self.0 - .exclusive_access() - .read_block(block_id, buf) - .expect("Error when reading VirtIOBlk"); + let nb = *DEV_NON_BLOCKING_ACCESS.exclusive_access(); + if nb { + let mut resp = BlkResp::default(); + let task_cx_ptr = self.virtio_blk.exclusive_session(|blk| { + let token = unsafe { blk.read_block_nb(block_id, buf, &mut resp).unwrap() }; + self.condvars.get(&token).unwrap().wait_no_sched() + }); + schedule(task_cx_ptr); + assert_eq!( + resp.status(), + RespStatus::Ok, + "Error when reading VirtIOBlk" + ); + } else { + self.virtio_blk + .exclusive_access() + .read_block(block_id, buf) + .expect("Error when reading VirtIOBlk"); + } } fn write_block(&self, block_id: usize, buf: &[u8]) { - self.0 - .exclusive_access() - .write_block(block_id, buf) - .expect("Error when writing VirtIOBlk"); + let nb = *DEV_NON_BLOCKING_ACCESS.exclusive_access(); + if nb { + let mut resp = BlkResp::default(); + let task_cx_ptr = self.virtio_blk.exclusive_session(|blk| { + let token = unsafe { blk.write_block_nb(block_id, buf, &mut resp).unwrap() }; + self.condvars.get(&token).unwrap().wait_no_sched() + }); + schedule(task_cx_ptr); + assert_eq!( + resp.status(), + RespStatus::Ok, + "Error when writing VirtIOBlk" + ); + } else { + self.virtio_blk + .exclusive_access() + .write_block(block_id, buf) + .expect("Error when writing VirtIOBlk"); + } + } + fn handle_irq(&self) { + self.virtio_blk.exclusive_session(|blk| { + while let Ok(token) = blk.pop_used() { + self.condvars.get(&token).unwrap().signal(); + } + }); } } impl VirtIOBlock { - #[allow(unused)] pub fn new() -> Self { - unsafe { - Self(UPSafeCell::new( + let virtio_blk = unsafe { + UPIntrFreeCell::new( VirtIOBlk::::new(&mut *(VIRTIO0 as *mut VirtIOHeader)).unwrap(), - )) + ) + }; + let mut condvars = BTreeMap::new(); + let channels = virtio_blk.exclusive_access().virt_queue_size(); + for i in 0..channels { + let condvar = Condvar::new(); + condvars.insert(i, condvar); + } + Self { + virtio_blk, + condvars, } } } - -pub struct VirtioHal; - -impl Hal for VirtioHal { - fn dma_alloc(pages: usize) -> usize { - let mut ppn_base = PhysPageNum(0); - for i in 0..pages { - let frame = frame_alloc().unwrap(); - if i == 0 { - ppn_base = frame.ppn; - } - assert_eq!(frame.ppn.0, ppn_base.0 + i); - QUEUE_FRAMES.exclusive_access().push(frame); - } - let pa: PhysAddr = ppn_base.into(); - pa.0 - } - - fn dma_dealloc(pa: usize, pages: usize) -> i32 { - let pa = PhysAddr::from(pa); - let mut ppn_base: PhysPageNum = pa.into(); - for _ in 0..pages { - frame_dealloc(ppn_base); - ppn_base.step(); - } - 0 - } - - fn phys_to_virt(addr: usize) -> usize { - addr - } - - fn virt_to_phys(vaddr: usize) -> usize { - PageTable::from_token(kernel_token()) - .translate_va(VirtAddr::from(vaddr)) - .unwrap() - .0 - } -} diff --git a/os/src/drivers/bus/mod.rs b/os/src/drivers/bus/mod.rs new file mode 100644 index 0000000..d43f304 --- /dev/null +++ b/os/src/drivers/bus/mod.rs @@ -0,0 +1 @@ +pub mod virtio; diff --git a/os/src/drivers/bus/virtio.rs b/os/src/drivers/bus/virtio.rs new file mode 100644 index 0000000..069d478 --- /dev/null +++ b/os/src/drivers/bus/virtio.rs @@ -0,0 +1,48 @@ +use crate::mm::{ + frame_alloc_more, frame_dealloc, kernel_token, FrameTracker, PageTable, PhysAddr, PhysPageNum, + StepByOne, VirtAddr, +}; +use crate::sync::UPIntrFreeCell; +use alloc::vec::Vec; +use lazy_static::*; +use virtio_drivers::Hal; + +lazy_static! { + static ref QUEUE_FRAMES: UPIntrFreeCell> = + unsafe { UPIntrFreeCell::new(Vec::new()) }; +} + +pub struct VirtioHal; + +impl Hal for VirtioHal { + fn dma_alloc(pages: usize) -> usize { + let trakcers = frame_alloc_more(pages); + let ppn_base = trakcers.as_ref().unwrap().last().unwrap().ppn; + QUEUE_FRAMES + .exclusive_access() + .append(&mut trakcers.unwrap()); + let pa: PhysAddr = ppn_base.into(); + pa.0 + } + + fn dma_dealloc(pa: usize, pages: usize) -> i32 { + let pa = PhysAddr::from(pa); + let mut ppn_base: PhysPageNum = pa.into(); + for _ in 0..pages { + frame_dealloc(ppn_base); + ppn_base.step(); + } + 0 + } + + fn phys_to_virt(addr: usize) -> usize { + addr + } + + fn virt_to_phys(vaddr: usize) -> usize { + PageTable::from_token(kernel_token()) + .translate_va(VirtAddr::from(vaddr)) + .unwrap() + .0 + } +} diff --git a/os/src/drivers/chardev/mod.rs b/os/src/drivers/chardev/mod.rs new file mode 100644 index 0000000..64c168f --- /dev/null +++ b/os/src/drivers/chardev/mod.rs @@ -0,0 +1,17 @@ +mod ns16550a; + +use crate::board::CharDeviceImpl; +use alloc::sync::Arc; +use lazy_static::*; +pub use ns16550a::NS16550a; + +pub trait CharDevice { + fn init(&self); + fn read(&self) -> u8; + fn write(&self, ch: u8); + fn handle_irq(&self); +} + +lazy_static! { + pub static ref UART: Arc = Arc::new(CharDeviceImpl::new()); +} diff --git a/os/src/drivers/chardev/ns16550a.rs b/os/src/drivers/chardev/ns16550a.rs new file mode 100644 index 0000000..7122cec --- /dev/null +++ b/os/src/drivers/chardev/ns16550a.rs @@ -0,0 +1,186 @@ +///! Ref: https://www.lammertbies.nl/comm/info/serial-uart +///! Ref: ns16550a datasheet: https://datasheetspdf.com/pdf-file/605590/NationalSemiconductor/NS16550A/1 +///! Ref: ns16450 datasheet: https://datasheetspdf.com/pdf-file/1311818/NationalSemiconductor/NS16450/1 +use super::CharDevice; +use crate::sync::{Condvar, UPIntrFreeCell}; +use crate::task::schedule; +use alloc::collections::VecDeque; +use bitflags::*; +use volatile::{ReadOnly, Volatile, WriteOnly}; + +bitflags! { + /// InterruptEnableRegister + pub struct IER: u8 { + const RX_AVAILABLE = 1 << 0; + const TX_EMPTY = 1 << 1; + } + + /// LineStatusRegister + pub struct LSR: u8 { + const DATA_AVAILABLE = 1 << 0; + const THR_EMPTY = 1 << 5; + } + + /// Model Control Register + pub struct MCR: u8 { + const DATA_TERMINAL_READY = 1 << 0; + const REQUEST_TO_SEND = 1 << 1; + const AUX_OUTPUT1 = 1 << 2; + const AUX_OUTPUT2 = 1 << 3; + } +} + +#[repr(C)] +#[allow(dead_code)] +struct ReadWithoutDLAB { + /// receiver buffer register + pub rbr: ReadOnly, + /// interrupt enable register + pub ier: Volatile, + /// interrupt identification register + pub iir: ReadOnly, + /// line control register + pub lcr: Volatile, + /// model control register + pub mcr: Volatile, + /// line status register + pub lsr: ReadOnly, + /// ignore MSR + _padding1: ReadOnly, + /// ignore SCR + _padding2: ReadOnly, +} + +#[repr(C)] +#[allow(dead_code)] +struct WriteWithoutDLAB { + /// transmitter holding register + pub thr: WriteOnly, + /// interrupt enable register + pub ier: Volatile, + /// ignore FCR + _padding0: ReadOnly, + /// line control register + pub lcr: Volatile, + /// modem control register + pub mcr: Volatile, + /// line status register + pub lsr: ReadOnly, + /// ignore other registers + _padding1: ReadOnly, +} + +pub struct NS16550aRaw { + base_addr: usize, +} + +impl NS16550aRaw { + fn read_end(&mut self) -> &mut ReadWithoutDLAB { + unsafe { &mut *(self.base_addr as *mut ReadWithoutDLAB) } + } + + fn write_end(&mut self) -> &mut WriteWithoutDLAB { + unsafe { &mut *(self.base_addr as *mut WriteWithoutDLAB) } + } + + pub fn new(base_addr: usize) -> Self { + Self { base_addr } + } + + pub fn init(&mut self) { + let read_end = self.read_end(); + let mut mcr = MCR::empty(); + mcr |= MCR::DATA_TERMINAL_READY; + mcr |= MCR::REQUEST_TO_SEND; + mcr |= MCR::AUX_OUTPUT2; + read_end.mcr.write(mcr); + let ier = IER::RX_AVAILABLE; + read_end.ier.write(ier); + } + + pub fn read(&mut self) -> Option { + let read_end = self.read_end(); + let lsr = read_end.lsr.read(); + if lsr.contains(LSR::DATA_AVAILABLE) { + Some(read_end.rbr.read()) + } else { + None + } + } + + pub fn write(&mut self, ch: u8) { + let write_end = self.write_end(); + loop { + if write_end.lsr.read().contains(LSR::THR_EMPTY) { + write_end.thr.write(ch); + break; + } + } + } +} + +struct NS16550aInner { + ns16550a: NS16550aRaw, + read_buffer: VecDeque, +} + +pub struct NS16550a { + inner: UPIntrFreeCell, + condvar: Condvar, +} + +impl NS16550a { + pub fn new() -> Self { + let inner = NS16550aInner { + ns16550a: NS16550aRaw::new(BASE_ADDR), + read_buffer: VecDeque::new(), + }; + //inner.ns16550a.init(); + Self { + inner: unsafe { UPIntrFreeCell::new(inner) }, + condvar: Condvar::new(), + } + } + + pub fn read_buffer_is_empty(&self) -> bool { + self.inner + .exclusive_session(|inner| inner.read_buffer.is_empty()) + } +} + +impl CharDevice for NS16550a { + fn init(&self) { + let mut inner = self.inner.exclusive_access(); + inner.ns16550a.init(); + drop(inner); + } + + fn read(&self) -> u8 { + loop { + let mut inner = self.inner.exclusive_access(); + if let Some(ch) = inner.read_buffer.pop_front() { + return ch; + } else { + let task_cx_ptr = self.condvar.wait_no_sched(); + drop(inner); + schedule(task_cx_ptr); + } + } + } + fn write(&self, ch: u8) { + let mut inner = self.inner.exclusive_access(); + inner.ns16550a.write(ch); + } + fn handle_irq(&self) { + let mut count = 0; + self.inner.exclusive_session(|inner| { + while let Some(ch) = inner.ns16550a.read() { + count += 1; + inner.read_buffer.push_back(ch); + } + }); + if count > 0 { + self.condvar.signal(); + } + } +} diff --git a/os/src/drivers/gpu/mod.rs b/os/src/drivers/gpu/mod.rs new file mode 100644 index 0000000..759633f --- /dev/null +++ b/os/src/drivers/gpu/mod.rs @@ -0,0 +1,68 @@ +use crate::drivers::bus::virtio::VirtioHal; +use crate::sync::UPIntrFreeCell; +use alloc::{sync::Arc, vec::Vec}; +use core::any::Any; +use embedded_graphics::pixelcolor::Rgb888; +use tinybmp::Bmp; +use virtio_drivers::{VirtIOGpu, VirtIOHeader}; +const VIRTIO7: usize = 0x10007000; +pub trait GpuDevice: Send + Sync + Any { + fn update_cursor(&self); + fn get_framebuffer(&self) -> &mut [u8]; + fn flush(&self); +} + +lazy_static::lazy_static!( + pub static ref GPU_DEVICE: Arc = Arc::new(VirtIOGpuWrapper::new()); +); + +pub struct VirtIOGpuWrapper { + gpu: UPIntrFreeCell>, + fb: &'static [u8], +} +static BMP_DATA: &[u8] = include_bytes!("../../assert/mouse.bmp"); +impl VirtIOGpuWrapper { + pub fn new() -> Self { + unsafe { + let mut virtio = + VirtIOGpu::::new(&mut *(VIRTIO7 as *mut VirtIOHeader)).unwrap(); + + let fbuffer = virtio.setup_framebuffer().unwrap(); + let len = fbuffer.len(); + let ptr = fbuffer.as_mut_ptr(); + let fb = core::slice::from_raw_parts_mut(ptr, len); + + let bmp = Bmp::::from_slice(BMP_DATA).unwrap(); + let raw = bmp.as_raw(); + let mut b = Vec::new(); + for i in raw.image_data().chunks(3) { + let mut v = i.to_vec(); + b.append(&mut v); + if i == [255, 255, 255] { + b.push(0x0) + } else { + b.push(0xff) + } + } + virtio.setup_cursor(b.as_slice(), 50, 50, 50, 50).unwrap(); + + Self { + gpu: UPIntrFreeCell::new(virtio), + fb, + } + } + } +} + +impl GpuDevice for VirtIOGpuWrapper { + fn flush(&self) { + self.gpu.exclusive_access().flush().unwrap(); + } + fn get_framebuffer(&self) -> &mut [u8] { + unsafe { + let ptr = self.fb.as_ptr() as *const _ as *mut u8; + core::slice::from_raw_parts_mut(ptr, self.fb.len()) + } + } + fn update_cursor(&self) {} +} diff --git a/os/src/drivers/input/mod.rs b/os/src/drivers/input/mod.rs new file mode 100644 index 0000000..a9f5f0b --- /dev/null +++ b/os/src/drivers/input/mod.rs @@ -0,0 +1,83 @@ +use crate::drivers::bus::virtio::VirtioHal; +use crate::sync::{Condvar, UPIntrFreeCell}; +use crate::task::schedule; +use alloc::collections::VecDeque; +use alloc::sync::Arc; +use core::any::Any; +use virtio_drivers::{VirtIOHeader, VirtIOInput}; + +const VIRTIO5: usize = 0x10005000; +const VIRTIO6: usize = 0x10006000; + +struct VirtIOInputInner { + virtio_input: VirtIOInput<'static, VirtioHal>, + events: VecDeque, +} + +struct VirtIOInputWrapper { + inner: UPIntrFreeCell, + condvar: Condvar, +} + +pub trait InputDevice: Send + Sync + Any { + fn read_event(&self) -> u64; + fn handle_irq(&self); + fn is_empty(&self) -> bool; +} + +lazy_static::lazy_static!( + pub static ref KEYBOARD_DEVICE: Arc = Arc::new(VirtIOInputWrapper::new(VIRTIO5)); + pub static ref MOUSE_DEVICE: Arc = Arc::new(VirtIOInputWrapper::new(VIRTIO6)); +); + +impl VirtIOInputWrapper { + pub fn new(addr: usize) -> Self { + let inner = VirtIOInputInner { + virtio_input: unsafe { + VirtIOInput::::new(&mut *(addr as *mut VirtIOHeader)).unwrap() + }, + events: VecDeque::new(), + }; + Self { + inner: unsafe { UPIntrFreeCell::new(inner) }, + condvar: Condvar::new(), + } + } +} + +impl InputDevice for VirtIOInputWrapper { + fn is_empty(&self) -> bool { + self.inner.exclusive_access().events.is_empty() + } + + fn read_event(&self) -> u64 { + loop { + let mut inner = self.inner.exclusive_access(); + if let Some(event) = inner.events.pop_front() { + return event; + } else { + let task_cx_ptr = self.condvar.wait_no_sched(); + drop(inner); + schedule(task_cx_ptr); + } + } + } + + fn handle_irq(&self) { + let mut count = 0; + let mut result = 0; + self.inner.exclusive_session(|inner| { + inner.virtio_input.ack_interrupt(); + while let Some(event) = inner.virtio_input.pop_pending_event() { + count += 1; + result = (event.event_type as u64) << 48 + | (event.code as u64) << 32 + | (event.value) as u64; + inner.events.push_back(result); + } + }); + if count > 0 { + self.condvar.signal(); + }; + } +} diff --git a/os/src/drivers/mod.rs b/os/src/drivers/mod.rs index c2dea36..605397d 100644 --- a/os/src/drivers/mod.rs +++ b/os/src/drivers/mod.rs @@ -1,3 +1,13 @@ pub mod block; +pub mod bus; +pub mod chardev; +pub mod gpu; +pub mod input; +pub mod net; +pub mod plic; pub use block::BLOCK_DEVICE; +pub use bus::*; +pub use gpu::*; +pub use input::*; +pub use net::*; diff --git a/os/src/drivers/net/mod.rs b/os/src/drivers/net/mod.rs new file mode 100644 index 0000000..7a70587 --- /dev/null +++ b/os/src/drivers/net/mod.rs @@ -0,0 +1,46 @@ +use core::any::Any; + +use crate::drivers::virtio::VirtioHal; +use crate::sync::UPIntrFreeCell; +use alloc::sync::Arc; +use lazy_static::*; +use virtio_drivers::{VirtIOHeader, VirtIONet}; + +const VIRTIO8: usize = 0x10004000; + +lazy_static! { + pub static ref NET_DEVICE: Arc = Arc::new(VirtIONetWrapper::new()); +} + +pub trait NetDevice: Send + Sync + Any { + fn transmit(&self, data: &[u8]); + fn receive(&self, data: &mut [u8]) -> usize; +} + +pub struct VirtIONetWrapper(UPIntrFreeCell>); + +impl NetDevice for VirtIONetWrapper { + fn transmit(&self, data: &[u8]) { + self.0 + .exclusive_access() + .send(data) + .expect("can't send data") + } + + fn receive(&self, data: &mut [u8]) -> usize { + self.0 + .exclusive_access() + .recv(data) + .expect("can't receive data") + } +} + +impl VirtIONetWrapper { + pub fn new() -> Self { + unsafe { + let virtio = VirtIONet::::new(&mut *(VIRTIO8 as *mut VirtIOHeader)) + .expect("can't create net device by virtio"); + VirtIONetWrapper(UPIntrFreeCell::new(virtio)) + } + } +} diff --git a/os/src/drivers/plic.rs b/os/src/drivers/plic.rs new file mode 100644 index 0000000..77bad5f --- /dev/null +++ b/os/src/drivers/plic.rs @@ -0,0 +1,124 @@ +#[allow(clippy::upper_case_acronyms)] +pub struct PLIC { + base_addr: usize, +} + +#[derive(Copy, Clone)] +pub enum IntrTargetPriority { + Machine = 0, + Supervisor = 1, +} + +impl IntrTargetPriority { + pub fn supported_number() -> usize { + 2 + } +} + +impl PLIC { + fn priority_ptr(&self, intr_source_id: usize) -> *mut u32 { + assert!(intr_source_id > 0 && intr_source_id <= 132); + (self.base_addr + intr_source_id * 4) as *mut u32 + } + fn hart_id_with_priority(hart_id: usize, target_priority: IntrTargetPriority) -> usize { + let priority_num = IntrTargetPriority::supported_number(); + hart_id * priority_num + target_priority as usize + } + fn enable_ptr( + &self, + hart_id: usize, + target_priority: IntrTargetPriority, + intr_source_id: usize, + ) -> (*mut u32, usize) { + let id = Self::hart_id_with_priority(hart_id, target_priority); + let (reg_id, reg_shift) = (intr_source_id / 32, intr_source_id % 32); + ( + (self.base_addr + 0x2000 + 0x80 * id + 0x4 * reg_id) as *mut u32, + reg_shift, + ) + } + fn threshold_ptr_of_hart_with_priority( + &self, + hart_id: usize, + target_priority: IntrTargetPriority, + ) -> *mut u32 { + let id = Self::hart_id_with_priority(hart_id, target_priority); + (self.base_addr + 0x20_0000 + 0x1000 * id) as *mut u32 + } + fn claim_comp_ptr_of_hart_with_priority( + &self, + hart_id: usize, + target_priority: IntrTargetPriority, + ) -> *mut u32 { + let id = Self::hart_id_with_priority(hart_id, target_priority); + (self.base_addr + 0x20_0004 + 0x1000 * id) as *mut u32 + } + pub unsafe fn new(base_addr: usize) -> Self { + Self { base_addr } + } + pub fn set_priority(&mut self, intr_source_id: usize, priority: u32) { + assert!(priority < 8); + unsafe { + self.priority_ptr(intr_source_id).write_volatile(priority); + } + } + #[allow(unused)] + pub fn get_priority(&mut self, intr_source_id: usize) -> u32 { + unsafe { self.priority_ptr(intr_source_id).read_volatile() & 7 } + } + pub fn enable( + &mut self, + hart_id: usize, + target_priority: IntrTargetPriority, + intr_source_id: usize, + ) { + let (reg_ptr, shift) = self.enable_ptr(hart_id, target_priority, intr_source_id); + unsafe { + reg_ptr.write_volatile(reg_ptr.read_volatile() | 1 << shift); + } + } + #[allow(unused)] + pub fn disable( + &mut self, + hart_id: usize, + target_priority: IntrTargetPriority, + intr_source_id: usize, + ) { + let (reg_ptr, shift) = self.enable_ptr(hart_id, target_priority, intr_source_id); + unsafe { + reg_ptr.write_volatile(reg_ptr.read_volatile() & (!(1u32 << shift))); + } + } + pub fn set_threshold( + &mut self, + hart_id: usize, + target_priority: IntrTargetPriority, + threshold: u32, + ) { + assert!(threshold < 8); + let threshold_ptr = self.threshold_ptr_of_hart_with_priority(hart_id, target_priority); + unsafe { + threshold_ptr.write_volatile(threshold); + } + } + #[allow(unused)] + pub fn get_threshold(&mut self, hart_id: usize, target_priority: IntrTargetPriority) -> u32 { + let threshold_ptr = self.threshold_ptr_of_hart_with_priority(hart_id, target_priority); + unsafe { threshold_ptr.read_volatile() & 7 } + } + pub fn claim(&mut self, hart_id: usize, target_priority: IntrTargetPriority) -> u32 { + let claim_comp_ptr = self.claim_comp_ptr_of_hart_with_priority(hart_id, target_priority); + unsafe { claim_comp_ptr.read_volatile() } + } + pub fn complete( + &mut self, + hart_id: usize, + target_priority: IntrTargetPriority, + completion: u32, + ) { + let claim_comp_ptr = self.claim_comp_ptr_of_hart_with_priority(hart_id, target_priority); + unsafe { + claim_comp_ptr.write_volatile(completion); + } + } +} diff --git a/os/src/entry.asm b/os/src/entry.asm index 3a9990f..c32d68f 100644 --- a/os/src/entry.asm +++ b/os/src/entry.asm @@ -9,4 +9,4 @@ _start: boot_stack_lower_bound: .space 4096 * 16 .globl boot_stack_top -boot_stack_top: \ No newline at end of file +boot_stack_top: diff --git a/os/src/fs/inode.rs b/os/src/fs/inode.rs index 2490d6a..321338d 100644 --- a/os/src/fs/inode.rs +++ b/os/src/fs/inode.rs @@ -1,41 +1,32 @@ -//! `Arc` -> `OSInodeInner`: In order to open files concurrently -//! we need to wrap `Inode` into `Arc`,but `Mutex` in `Inode` prevents -//! file systems from being accessed simultaneously -//! -//! `UPSafeCell` -> `OSInode`: for static `ROOT_INODE`,we -//! need to wrap `OSInodeInner` into `UPSafeCell` use super::File; use crate::drivers::BLOCK_DEVICE; use crate::mm::UserBuffer; -use crate::sync::UPSafeCell; +use crate::sync::UPIntrFreeCell; use alloc::sync::Arc; use alloc::vec::Vec; use bitflags::*; use easy_fs::{EasyFileSystem, Inode}; use lazy_static::*; -/// A wrapper around a filesystem inode -/// to implement File trait atop + pub struct OSInode { readable: bool, writable: bool, - inner: UPSafeCell, + inner: UPIntrFreeCell, } -/// The OS inode inner in 'UPSafeCell' + pub struct OSInodeInner { offset: usize, inode: Arc, } impl OSInode { - /// Construct an OS inode from a inode pub fn new(readable: bool, writable: bool, inode: Arc) -> Self { Self { readable, writable, - inner: unsafe { UPSafeCell::new(OSInodeInner { offset: 0, inode }) }, + inner: unsafe { UPIntrFreeCell::new(OSInodeInner { offset: 0, inode }) }, } } - /// Read all data inside a inode into vector pub fn read_all(&self) -> Vec { let mut inner = self.inner.exclusive_access(); let mut buffer = [0u8; 512]; @@ -58,27 +49,21 @@ lazy_static! { Arc::new(EasyFileSystem::root_inode(&efs)) }; } -/// List all files in the filesystems + pub fn list_apps() { println!("/**** APPS ****"); for app in ROOT_INODE.ls() { println!("{}", app); } - println!("**************/"); + println!("**************/") } bitflags! { - ///Open file flags pub struct OpenFlags: u32 { - ///Read only const RDONLY = 0; - ///Write only const WRONLY = 1 << 0; - ///Read & Write const RDWR = 1 << 1; - ///Allow create const CREATE = 1 << 9; - ///Clear file and return an empty one const TRUNC = 1 << 10; } } @@ -96,7 +81,7 @@ impl OpenFlags { } } } -///Open file with flags + pub fn open_file(name: &str, flags: OpenFlags) -> Option> { let (readable, writable) = flags.read_write(); if flags.contains(OpenFlags::CREATE) { diff --git a/os/src/fs/mod.rs b/os/src/fs/mod.rs index c3cc1e7..cbde739 100644 --- a/os/src/fs/mod.rs +++ b/os/src/fs/mod.rs @@ -1,19 +1,16 @@ -//! File system in os mod inode; +mod pipe; mod stdio; use crate::mm::UserBuffer; -/// File trait + pub trait File: Send + Sync { - /// If readable fn readable(&self) -> bool; - /// If writable fn writable(&self) -> bool; - /// Read file to `UserBuffer` fn read(&self, buf: UserBuffer) -> usize; - /// Write `UserBuffer` to file fn write(&self, buf: UserBuffer) -> usize; } -pub use inode::{OSInode, OpenFlags, list_apps, open_file}; +pub use inode::{list_apps, open_file, OpenFlags}; +pub use pipe::make_pipe; pub use stdio::{Stdin, Stdout}; diff --git a/os/src/fs/pipe.rs b/os/src/fs/pipe.rs new file mode 100644 index 0000000..d10dc33 --- /dev/null +++ b/os/src/fs/pipe.rs @@ -0,0 +1,173 @@ +use super::File; +use crate::mm::UserBuffer; +use crate::sync::UPIntrFreeCell; +use alloc::sync::{Arc, Weak}; + +use crate::task::suspend_current_and_run_next; + +pub struct Pipe { + readable: bool, + writable: bool, + buffer: Arc>, +} + +impl Pipe { + pub fn read_end_with_buffer(buffer: Arc>) -> Self { + Self { + readable: true, + writable: false, + buffer, + } + } + pub fn write_end_with_buffer(buffer: Arc>) -> Self { + Self { + readable: false, + writable: true, + buffer, + } + } +} + +const RING_BUFFER_SIZE: usize = 32; + +#[derive(Copy, Clone, PartialEq)] +enum RingBufferStatus { + Full, + Empty, + Normal, +} + +pub struct PipeRingBuffer { + arr: [u8; RING_BUFFER_SIZE], + head: usize, + tail: usize, + status: RingBufferStatus, + write_end: Option>, +} + +impl PipeRingBuffer { + pub fn new() -> Self { + Self { + arr: [0; RING_BUFFER_SIZE], + head: 0, + tail: 0, + status: RingBufferStatus::Empty, + write_end: None, + } + } + pub fn set_write_end(&mut self, write_end: &Arc) { + self.write_end = Some(Arc::downgrade(write_end)); + } + pub fn write_byte(&mut self, byte: u8) { + self.status = RingBufferStatus::Normal; + self.arr[self.tail] = byte; + self.tail = (self.tail + 1) % RING_BUFFER_SIZE; + if self.tail == self.head { + self.status = RingBufferStatus::Full; + } + } + pub fn read_byte(&mut self) -> u8 { + self.status = RingBufferStatus::Normal; + let c = self.arr[self.head]; + self.head = (self.head + 1) % RING_BUFFER_SIZE; + if self.head == self.tail { + self.status = RingBufferStatus::Empty; + } + c + } + pub fn available_read(&self) -> usize { + if self.status == RingBufferStatus::Empty { + 0 + } else if self.tail > self.head { + self.tail - self.head + } else { + self.tail + RING_BUFFER_SIZE - self.head + } + } + pub fn available_write(&self) -> usize { + if self.status == RingBufferStatus::Full { + 0 + } else { + RING_BUFFER_SIZE - self.available_read() + } + } + pub fn all_write_ends_closed(&self) -> bool { + self.write_end.as_ref().unwrap().upgrade().is_none() + } +} + +/// Return (read_end, write_end) +pub fn make_pipe() -> (Arc, Arc) { + let buffer = Arc::new(unsafe { UPIntrFreeCell::new(PipeRingBuffer::new()) }); + let read_end = Arc::new(Pipe::read_end_with_buffer(buffer.clone())); + let write_end = Arc::new(Pipe::write_end_with_buffer(buffer.clone())); + buffer.exclusive_access().set_write_end(&write_end); + (read_end, write_end) +} + +impl File for Pipe { + fn readable(&self) -> bool { + self.readable + } + fn writable(&self) -> bool { + self.writable + } + fn read(&self, buf: UserBuffer) -> usize { + assert!(self.readable()); + let want_to_read = buf.len(); + let mut buf_iter = buf.into_iter(); + let mut already_read = 0usize; + loop { + let mut ring_buffer = self.buffer.exclusive_access(); + let loop_read = ring_buffer.available_read(); + if loop_read == 0 { + if ring_buffer.all_write_ends_closed() { + return already_read; + } + drop(ring_buffer); + suspend_current_and_run_next(); + continue; + } + for _ in 0..loop_read { + if let Some(byte_ref) = buf_iter.next() { + unsafe { + *byte_ref = ring_buffer.read_byte(); + } + already_read += 1; + if already_read == want_to_read { + return want_to_read; + } + } else { + return already_read; + } + } + } + } + fn write(&self, buf: UserBuffer) -> usize { + assert!(self.writable()); + let want_to_write = buf.len(); + let mut buf_iter = buf.into_iter(); + let mut already_write = 0usize; + loop { + let mut ring_buffer = self.buffer.exclusive_access(); + let loop_write = ring_buffer.available_write(); + if loop_write == 0 { + drop(ring_buffer); + suspend_current_and_run_next(); + continue; + } + // write at most loop_write bytes + for _ in 0..loop_write { + if let Some(byte_ref) = buf_iter.next() { + ring_buffer.write_byte(unsafe { *byte_ref }); + already_write += 1; + if already_write == want_to_write { + return want_to_write; + } + } else { + return already_write; + } + } + } + } +} diff --git a/os/src/fs/stdio.rs b/os/src/fs/stdio.rs index 8375ac9..66f4c5a 100644 --- a/os/src/fs/stdio.rs +++ b/os/src/fs/stdio.rs @@ -1,11 +1,9 @@ -//!Stdin & Stdout use super::File; +use crate::drivers::chardev::CharDevice; +use crate::drivers::chardev::UART; use crate::mm::UserBuffer; -use crate::sbi::console_getchar; -use crate::task::suspend_current_and_run_next; -///Standard input + pub struct Stdin; -///Standard output pub struct Stdout; impl File for Stdin { @@ -17,18 +15,8 @@ impl File for Stdin { } fn read(&self, mut user_buf: UserBuffer) -> usize { assert_eq!(user_buf.len(), 1); - // busy loop - let mut c: usize; - loop { - c = console_getchar(); - if c == 0 { - suspend_current_and_run_next(); - continue; - } else { - break; - } - } - let ch = c as u8; + //println!("before UART.read() in Stdin::read()"); + let ch = UART.read(); unsafe { user_buf.buffers[0].as_mut_ptr().write_volatile(ch); } diff --git a/os/src/lang_items.rs b/os/src/lang_items.rs index 79cd51e..021f16f 100644 --- a/os/src/lang_items.rs +++ b/os/src/lang_items.rs @@ -1,5 +1,6 @@ -//! The panic handler use crate::sbi::shutdown; +use crate::task::current_kstack_top; +use core::arch::asm; use core::panic::PanicInfo; use log::*; @@ -10,10 +11,28 @@ fn panic(info: &PanicInfo) -> ! { "[kernel] Panicked at {}:{} {}", location.file(), location.line(), - info.message() + info.message().unwrap() ); } else { - error!("[kernel] Panicked: {}", info.message()); + error!("[kernel] Panicked: {}", info.message().unwrap()); + } + unsafe { + backtrace(); } shutdown(true) } + +unsafe fn backtrace() { + let mut fp: usize; + let stop = current_kstack_top(); + asm!("mv {}, s0", out(reg) fp); + println!("---START BACKTRACE---"); + for i in 0..10 { + if fp == stop { + break; + } + println!("#{}:ra={:#x}", i, *((fp - 8) as *const usize)); + fp = *((fp - 16) as *const usize); + } + println!("---END BACKTRACE---"); +} diff --git a/os/src/logging.rs b/os/src/logging.rs deleted file mode 100644 index b5421f1..0000000 --- a/os/src/logging.rs +++ /dev/null @@ -1,47 +0,0 @@ -/*! - -本模块利用 log crate 为你提供了日志功能,使用方式见 main.rs. - -*/ - -use log::{self, Level, LevelFilter, Log, Metadata, Record}; - -struct SimpleLogger; - -impl Log for SimpleLogger { - fn enabled(&self, _metadata: &Metadata) -> bool { - true - } - fn log(&self, record: &Record) { - if !self.enabled(record.metadata()) { - return; - } - let color = match record.level() { - Level::Error => 31, // Red - Level::Warn => 93, // BrightYellow - Level::Info => 34, // Blue - Level::Debug => 32, // Green - Level::Trace => 90, // BrightBlack - }; - println!( - "\u{1B}[{}m[{:>5}] {}\u{1B}[0m", - color, - record.level(), - record.args(), - ); - } - fn flush(&self) {} -} - -pub fn init() { - static LOGGER: SimpleLogger = SimpleLogger; - log::set_logger(&LOGGER).unwrap(); - log::set_max_level(match option_env!("LOG") { - Some("ERROR") => LevelFilter::Error, - Some("WARN") => LevelFilter::Warn, - Some("INFO") => LevelFilter::Info, - Some("DEBUG") => LevelFilter::Debug, - Some("TRACE") => LevelFilter::Trace, - _ => LevelFilter::Info, - }); -} diff --git a/os/src/main.rs b/os/src/main.rs index 46161de..276aaf5 100644 --- a/os/src/main.rs +++ b/os/src/main.rs @@ -1,37 +1,15 @@ -//! The main module and entrypoint -//! -//! Various facilities of the kernels are implemented as submodules. The most -//! important ones are: -//! -//! - [`trap`]: Handles all cases of switching from userspace to the kernel -//! - [`task`]: Task management -//! - [`syscall`]: System call handling and implementation -//! - [`mm`]: Address map using SV39 -//! - [`sync`]: Wrap a static data structure inside it so that we are able to access it without any `unsafe`. -//! - [`fs`]: Separate user from file system with some structures -//! -//! The operating system also starts in this module. Kernel code starts -//! executing from `entry.asm`, after which [`rust_main()`] is called to -//! initialize various pieces of functionality. (See its source code for -//! details.) -//! -//! We then call [`task::run_tasks()`] and for the first time go to -//! userspace. - -#![deny(missing_docs)] -#![deny(warnings)] -#![allow(unused_imports)] #![no_std] #![no_main] +#![feature(panic_info_message)] #![feature(alloc_error_handler)] +//use crate::drivers::{GPU_DEVICE, KEYBOARD_DEVICE, MOUSE_DEVICE, INPUT_CONDVAR}; +use crate::drivers::{GPU_DEVICE, KEYBOARD_DEVICE, MOUSE_DEVICE}; extern crate alloc; #[macro_use] extern crate bitflags; -use log::*; - #[path = "boards/qemu.rs"] mod board; @@ -39,25 +17,26 @@ mod board; mod console; mod config; mod drivers; -pub mod fs; -pub mod lang_items; -mod logging; -pub mod mm; -pub mod sbi; -pub mod sync; -pub mod syscall; -pub mod task; -pub mod timer; -pub mod trap; +mod fs; +mod lang_items; +mod mm; +mod net; +mod sbi; +mod sync; +mod syscall; +mod task; +mod timer; +mod trap; -use core::arch::global_asm; +use crate::drivers::chardev::CharDevice; +use crate::drivers::chardev::UART; + +core::arch::global_asm!(include_str!("entry.asm")); -global_asm!(include_str!("entry.asm")); -/// clear BSS segment fn clear_bss() { - unsafe extern "C" { - safe fn sbss(); - safe fn ebss(); + extern "C" { + fn sbss(); + fn ebss(); } unsafe { core::slice::from_raw_parts_mut(sbss as usize as *mut u8, ebss as usize - sbss as usize) @@ -65,19 +44,33 @@ fn clear_bss() { } } -/// the rust entry-point of os -#[unsafe(no_mangle)] +use lazy_static::*; +use sync::UPIntrFreeCell; + +lazy_static! { + pub static ref DEV_NON_BLOCKING_ACCESS: UPIntrFreeCell = + unsafe { UPIntrFreeCell::new(false) }; +} + +#[no_mangle] pub fn rust_main() -> ! { clear_bss(); - logging::init(); - info!("[kernel] Hello, world!"); mm::init(); - mm::remap_test(); + UART.init(); + println!("KERN: init gpu"); + let _gpu = GPU_DEVICE.clone(); + println!("KERN: init keyboard"); + let _keyboard = KEYBOARD_DEVICE.clone(); + println!("KERN: init mouse"); + let _mouse = MOUSE_DEVICE.clone(); + println!("KERN: init trap"); trap::init(); trap::enable_timer_interrupt(); timer::set_next_trigger(); + board::device_init(); fs::list_apps(); task::add_initproc(); + *DEV_NON_BLOCKING_ACCESS.exclusive_access() = true; task::run_tasks(); panic!("Unreachable in rust_main!"); } diff --git a/os/src/mm/address.rs b/os/src/mm/address.rs index f0ee691..df3e130 100644 --- a/os/src/mm/address.rs +++ b/os/src/mm/address.rs @@ -1,4 +1,3 @@ -//! Implementation of physical and virtual address and page number. use super::PageTableEntry; use crate::config::{PAGE_SIZE, PAGE_SIZE_BITS}; use core::fmt::{self, Debug, Formatter}; @@ -15,17 +14,14 @@ pub struct PhysAddr(pub usize); #[repr(C)] #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] -///virtual address pub struct VirtAddr(pub usize); #[repr(C)] #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] -///phiscal page number pub struct PhysPageNum(pub usize); #[repr(C)] #[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] -///virtual page number pub struct VirtPageNum(pub usize); /// Debugging @@ -99,13 +95,11 @@ impl From for usize { v.0 } } -/// + impl VirtAddr { - ///`VirtAddr`->`VirtPageNum` pub fn floor(&self) -> VirtPageNum { VirtPageNum(self.0 / PAGE_SIZE) } - ///`VirtAddr`->`VirtPageNum` pub fn ceil(&self) -> VirtPageNum { if self.0 == 0 { VirtPageNum(0) @@ -113,11 +107,9 @@ impl VirtAddr { VirtPageNum((self.0 - 1 + PAGE_SIZE) / PAGE_SIZE) } } - ///Get page offset pub fn page_offset(&self) -> usize { self.0 & (PAGE_SIZE - 1) } - ///Check page aligned pub fn aligned(&self) -> bool { self.page_offset() == 0 } @@ -134,11 +126,9 @@ impl From for VirtAddr { } } impl PhysAddr { - ///`PhysAddr`->`PhysPageNum` pub fn floor(&self) -> PhysPageNum { PhysPageNum(self.0 / PAGE_SIZE) } - ///`PhysAddr`->`PhysPageNum` pub fn ceil(&self) -> PhysPageNum { if self.0 == 0 { PhysPageNum(0) @@ -146,11 +136,9 @@ impl PhysAddr { PhysPageNum((self.0 - 1 + PAGE_SIZE) / PAGE_SIZE) } } - ///Get page offset pub fn page_offset(&self) -> usize { self.0 & (PAGE_SIZE - 1) } - ///Check page aligned pub fn aligned(&self) -> bool { self.page_offset() == 0 } @@ -168,7 +156,6 @@ impl From for PhysAddr { } impl VirtPageNum { - ///Return VPN 3 level index pub fn indexes(&self) -> [usize; 3] { let mut vpn = self.0; let mut idx = [0usize; 3]; @@ -181,35 +168,29 @@ impl VirtPageNum { } impl PhysAddr { - ///Get reference to `PhysAddr` value pub fn get_ref(&self) -> &'static T { unsafe { (self.0 as *const T).as_ref().unwrap() } } - ///Get mutable reference to `PhysAddr` value pub fn get_mut(&self) -> &'static mut T { unsafe { (self.0 as *mut T).as_mut().unwrap() } } } impl PhysPageNum { - ///Get `PageTableEntry` on `PhysPageNum` pub fn get_pte_array(&self) -> &'static mut [PageTableEntry] { let pa: PhysAddr = (*self).into(); unsafe { core::slice::from_raw_parts_mut(pa.0 as *mut PageTableEntry, 512) } } - ///Get u8 array on `PhysPageNum` pub fn get_bytes_array(&self) -> &'static mut [u8] { let pa: PhysAddr = (*self).into(); unsafe { core::slice::from_raw_parts_mut(pa.0 as *mut u8, 4096) } } - ///Get Get mutable reference to `PhysAddr` value on `PhysPageNum` pub fn get_mut(&self) -> &'static mut T { let pa: PhysAddr = (*self).into(); pa.get_mut() } } -///Add value by one + pub trait StepByOne { - ///Add value by one fn step(&mut self); } impl StepByOne for VirtPageNum { @@ -224,7 +205,6 @@ impl StepByOne for PhysPageNum { } #[derive(Copy, Clone)] -/// a simple range structure for type T pub struct SimpleRange where T: StepByOne + Copy + PartialEq + PartialOrd + Debug, @@ -257,7 +237,6 @@ where SimpleRangeIterator::new(self.l, self.r) } } -/// iterator for the simple range structure pub struct SimpleRangeIterator where T: StepByOne + Copy + PartialEq + PartialOrd + Debug, @@ -288,5 +267,4 @@ where } } } -/// a simple range structure for virtual page number pub type VPNRange = SimpleRange; diff --git a/os/src/mm/frame_allocator.rs b/os/src/mm/frame_allocator.rs index 28b519c..163bb57 100644 --- a/os/src/mm/frame_allocator.rs +++ b/os/src/mm/frame_allocator.rs @@ -1,20 +1,15 @@ -//! Implementation of [`FrameAllocator`] which -//! controls all the frames in the operating system. use super::{PhysAddr, PhysPageNum}; use crate::config::MEMORY_END; -use crate::sync::UPSafeCell; +use crate::sync::UPIntrFreeCell; use alloc::vec::Vec; use core::fmt::{self, Debug, Formatter}; use lazy_static::*; -/// manage a frame which has the same lifecycle as the tracker pub struct FrameTracker { - /// pub ppn: PhysPageNum, } impl FrameTracker { - ///Create an empty `FrameTracker` pub fn new(ppn: PhysPageNum) -> Self { // page cleaning let bytes_array = ppn.get_bytes_array(); @@ -40,9 +35,10 @@ impl Drop for FrameTracker { trait FrameAllocator { fn new() -> Self; fn alloc(&mut self) -> Option; + fn alloc_more(&mut self, pages: usize) -> Option>; fn dealloc(&mut self, ppn: PhysPageNum); } -/// an implementation for frame allocator + pub struct StackFrameAllocator { current: usize, end: usize, @@ -53,7 +49,7 @@ impl StackFrameAllocator { pub fn init(&mut self, l: PhysPageNum, r: PhysPageNum) { self.current = l.0; self.end = r.0; - println!("last {} Physical Frames.", self.end - self.current); + // println!("last {} Physical Frames.", self.end - self.current); } } impl FrameAllocator for StackFrameAllocator { @@ -74,6 +70,16 @@ impl FrameAllocator for StackFrameAllocator { Some((self.current - 1).into()) } } + fn alloc_more(&mut self, pages: usize) -> Option> { + if self.current + pages >= self.end { + None + } else { + self.current += pages; + let arr: Vec = (1..pages + 1).collect(); + let v = arr.iter().map(|x| (self.current - x).into()).collect(); + Some(v) + } + } fn dealloc(&mut self, ppn: PhysPageNum) { let ppn = ppn.0; // validity check @@ -88,34 +94,39 @@ impl FrameAllocator for StackFrameAllocator { type FrameAllocatorImpl = StackFrameAllocator; lazy_static! { - /// frame allocator instance through lazy_static! - pub static ref FRAME_ALLOCATOR: UPSafeCell = - unsafe { UPSafeCell::new(FrameAllocatorImpl::new()) }; + pub static ref FRAME_ALLOCATOR: UPIntrFreeCell = + unsafe { UPIntrFreeCell::new(FrameAllocatorImpl::new()) }; } -/// initiate the frame allocator using `ekernel` and `MEMORY_END` + pub fn init_frame_allocator() { - unsafe extern "C" { - safe fn ekernel(); + extern "C" { + fn ekernel(); } FRAME_ALLOCATOR.exclusive_access().init( PhysAddr::from(ekernel as usize).ceil(), PhysAddr::from(MEMORY_END).floor(), ); } -/// allocate a frame + pub fn frame_alloc() -> Option { FRAME_ALLOCATOR .exclusive_access() .alloc() .map(FrameTracker::new) } -/// deallocate a frame + +pub fn frame_alloc_more(num: usize) -> Option> { + FRAME_ALLOCATOR + .exclusive_access() + .alloc_more(num) + .map(|x| x.iter().map(|&t| FrameTracker::new(t)).collect()) +} + pub fn frame_dealloc(ppn: PhysPageNum) { FRAME_ALLOCATOR.exclusive_access().dealloc(ppn); } #[allow(unused)] -/// a simple test for frame allocator pub fn frame_allocator_test() { let mut v: Vec = Vec::new(); for i in 0..5 { @@ -132,3 +143,20 @@ pub fn frame_allocator_test() { drop(v); println!("frame_allocator_test passed!"); } + +#[allow(unused)] +pub fn frame_allocator_alloc_more_test() { + let mut v: Vec = Vec::new(); + let frames = frame_alloc_more(5).unwrap(); + for frame in &frames { + println!("{:?}", frame); + } + v.extend(frames); + v.clear(); + let frames = frame_alloc_more(5).unwrap(); + for frame in &frames { + println!("{:?}", frame); + } + drop(v); + println!("frame_allocator_test passed!"); +} diff --git a/os/src/mm/heap_allocator.rs b/os/src/mm/heap_allocator.rs index a6b413b..42a6d76 100644 --- a/os/src/mm/heap_allocator.rs +++ b/os/src/mm/heap_allocator.rs @@ -1,25 +1,21 @@ -//! The global allocator use crate::config::KERNEL_HEAP_SIZE; use buddy_system_allocator::LockedHeap; -use core::ptr::addr_of_mut; #[global_allocator] -/// heap allocator instance static HEAP_ALLOCATOR: LockedHeap = LockedHeap::empty(); #[alloc_error_handler] -/// panic when heap allocation error occurs pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! { panic!("Heap allocation error, layout = {:?}", layout); } -/// heap space ([u8; KERNEL_HEAP_SIZE]) + static mut HEAP_SPACE: [u8; KERNEL_HEAP_SIZE] = [0; KERNEL_HEAP_SIZE]; -/// initiate heap allocator + pub fn init_heap() { unsafe { HEAP_ALLOCATOR .lock() - .init(addr_of_mut!(HEAP_SPACE) as usize, KERNEL_HEAP_SIZE); + .init(HEAP_SPACE.as_ptr() as usize, KERNEL_HEAP_SIZE); } } @@ -27,9 +23,9 @@ pub fn init_heap() { pub fn heap_test() { use alloc::boxed::Box; use alloc::vec::Vec; - unsafe extern "C" { - safe fn sbss(); - safe fn ebss(); + extern "C" { + fn sbss(); + fn ebss(); } let bss_range = sbss as usize..ebss as usize; let a = Box::new(5); diff --git a/os/src/mm/memory_set.rs b/os/src/mm/memory_set.rs index b1e5072..d607c88 100644 --- a/os/src/mm/memory_set.rs +++ b/os/src/mm/memory_set.rs @@ -1,11 +1,9 @@ -//! Implementation of [`MapArea`] and [`MemorySet`]. - -use super::{FrameTracker, frame_alloc}; +use super::{frame_alloc, FrameTracker}; use super::{PTEFlags, PageTable, PageTableEntry}; use super::{PhysAddr, PhysPageNum, VirtAddr, VirtPageNum}; use super::{StepByOne, VPNRange}; -use crate::config::{MEMORY_END, MMIO, PAGE_SIZE, TRAMPOLINE, TRAP_CONTEXT, USER_STACK_SIZE}; -use crate::sync::UPSafeCell; +use crate::config::{MEMORY_END, MMIO, PAGE_SIZE, TRAMPOLINE}; +use crate::sync::UPIntrFreeCell; use alloc::collections::BTreeMap; use alloc::sync::Arc; use alloc::vec::Vec; @@ -13,43 +11,40 @@ use core::arch::asm; use lazy_static::*; use riscv::register::satp; -unsafe extern "C" { - safe fn stext(); - safe fn etext(); - safe fn srodata(); - safe fn erodata(); - safe fn sdata(); - safe fn edata(); - safe fn sbss_with_stack(); - safe fn ebss(); - safe fn ekernel(); - safe fn strampoline(); +extern "C" { + fn stext(); + fn etext(); + fn srodata(); + fn erodata(); + fn sdata(); + fn edata(); + fn sbss_with_stack(); + fn ebss(); + fn ekernel(); + fn strampoline(); } lazy_static! { - /// a memory set instance through lazy_static! managing kernel space - pub static ref KERNEL_SPACE: Arc> = - Arc::new(unsafe { UPSafeCell::new(MemorySet::new_kernel()) }); + pub static ref KERNEL_SPACE: Arc> = + Arc::new(unsafe { UPIntrFreeCell::new(MemorySet::new_kernel()) }); } -///Get kernelspace root ppn + pub fn kernel_token() -> usize { KERNEL_SPACE.exclusive_access().token() } -/// memory set structure, controls virtual-memory space + pub struct MemorySet { page_table: PageTable, areas: Vec, } impl MemorySet { - ///Create an empty `MemorySet` pub fn new_bare() -> Self { Self { page_table: PageTable::new(), areas: Vec::new(), } } - ///Get pagetable `root_ppn` pub fn token(&self) -> usize { self.page_table.token() } @@ -65,7 +60,6 @@ impl MemorySet { None, ); } - ///Remove `MapArea` that starts with `start_vpn` pub fn remove_area_with_start_vpn(&mut self, start_vpn: VirtPageNum) { if let Some((idx, area)) = self .areas @@ -77,7 +71,10 @@ impl MemorySet { self.areas.remove(idx); } } - fn push(&mut self, mut map_area: MapArea, data: Option<&[u8]>) { + /// Add a new MapArea into this MemorySet. + /// Assuming that there are no conflicts in the virtual address + /// space. + pub fn push(&mut self, mut map_area: MapArea, data: Option<&[u8]>) { map_area.map(&mut self.page_table); if let Some(data) = data { map_area.copy_data(&self.page_table, data); @@ -98,14 +95,14 @@ impl MemorySet { // map trampoline memory_set.map_trampoline(); // map kernel sections - println!(".text [{:#x}, {:#x})", stext as usize, etext as usize); - println!(".rodata [{:#x}, {:#x})", srodata as usize, erodata as usize); - println!(".data [{:#x}, {:#x})", sdata as usize, edata as usize); - println!( - ".bss [{:#x}, {:#x})", - sbss_with_stack as usize, ebss as usize - ); - println!("mapping .text section"); + // println!(".text [{:#x}, {:#x})", stext as usize, etext as usize); + // println!(".rodata [{:#x}, {:#x})", srodata as usize, erodata as usize); + // println!(".data [{:#x}, {:#x})", sdata as usize, edata as usize); + // println!( + // ".bss [{:#x}, {:#x})", + // sbss_with_stack as usize, ebss as usize + // ); + // println!("mapping .text section"); memory_set.push( MapArea::new( (stext as usize).into(), @@ -115,7 +112,7 @@ impl MemorySet { ), None, ); - println!("mapping .rodata section"); + // println!("mapping .rodata section"); memory_set.push( MapArea::new( (srodata as usize).into(), @@ -125,7 +122,7 @@ impl MemorySet { ), None, ); - println!("mapping .data section"); + // println!("mapping .data section"); memory_set.push( MapArea::new( (sdata as usize).into(), @@ -135,7 +132,7 @@ impl MemorySet { ), None, ); - println!("mapping .bss section"); + // println!("mapping .bss section"); memory_set.push( MapArea::new( (sbss_with_stack as usize).into(), @@ -145,7 +142,7 @@ impl MemorySet { ), None, ); - println!("mapping physical memory"); + // println!("mapping physical memory"); memory_set.push( MapArea::new( (ekernel as usize).into(), @@ -155,7 +152,7 @@ impl MemorySet { ), None, ); - println!("mapping memory-mapped registers"); + //println!("mapping memory-mapped registers"); for pair in MMIO { memory_set.push( MapArea::new( @@ -169,8 +166,8 @@ impl MemorySet { } memory_set } - /// Include sections in elf and trampoline and TrapContext and user stack, - /// also returns user_sp and entry point. + /// Include sections in elf and trampoline, + /// also returns user_sp_base and entry point. pub fn from_elf(elf_data: &[u8]) -> (Self, usize, usize) { let mut memory_set = Self::new_bare(); // map trampoline @@ -206,38 +203,15 @@ impl MemorySet { ); } } - // map user stack with U flags let max_end_va: VirtAddr = max_end_vpn.into(); - let mut user_stack_bottom: usize = max_end_va.into(); - // guard page - user_stack_bottom += PAGE_SIZE; - let user_stack_top = user_stack_bottom + USER_STACK_SIZE; - memory_set.push( - MapArea::new( - user_stack_bottom.into(), - user_stack_top.into(), - MapType::Framed, - MapPermission::R | MapPermission::W | MapPermission::U, - ), - None, - ); - // map TrapContext - memory_set.push( - MapArea::new( - TRAP_CONTEXT.into(), - TRAMPOLINE.into(), - MapType::Framed, - MapPermission::R | MapPermission::W, - ), - None, - ); + let mut user_stack_base: usize = max_end_va.into(); + user_stack_base += PAGE_SIZE; ( memory_set, - user_stack_top, + user_stack_base, elf.header.pt2.entry_point() as usize, ) } - ///Clone a same `MemorySet` pub fn from_existed_user(user_space: &MemorySet) -> MemorySet { let mut memory_set = Self::new_bare(); // map trampoline @@ -257,7 +231,6 @@ impl MemorySet { } memory_set } - ///Refresh TLB with `sfence.vma` pub fn activate(&self) { let satp = self.page_table.token(); unsafe { @@ -265,17 +238,15 @@ impl MemorySet { asm!("sfence.vma"); } } - ///Translate throuth pagetable pub fn translate(&self, vpn: VirtPageNum) -> Option { self.page_table.translate(vpn) } - ///Remove all `MapArea` pub fn recycle_data_pages(&mut self) { //*self = Self::new_bare(); self.areas.clear(); } } -/// map area structure, controls a contiguous piece of virtual memory + pub struct MapArea { vpn_range: VPNRange, data_frames: BTreeMap, @@ -318,6 +289,11 @@ impl MapArea { ppn = frame.ppn; self.data_frames.insert(vpn, frame); } + MapType::Linear(pn_offset) => { + // check for sv39 + assert!(vpn.0 < (1usize << 27)); + ppn = PhysPageNum((vpn.0 as isize + pn_offset) as usize); + } } let pte_flags = PTEFlags::from_bits(self.map_perm.bits).unwrap(); page_table.map(vpn, ppn, pte_flags); @@ -363,53 +339,42 @@ impl MapArea { } #[derive(Copy, Clone, PartialEq, Debug)] -/// map type for memory set: identical or framed pub enum MapType { Identical, Framed, + /// offset of page num + Linear(isize), } bitflags! { - /// map permission corresponding to that in pte: `R W X U` pub struct MapPermission: u8 { - ///Readable const R = 1 << 1; - ///Writable const W = 1 << 2; - ///Excutable const X = 1 << 3; - ///Accessible in U mode const U = 1 << 4; } } #[allow(unused)] -///Check PageTable running correctly pub fn remap_test() { let mut kernel_space = KERNEL_SPACE.exclusive_access(); let mid_text: VirtAddr = ((stext as usize + etext as usize) / 2).into(); let mid_rodata: VirtAddr = ((srodata as usize + erodata as usize) / 2).into(); let mid_data: VirtAddr = ((sdata as usize + edata as usize) / 2).into(); - assert!( - !kernel_space - .page_table - .translate(mid_text.floor()) - .unwrap() - .writable(), - ); - assert!( - !kernel_space - .page_table - .translate(mid_rodata.floor()) - .unwrap() - .writable(), - ); - assert!( - !kernel_space - .page_table - .translate(mid_data.floor()) - .unwrap() - .executable(), - ); + assert!(!kernel_space + .page_table + .translate(mid_text.floor()) + .unwrap() + .writable(),); + assert!(!kernel_space + .page_table + .translate(mid_rodata.floor()) + .unwrap() + .writable(),); + assert!(!kernel_space + .page_table + .translate(mid_data.floor()) + .unwrap() + .executable(),); println!("remap_test passed!"); } diff --git a/os/src/mm/mod.rs b/os/src/mm/mod.rs index 3255725..642d0b7 100644 --- a/os/src/mm/mod.rs +++ b/os/src/mm/mod.rs @@ -1,27 +1,19 @@ -//! Memory management implementation -//! -//! SV39 page-based virtual-memory architecture for RV64 systems, and -//! everything about memory management, like frame allocator, page table, -//! map area and memory set, is implemented here. -//! -//! Every task or process has a memory_set to control its virtual memory. mod address; mod frame_allocator; mod heap_allocator; mod memory_set; mod page_table; -use address::VPNRange; +pub use address::VPNRange; pub use address::{PhysAddr, PhysPageNum, StepByOne, VirtAddr, VirtPageNum}; -pub use frame_allocator::{FrameTracker, frame_alloc, frame_dealloc}; -pub use memory_set::remap_test; -pub use memory_set::{KERNEL_SPACE, MapPermission, MemorySet, kernel_token}; +pub use frame_allocator::{frame_alloc, frame_alloc_more, frame_dealloc, FrameTracker}; +pub use memory_set::{kernel_token, MapArea, MapPermission, MapType, MemorySet, KERNEL_SPACE}; use page_table::PTEFlags; pub use page_table::{ - PageTable, PageTableEntry, UserBuffer, UserBufferIterator, translated_byte_buffer, - translated_ref, translated_refmut, translated_str, + translated_byte_buffer, translated_ref, translated_refmut, translated_str, PageTable, + PageTableEntry, UserBuffer, }; -/// initiate heap allocator, frame allocator and kernel space + pub fn init() { heap_allocator::init_heap(); frame_allocator::init_frame_allocator(); diff --git a/os/src/mm/page_table.rs b/os/src/mm/page_table.rs index f5fdf2b..dfaf4b6 100644 --- a/os/src/mm/page_table.rs +++ b/os/src/mm/page_table.rs @@ -1,6 +1,4 @@ -//! Implementation of [`PageTableEntry`] and [`PageTable`]. - -use super::{FrameTracker, PhysAddr, PhysPageNum, StepByOne, VirtAddr, VirtPageNum, frame_alloc}; +use super::{frame_alloc, FrameTracker, PhysAddr, PhysPageNum, StepByOne, VirtAddr, VirtPageNum}; use alloc::string::String; use alloc::vec; use alloc::vec::Vec; @@ -21,49 +19,39 @@ bitflags! { #[derive(Copy, Clone)] #[repr(C)] -/// page table entry structure pub struct PageTableEntry { - ///PTE pub bits: usize, } impl PageTableEntry { - ///Create a PTE from ppn pub fn new(ppn: PhysPageNum, flags: PTEFlags) -> Self { PageTableEntry { bits: ppn.0 << 10 | flags.bits as usize, } } - ///Return an empty PTE pub fn empty() -> Self { PageTableEntry { bits: 0 } } - ///Return 44bit ppn pub fn ppn(&self) -> PhysPageNum { (self.bits >> 10 & ((1usize << 44) - 1)).into() } - ///Return 10bit flag pub fn flags(&self) -> PTEFlags { PTEFlags::from_bits(self.bits as u8).unwrap() } - ///Check PTE valid pub fn is_valid(&self) -> bool { (self.flags() & PTEFlags::V) != PTEFlags::empty() } - ///Check PTE readable pub fn readable(&self) -> bool { (self.flags() & PTEFlags::R) != PTEFlags::empty() } - ///Check PTE writable pub fn writable(&self) -> bool { (self.flags() & PTEFlags::W) != PTEFlags::empty() } - ///Check PTE executable pub fn executable(&self) -> bool { (self.flags() & PTEFlags::X) != PTEFlags::empty() } } -///Record root ppn and has the same lifetime as 1 and 2 level `PageTableEntry` + pub struct PageTable { root_ppn: PhysPageNum, frames: Vec, @@ -71,7 +59,6 @@ pub struct PageTable { /// Assume that it won't oom when creating/mapping. impl PageTable { - /// Create an empty `PageTable` pub fn new() -> Self { let frame = frame_alloc().unwrap(); PageTable { @@ -86,7 +73,6 @@ impl PageTable { frames: Vec::new(), } } - /// Find phsical address by virtual address, create a frame if not exist fn find_pte_create(&mut self, vpn: VirtPageNum) -> Option<&mut PageTableEntry> { let idxs = vpn.indexes(); let mut ppn = self.root_ppn; @@ -106,7 +92,6 @@ impl PageTable { } result } - /// Find phsical address by virtual address fn find_pte(&self, vpn: VirtPageNum) -> Option<&mut PageTableEntry> { let idxs = vpn.indexes(); let mut ppn = self.root_ppn; @@ -125,24 +110,20 @@ impl PageTable { result } #[allow(unused)] - /// Create a mapping form `vpn` to `ppn` pub fn map(&mut self, vpn: VirtPageNum, ppn: PhysPageNum, flags: PTEFlags) { let pte = self.find_pte_create(vpn).unwrap(); assert!(!pte.is_valid(), "vpn {:?} is mapped before mapping", vpn); *pte = PageTableEntry::new(ppn, flags | PTEFlags::V); } #[allow(unused)] - /// Delete a mapping form `vpn` pub fn unmap(&mut self, vpn: VirtPageNum) { let pte = self.find_pte(vpn).unwrap(); assert!(pte.is_valid(), "vpn {:?} is invalid before unmapping", vpn); *pte = PageTableEntry::empty(); } - /// Translate `VirtPageNum` to `PageTableEntry` pub fn translate(&self, vpn: VirtPageNum) -> Option { self.find_pte(vpn).map(|pte| *pte) } - /// Translate `VirtAddr` to `PhysAddr` pub fn translate_va(&self, va: VirtAddr) -> Option { self.find_pte(va.clone().floor()).map(|pte| { let aligned_pa: PhysAddr = pte.ppn().into(); @@ -151,12 +132,11 @@ impl PageTable { (aligned_pa_usize + offset).into() }) } - /// Get root ppn pub fn token(&self) -> usize { 8usize << 60 | self.root_ppn.0 } } -/// Translate a pointer to a mutable u8 Vec through page table + pub fn translated_byte_buffer(token: usize, ptr: *const u8, len: usize) -> Vec<&'static mut [u8]> { let page_table = PageTable::from_token(token); let mut start = ptr as usize; @@ -179,7 +159,7 @@ pub fn translated_byte_buffer(token: usize, ptr: *const u8, len: usize) -> Vec<& v } -/// Translate a pointer to a mutable u8 Vec end with `\0` through page table to a `String` +/// Load a string from other address spaces into kernel space without an end `\0`. pub fn translated_str(token: usize, ptr: *const u8) -> String { let page_table = PageTable::from_token(token); let mut string = String::new(); @@ -198,8 +178,6 @@ pub fn translated_str(token: usize, ptr: *const u8) -> String { string } -#[allow(unused)] -///Translate a generic through page table and return a reference pub fn translated_ref(token: usize, ptr: *const T) -> &'static T { let page_table = PageTable::from_token(token); page_table @@ -207,7 +185,7 @@ pub fn translated_ref(token: usize, ptr: *const T) -> &'static T { .unwrap() .get_ref() } -///Translate a generic through page table and return a mutable reference + pub fn translated_refmut(token: usize, ptr: *mut T) -> &'static mut T { let page_table = PageTable::from_token(token); let va = ptr as usize; @@ -216,18 +194,15 @@ pub fn translated_refmut(token: usize, ptr: *mut T) -> &'static mut T { .unwrap() .get_mut() } -///Array of u8 slice that user communicate with os + pub struct UserBuffer { - ///U8 vec pub buffers: Vec<&'static mut [u8]>, } impl UserBuffer { - ///Create a `UserBuffer` by parameter pub fn new(buffers: Vec<&'static mut [u8]>) -> Self { Self { buffers } } - ///Length of `UserBuffer` pub fn len(&self) -> usize { let mut total: usize = 0; for b in self.buffers.iter() { @@ -248,7 +223,7 @@ impl IntoIterator for UserBuffer { } } } -/// Iterator of `UserBuffer` + pub struct UserBufferIterator { buffers: Vec<&'static mut [u8]>, current_buffer: usize, diff --git a/os/src/net/mod.rs b/os/src/net/mod.rs new file mode 100644 index 0000000..469a365 --- /dev/null +++ b/os/src/net/mod.rs @@ -0,0 +1,136 @@ +pub mod port_table; +pub mod socket; +pub mod tcp; +pub mod udp; + +pub use lose_net_stack::IPv4; + +use alloc::{sync::Arc, vec}; +use lose_net_stack::{results::Packet, LoseStack, MacAddress, TcpFlags}; + +use crate::{ + drivers::NET_DEVICE, + net::socket::{get_socket, push_data}, + sync::UPIntrFreeCell, +}; + +use self::{port_table::check_accept, socket::set_s_a_by_index}; + +pub struct NetStack(UPIntrFreeCell); + +impl NetStack { + pub fn new() -> Self { + unsafe { + NetStack(UPIntrFreeCell::new(LoseStack::new( + IPv4::new(10, 0, 2, 15), + MacAddress::new([0x52, 0x54, 0x00, 0x12, 0x34, 0x56]), + ))) + } + } +} + +lazy_static::lazy_static! { + static ref LOSE_NET_STACK: Arc = Arc::new(NetStack::new()); +} + +pub fn net_interrupt_handler() { + let mut recv_buf = vec![0u8; 1024]; + + let len = NET_DEVICE.receive(&mut recv_buf); + + let packet = LOSE_NET_STACK + .0 + .exclusive_access() + .analysis(&recv_buf[..len]); + + // println!("[kernel] receive a packet"); + // hexdump(&recv_buf[..len]); + + match packet { + Packet::ARP(arp_packet) => { + let lose_stack = LOSE_NET_STACK.0.exclusive_access(); + let reply_packet = arp_packet + .reply_packet(lose_stack.ip, lose_stack.mac) + .expect("can't build reply"); + let reply_data = reply_packet.build_data(); + NET_DEVICE.transmit(&reply_data) + } + + Packet::UDP(udp_packet) => { + let target = udp_packet.source_ip; + let lport = udp_packet.dest_port; + let rport = udp_packet.source_port; + + if let Some(socket_index) = get_socket(target, lport, rport) { + push_data(socket_index, udp_packet.data.to_vec()); + } + } + + Packet::TCP(tcp_packet) => { + let target = tcp_packet.source_ip; + let lport = tcp_packet.dest_port; + let rport = tcp_packet.source_port; + let flags = tcp_packet.flags; + + if flags.contains(TcpFlags::S) { + // if it has a port to accept, then response the request + if check_accept(lport, &tcp_packet).is_some() { + let mut reply_packet = tcp_packet.ack(); + reply_packet.flags = TcpFlags::S | TcpFlags::A; + NET_DEVICE.transmit(&reply_packet.build_data()); + } + return; + } else if tcp_packet.flags.contains(TcpFlags::F) { + // tcp disconnected + let reply_packet = tcp_packet.ack(); + NET_DEVICE.transmit(&reply_packet.build_data()); + + let mut end_packet = reply_packet.ack(); + end_packet.flags |= TcpFlags::F; + NET_DEVICE.transmit(&end_packet.build_data()); + } else if tcp_packet.flags.contains(TcpFlags::A) && tcp_packet.data_len == 0 { + return; + } + + if let Some(socket_index) = get_socket(target, lport, rport) { + push_data(socket_index, tcp_packet.data.to_vec()); + set_s_a_by_index(socket_index, tcp_packet.seq, tcp_packet.ack); + } + } + _ => {} + } +} + +#[allow(unused)] +pub fn hexdump(data: &[u8]) { + const PRELAND_WIDTH: usize = 70; + println!("[kernel] {:-^1$}", " hexdump ", PRELAND_WIDTH); + for offset in (0..data.len()).step_by(16) { + print!("[kernel] "); + for i in 0..16 { + if offset + i < data.len() { + print!("{:02x} ", data[offset + i]); + } else { + print!("{:02} ", ""); + } + } + + print!("{:>6}", ' '); + + for i in 0..16 { + if offset + i < data.len() { + let c = data[offset + i]; + if c >= 0x20 && c <= 0x7e { + print!("{}", c as char); + } else { + print!("."); + } + } else { + print!("{:02} ", ""); + } + } + + println!(""); + } + println!("[kernel] {:-^1$}", " hexdump end ", PRELAND_WIDTH); +} diff --git a/os/src/net/port_table.rs b/os/src/net/port_table.rs new file mode 100644 index 0000000..bcda046 --- /dev/null +++ b/os/src/net/port_table.rs @@ -0,0 +1,141 @@ +use alloc::{sync::Arc, vec::Vec}; +use lazy_static::lazy_static; +use lose_net_stack::packets::tcp::TCPPacket; + +use crate::fs::File; +use crate::sync::UPIntrFreeCell; +use crate::task::TaskControlBlock; + +use super::tcp::TCP; + +pub struct Port { + pub port: u16, + pub receivable: bool, + pub schedule: Option>, +} + +lazy_static! { + static ref LISTEN_TABLE: UPIntrFreeCell>> = + unsafe { UPIntrFreeCell::new(Vec::new()) }; +} + +pub fn listen(port: u16) -> Option { + let mut listen_table = LISTEN_TABLE.exclusive_access(); + let mut index = usize::MAX; + for i in 0..listen_table.len() { + if listen_table[i].is_none() { + index = i; + break; + } + } + + let listen_port = Port { + port, + receivable: false, + schedule: None, + }; + + if index == usize::MAX { + listen_table.push(Some(listen_port)); + Some(listen_table.len() - 1) + } else { + listen_table[index] = Some(listen_port); + Some(index) + } +} + +// can accept request +pub fn accept(listen_index: usize, task: Arc) { + let mut listen_table = LISTEN_TABLE.exclusive_access(); + assert!(listen_index < listen_table.len()); + let listen_port = listen_table[listen_index].as_mut(); + assert!(listen_port.is_some()); + let listen_port = listen_port.unwrap(); + listen_port.receivable = true; + listen_port.schedule = Some(task); +} + +pub fn port_acceptable(listen_index: usize) -> bool { + let mut listen_table = LISTEN_TABLE.exclusive_access(); + assert!(listen_index < listen_table.len()); + + let listen_port = listen_table[listen_index].as_mut(); + listen_port.map_or(false, |x| x.receivable) +} + +// check whether it can accept request +pub fn check_accept(port: u16, tcp_packet: &TCPPacket) -> Option<()> { + LISTEN_TABLE.exclusive_session(|listen_table| { + let mut listen_ports: Vec<&mut Option> = listen_table + .iter_mut() + .filter(|x| match x { + Some(t) => t.port == port && t.receivable == true, + None => false, + }) + .collect(); + if listen_ports.len() == 0 { + None + } else { + let listen_port = listen_ports[0].as_mut().unwrap(); + let task = listen_port.schedule.clone().unwrap(); + // wakeup_task(Arc::clone(&listen_port.schedule.clone().unwrap())); + listen_port.schedule = None; + listen_port.receivable = false; + + accept_connection(port, tcp_packet, task); + Some(()) + } + }) +} + +pub fn accept_connection(_port: u16, tcp_packet: &TCPPacket, task: Arc) { + let process = task.process.upgrade().unwrap(); + let mut inner = process.inner_exclusive_access(); + let fd = inner.alloc_fd(); + + let tcp_socket = TCP::new( + tcp_packet.source_ip, + tcp_packet.dest_port, + tcp_packet.source_port, + tcp_packet.seq, + tcp_packet.ack, + ); + + inner.fd_table[fd] = Some(Arc::new(tcp_socket)); + + let cx = task.inner_exclusive_access().get_trap_cx(); + cx.x[10] = fd; +} + +// store in the fd_table, delete the listen table when close the application. +pub struct PortFd(usize); + +impl PortFd { + pub fn new(port_index: usize) -> Self { + PortFd(port_index) + } +} + +impl Drop for PortFd { + fn drop(&mut self) { + LISTEN_TABLE.exclusive_access()[self.0] = None + } +} + +impl File for PortFd { + fn readable(&self) -> bool { + false + } + + fn writable(&self) -> bool { + false + } + + fn read(&self, _buf: crate::mm::UserBuffer) -> usize { + 0 + } + + fn write(&self, _buf: crate::mm::UserBuffer) -> usize { + 0 + } +} diff --git a/os/src/net/socket.rs b/os/src/net/socket.rs new file mode 100644 index 0000000..a37ba82 --- /dev/null +++ b/os/src/net/socket.rs @@ -0,0 +1,123 @@ +use alloc::collections::VecDeque; +use alloc::vec::Vec; +use lazy_static::lazy_static; +use lose_net_stack::IPv4; + +use crate::sync::UPIntrFreeCell; + +// TODO: specify the protocol, TCP or UDP +pub struct Socket { + pub raddr: IPv4, // remote address + pub lport: u16, // local port + pub rport: u16, // rempote port + pub buffers: VecDeque>, // datas + pub seq: u32, + pub ack: u32, +} + +lazy_static! { + static ref SOCKET_TABLE: UPIntrFreeCell>> = + unsafe { UPIntrFreeCell::new(Vec::new()) }; +} + +/// get the seq and ack by socket index +pub fn get_s_a_by_index(index: usize) -> Option<(u32, u32)> { + let socket_table = SOCKET_TABLE.exclusive_access(); + + assert!(index < socket_table.len()); + + socket_table.get(index).map_or(None, |x| match x { + Some(x) => Some((x.seq, x.ack)), + None => None, + }) +} + +pub fn set_s_a_by_index(index: usize, seq: u32, ack: u32) { + let mut socket_table = SOCKET_TABLE.exclusive_access(); + + assert!(socket_table.len() > index); + assert!(socket_table[index].is_some()); + + let sock = socket_table[index].as_mut().unwrap(); + + sock.ack = ack; + sock.seq = seq; +} + +pub fn get_socket(raddr: IPv4, lport: u16, rport: u16) -> Option { + let socket_table = SOCKET_TABLE.exclusive_access(); + for i in 0..socket_table.len() { + let sock = &socket_table[i]; + if sock.is_none() { + continue; + } + + let sock = sock.as_ref().unwrap(); + if sock.raddr == raddr && sock.lport == lport && sock.rport == rport { + return Some(i); + } + } + None +} + +pub fn add_socket(raddr: IPv4, lport: u16, rport: u16) -> Option { + if get_socket(raddr, lport, rport).is_some() { + return None; + } + + let mut socket_table = SOCKET_TABLE.exclusive_access(); + let mut index = usize::MAX; + for i in 0..socket_table.len() { + if socket_table[i].is_none() { + index = i; + break; + } + } + + let socket = Socket { + raddr, + lport, + rport, + buffers: VecDeque::new(), + seq: 0, + ack: 0, + }; + + if index == usize::MAX { + socket_table.push(Some(socket)); + Some(socket_table.len() - 1) + } else { + socket_table[index] = Some(socket); + Some(index) + } +} + +pub fn remove_socket(index: usize) { + let mut socket_table = SOCKET_TABLE.exclusive_access(); + + assert!(socket_table.len() > index); + + socket_table[index] = None; +} + +pub fn push_data(index: usize, data: Vec) { + let mut socket_table = SOCKET_TABLE.exclusive_access(); + + assert!(socket_table.len() > index); + assert!(socket_table[index].is_some()); + + socket_table[index] + .as_mut() + .unwrap() + .buffers + .push_back(data); +} + +pub fn pop_data(index: usize) -> Option> { + let mut socket_table = SOCKET_TABLE.exclusive_access(); + + assert!(socket_table.len() > index); + assert!(socket_table[index].is_some()); + + socket_table[index].as_mut().unwrap().buffers.pop_front() +} diff --git a/os/src/net/tcp.rs b/os/src/net/tcp.rs new file mode 100644 index 0000000..d1f7bd2 --- /dev/null +++ b/os/src/net/tcp.rs @@ -0,0 +1,113 @@ +use alloc::vec; +use lose_net_stack::packets::tcp::TCPPacket; +use lose_net_stack::IPv4; +use lose_net_stack::MacAddress; +use lose_net_stack::TcpFlags; + +use crate::{drivers::NET_DEVICE, fs::File}; + +use super::socket::get_s_a_by_index; +use super::{ + net_interrupt_handler, + socket::{add_socket, pop_data, remove_socket}, + LOSE_NET_STACK, +}; + +// add tcp packet info to this structure +pub struct TCP { + pub target: IPv4, + pub sport: u16, + pub dport: u16, + pub seq: u32, + pub ack: u32, + pub socket_index: usize, +} + +impl TCP { + pub fn new(target: IPv4, sport: u16, dport: u16, seq: u32, ack: u32) -> Self { + let index = add_socket(target, sport, dport).expect("can't add socket"); + + Self { + target, + sport, + dport, + seq, + ack, + socket_index: index, + } + } +} + +impl File for TCP { + fn readable(&self) -> bool { + true + } + + fn writable(&self) -> bool { + true + } + + fn read(&self, mut buf: crate::mm::UserBuffer) -> usize { + loop { + if let Some(data) = pop_data(self.socket_index) { + let data_len = data.len(); + let mut left = 0; + for i in 0..buf.buffers.len() { + let buffer_i_len = buf.buffers[i].len().min(data_len - left); + + buf.buffers[i][..buffer_i_len] + .copy_from_slice(&data[left..(left + buffer_i_len)]); + + left += buffer_i_len; + if left == data_len { + break; + } + } + return left; + } else { + net_interrupt_handler(); + } + } + } + + fn write(&self, buf: crate::mm::UserBuffer) -> usize { + let lose_net_stack = LOSE_NET_STACK.0.exclusive_access(); + + let mut data = vec![0u8; buf.len()]; + + let mut left = 0; + for i in 0..buf.buffers.len() { + data[left..(left + buf.buffers[i].len())].copy_from_slice(buf.buffers[i]); + left += buf.buffers[i].len(); + } + + let len = data.len(); + + // get sock and sequence + let (ack, seq) = get_s_a_by_index(self.socket_index).map_or((0, 0), |x| x); + + let tcp_packet = TCPPacket { + source_ip: lose_net_stack.ip, + source_mac: lose_net_stack.mac, + source_port: self.sport, + dest_ip: self.target, + dest_mac: MacAddress::new([0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), + dest_port: self.dport, + data_len: len, + seq, + ack, + flags: TcpFlags::A, + win: 65535, + urg: 0, + data: data.as_ref(), + }; + NET_DEVICE.transmit(&tcp_packet.build_data()); + len + } +} + +impl Drop for TCP { + fn drop(&mut self) { + remove_socket(self.socket_index) + } +} diff --git a/os/src/net/udp.rs b/os/src/net/udp.rs new file mode 100644 index 0000000..f343ed4 --- /dev/null +++ b/os/src/net/udp.rs @@ -0,0 +1,95 @@ +use super::net_interrupt_handler; +use super::socket::{add_socket, pop_data, remove_socket}; +use super::LOSE_NET_STACK; +use super::NET_DEVICE; +use crate::fs::File; +use alloc::vec; +use lose_net_stack::packets::udp::UDPPacket; +use lose_net_stack::IPv4; +use lose_net_stack::MacAddress; + +pub struct UDP { + pub target: IPv4, + pub sport: u16, + pub dport: u16, + pub socket_index: usize, +} + +impl UDP { + pub fn new(target: IPv4, sport: u16, dport: u16) -> Self { + let index = add_socket(target, sport, dport).expect("can't add socket"); + + Self { + target, + sport, + dport, + socket_index: index, + } + } +} + +impl File for UDP { + fn readable(&self) -> bool { + true + } + + fn writable(&self) -> bool { + true + } + + fn read(&self, mut buf: crate::mm::UserBuffer) -> usize { + loop { + if let Some(data) = pop_data(self.socket_index) { + let data_len = data.len(); + let mut left = 0; + for i in 0..buf.buffers.len() { + let buffer_i_len = buf.buffers[i].len().min(data_len - left); + + buf.buffers[i][..buffer_i_len] + .copy_from_slice(&data[left..(left + buffer_i_len)]); + + left += buffer_i_len; + if left == data_len { + break; + } + } + return left; + } else { + net_interrupt_handler(); + } + } + } + + fn write(&self, buf: crate::mm::UserBuffer) -> usize { + let lose_net_stack = LOSE_NET_STACK.0.exclusive_access(); + + let mut data = vec![0u8; buf.len()]; + + let mut left = 0; + for i in 0..buf.buffers.len() { + data[left..(left + buf.buffers[i].len())].copy_from_slice(buf.buffers[i]); + left += buf.buffers[i].len(); + } + + let len = data.len(); + + let udp_packet = UDPPacket::new( + lose_net_stack.ip, + lose_net_stack.mac, + self.sport, + self.target, + MacAddress::new([0xff, 0xff, 0xff, 0xff, 0xff, 0xff]), + self.dport, + len, + data.as_ref(), + ); + NET_DEVICE.transmit(&udp_packet.build_data()); + len + } +} + +impl Drop for UDP { + fn drop(&mut self) { + remove_socket(self.socket_index) + } +} diff --git a/os/src/sbi.rs b/os/src/sbi.rs index a73476f..2c0aaa5 100644 --- a/os/src/sbi.rs +++ b/os/src/sbi.rs @@ -1,18 +1,3 @@ -//! SBI call wrappers -#![allow(unused)] - -/// use sbi call to putchar in console (qemu uart handler) -pub fn console_putchar(c: usize) { - #[allow(deprecated)] - sbi_rt::legacy::console_putchar(c); -} - -/// use sbi call to getchar from console (qemu uart handler) -pub fn console_getchar() -> usize { - #[allow(deprecated)] - sbi_rt::legacy::console_getchar() -} - /// use sbi call to set timer pub fn set_timer(timer: usize) { sbi_rt::set_timer(timer as _); @@ -20,7 +5,7 @@ pub fn set_timer(timer: usize) { /// use sbi call to shutdown the kernel pub fn shutdown(failure: bool) -> ! { - use sbi_rt::{NoReason, Shutdown, SystemFailure, system_reset}; + use sbi_rt::{system_reset, NoReason, Shutdown, SystemFailure}; if !failure { system_reset(Shutdown, NoReason); } else { diff --git a/os/src/sync/condvar.rs b/os/src/sync/condvar.rs new file mode 100644 index 0000000..d13652f --- /dev/null +++ b/os/src/sync/condvar.rs @@ -0,0 +1,58 @@ +use crate::sync::{Mutex, UPIntrFreeCell}; +use crate::task::{ + block_current_and_run_next, block_current_task, current_task, wakeup_task, TaskContext, + TaskControlBlock, +}; +use alloc::{collections::VecDeque, sync::Arc}; + +pub struct Condvar { + pub inner: UPIntrFreeCell, +} + +pub struct CondvarInner { + pub wait_queue: VecDeque>, +} + +impl Condvar { + pub fn new() -> Self { + Self { + inner: unsafe { + UPIntrFreeCell::new(CondvarInner { + wait_queue: VecDeque::new(), + }) + }, + } + } + + pub fn signal(&self) { + let mut inner = self.inner.exclusive_access(); + if let Some(task) = inner.wait_queue.pop_front() { + wakeup_task(task); + } + } + + /* + pub fn wait(&self) { + let mut inner = self.inner.exclusive_access(); + inner.wait_queue.push_back(current_task().unwrap()); + drop(inner); + block_current_and_run_next(); + } + */ + + pub fn wait_no_sched(&self) -> *mut TaskContext { + self.inner.exclusive_session(|inner| { + inner.wait_queue.push_back(current_task().unwrap()); + }); + block_current_task() + } + + pub fn wait_with_mutex(&self, mutex: Arc) { + mutex.unlock(); + self.inner.exclusive_session(|inner| { + inner.wait_queue.push_back(current_task().unwrap()); + }); + block_current_and_run_next(); + mutex.lock(); + } +} diff --git a/os/src/sync/mod.rs b/os/src/sync/mod.rs index aa04db3..d9a00b5 100644 --- a/os/src/sync/mod.rs +++ b/os/src/sync/mod.rs @@ -1,4 +1,9 @@ -//! Synchronization and interior mutability primitives +mod condvar; +mod mutex; +mod semaphore; mod up; -pub use up::UPSafeCell; +pub use condvar::Condvar; +pub use mutex::{Mutex, MutexBlocking, MutexSpin}; +pub use semaphore::Semaphore; +pub use up::{UPIntrFreeCell, UPIntrRefMut}; diff --git a/os/src/sync/mutex.rs b/os/src/sync/mutex.rs new file mode 100644 index 0000000..aaf1571 --- /dev/null +++ b/os/src/sync/mutex.rs @@ -0,0 +1,88 @@ +use super::UPIntrFreeCell; +use crate::task::TaskControlBlock; +use crate::task::{block_current_and_run_next, suspend_current_and_run_next}; +use crate::task::{current_task, wakeup_task}; +use alloc::{collections::VecDeque, sync::Arc}; + +pub trait Mutex: Sync + Send { + fn lock(&self); + fn unlock(&self); +} + +pub struct MutexSpin { + locked: UPIntrFreeCell, +} + +impl MutexSpin { + pub fn new() -> Self { + Self { + locked: unsafe { UPIntrFreeCell::new(false) }, + } + } +} + +impl Mutex for MutexSpin { + fn lock(&self) { + loop { + let mut locked = self.locked.exclusive_access(); + if *locked { + drop(locked); + suspend_current_and_run_next(); + continue; + } else { + *locked = true; + return; + } + } + } + + fn unlock(&self) { + let mut locked = self.locked.exclusive_access(); + *locked = false; + } +} + +pub struct MutexBlocking { + inner: UPIntrFreeCell, +} + +pub struct MutexBlockingInner { + locked: bool, + wait_queue: VecDeque>, +} + +impl MutexBlocking { + pub fn new() -> Self { + Self { + inner: unsafe { + UPIntrFreeCell::new(MutexBlockingInner { + locked: false, + wait_queue: VecDeque::new(), + }) + }, + } + } +} + +impl Mutex for MutexBlocking { + fn lock(&self) { + let mut mutex_inner = self.inner.exclusive_access(); + if mutex_inner.locked { + mutex_inner.wait_queue.push_back(current_task().unwrap()); + drop(mutex_inner); + block_current_and_run_next(); + } else { + mutex_inner.locked = true; + } + } + + fn unlock(&self) { + let mut mutex_inner = self.inner.exclusive_access(); + assert!(mutex_inner.locked); + if let Some(waking_task) = mutex_inner.wait_queue.pop_front() { + wakeup_task(waking_task); + } else { + mutex_inner.locked = false; + } + } +} diff --git a/os/src/sync/semaphore.rs b/os/src/sync/semaphore.rs new file mode 100644 index 0000000..bdad319 --- /dev/null +++ b/os/src/sync/semaphore.rs @@ -0,0 +1,45 @@ +use crate::sync::UPIntrFreeCell; +use crate::task::{block_current_and_run_next, current_task, wakeup_task, TaskControlBlock}; +use alloc::{collections::VecDeque, sync::Arc}; + +pub struct Semaphore { + pub inner: UPIntrFreeCell, +} + +pub struct SemaphoreInner { + pub count: isize, + pub wait_queue: VecDeque>, +} + +impl Semaphore { + pub fn new(res_count: usize) -> Self { + Self { + inner: unsafe { + UPIntrFreeCell::new(SemaphoreInner { + count: res_count as isize, + wait_queue: VecDeque::new(), + }) + }, + } + } + + pub fn up(&self) { + let mut inner = self.inner.exclusive_access(); + inner.count += 1; + if inner.count <= 0 { + if let Some(task) = inner.wait_queue.pop_front() { + wakeup_task(task); + } + } + } + + pub fn down(&self) { + let mut inner = self.inner.exclusive_access(); + inner.count -= 1; + if inner.count < 0 { + inner.wait_queue.push_back(current_task().unwrap()); + drop(inner); + block_current_and_run_next(); + } + } +} diff --git a/os/src/sync/up.rs b/os/src/sync/up.rs index 955b9dc..6c7a0f4 100644 --- a/os/src/sync/up.rs +++ b/os/src/sync/up.rs @@ -1,6 +1,9 @@ -//! Uniprocessor interior mutability primitives -use core::cell::{RefCell, RefMut}; +use core::cell::{RefCell, RefMut, UnsafeCell}; +use core::ops::{Deref, DerefMut}; +use lazy_static::*; +use riscv::register::sstatus; +/* /// Wrap a static data structure inside it so that we are /// able to access it without any `unsafe`. /// @@ -23,8 +26,115 @@ impl UPSafeCell { inner: RefCell::new(value), } } - /// Exclusive access inner data in UPSafeCell. Panic if the data has been borrowed. + /// Panic if the data has been borrowed. pub fn exclusive_access(&self) -> RefMut<'_, T> { self.inner.borrow_mut() } } +*/ + +pub struct UPSafeCellRaw { + inner: UnsafeCell, +} + +unsafe impl Sync for UPSafeCellRaw {} + +impl UPSafeCellRaw { + pub unsafe fn new(value: T) -> Self { + Self { + inner: UnsafeCell::new(value), + } + } + pub fn get_mut(&self) -> &mut T { + unsafe { &mut (*self.inner.get()) } + } +} + +pub struct IntrMaskingInfo { + nested_level: usize, + sie_before_masking: bool, +} + +lazy_static! { + static ref INTR_MASKING_INFO: UPSafeCellRaw = + unsafe { UPSafeCellRaw::new(IntrMaskingInfo::new()) }; +} + +impl IntrMaskingInfo { + pub fn new() -> Self { + Self { + nested_level: 0, + sie_before_masking: false, + } + } + + pub fn enter(&mut self) { + let sie = sstatus::read().sie(); + unsafe { + sstatus::clear_sie(); + } + if self.nested_level == 0 { + self.sie_before_masking = sie; + } + self.nested_level += 1; + } + + pub fn exit(&mut self) { + self.nested_level -= 1; + if self.nested_level == 0 && self.sie_before_masking { + unsafe { + sstatus::set_sie(); + } + } + } +} + +pub struct UPIntrFreeCell { + /// inner data + inner: RefCell, +} + +unsafe impl Sync for UPIntrFreeCell {} + +pub struct UPIntrRefMut<'a, T>(Option>); + +impl UPIntrFreeCell { + pub unsafe fn new(value: T) -> Self { + Self { + inner: RefCell::new(value), + } + } + + /// Panic if the data has been borrowed. + pub fn exclusive_access(&self) -> UPIntrRefMut<'_, T> { + INTR_MASKING_INFO.get_mut().enter(); + UPIntrRefMut(Some(self.inner.borrow_mut())) + } + + pub fn exclusive_session(&self, f: F) -> V + where + F: FnOnce(&mut T) -> V, + { + let mut inner = self.exclusive_access(); + f(inner.deref_mut()) + } +} + +impl<'a, T> Drop for UPIntrRefMut<'a, T> { + fn drop(&mut self) { + self.0 = None; + INTR_MASKING_INFO.get_mut().exit(); + } +} + +impl<'a, T> Deref for UPIntrRefMut<'a, T> { + type Target = T; + fn deref(&self) -> &Self::Target { + self.0.as_ref().unwrap().deref() + } +} +impl<'a, T> DerefMut for UPIntrRefMut<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0.as_mut().unwrap().deref_mut() + } +} diff --git a/os/src/syscall/fs.rs b/os/src/syscall/fs.rs index 8d46fad..2758825 100644 --- a/os/src/syscall/fs.rs +++ b/os/src/syscall/fs.rs @@ -1,12 +1,12 @@ -//! File and filesystem-related syscalls -use crate::fs::{OpenFlags, open_file}; -use crate::mm::{UserBuffer, translated_byte_buffer, translated_str}; -use crate::task::{current_task, current_user_token}; +use crate::fs::{make_pipe, open_file, OpenFlags}; +use crate::mm::{translated_byte_buffer, translated_refmut, translated_str, UserBuffer}; +use crate::task::{current_process, current_user_token}; +use alloc::sync::Arc; pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize { let token = current_user_token(); - let task = current_task().unwrap(); - let inner = task.inner_exclusive_access(); + let process = current_process(); + let inner = process.inner_exclusive_access(); if fd >= inner.fd_table.len() { return -1; } @@ -25,8 +25,8 @@ pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize { pub fn sys_read(fd: usize, buf: *const u8, len: usize) -> isize { let token = current_user_token(); - let task = current_task().unwrap(); - let inner = task.inner_exclusive_access(); + let process = current_process(); + let inner = process.inner_exclusive_access(); if fd >= inner.fd_table.len() { return -1; } @@ -44,18 +44,11 @@ pub fn sys_read(fd: usize, buf: *const u8, len: usize) -> isize { } pub fn sys_open(path: *const u8, flags: u32) -> isize { - let task = current_task().unwrap(); + let process = current_process(); let token = current_user_token(); let path = translated_str(token, path); - - // 简单用户检查示例:非 root 用户不能打开 /root 下文件 - let username = task.inner_exclusive_access().user.clone(); - if path.starts_with("/root") && username != "root" { - return -1; // Permission denied - } - if let Some(inode) = open_file(path.as_str(), OpenFlags::from_bits(flags).unwrap()) { - let mut inner = task.inner_exclusive_access(); + let mut inner = process.inner_exclusive_access(); let fd = inner.alloc_fd(); inner.fd_table[fd] = Some(inode); fd as isize @@ -64,10 +57,9 @@ pub fn sys_open(path: *const u8, flags: u32) -> isize { } } - pub fn sys_close(fd: usize) -> isize { - let task = current_task().unwrap(); - let mut inner = task.inner_exclusive_access(); + let process = current_process(); + let mut inner = process.inner_exclusive_access(); if fd >= inner.fd_table.len() { return -1; } @@ -77,3 +69,31 @@ pub fn sys_close(fd: usize) -> isize { inner.fd_table[fd].take(); 0 } + +pub fn sys_pipe(pipe: *mut usize) -> isize { + let process = current_process(); + let token = current_user_token(); + let mut inner = process.inner_exclusive_access(); + let (pipe_read, pipe_write) = make_pipe(); + let read_fd = inner.alloc_fd(); + inner.fd_table[read_fd] = Some(pipe_read); + let write_fd = inner.alloc_fd(); + inner.fd_table[write_fd] = Some(pipe_write); + *translated_refmut(token, pipe) = read_fd; + *translated_refmut(token, unsafe { pipe.add(1) }) = write_fd; + 0 +} + +pub fn sys_dup(fd: usize) -> isize { + let process = current_process(); + let mut inner = process.inner_exclusive_access(); + if fd >= inner.fd_table.len() { + return -1; + } + if inner.fd_table[fd].is_none() { + return -1; + } + let new_fd = inner.alloc_fd(); + inner.fd_table[new_fd] = Some(Arc::clone(inner.fd_table[fd].as_ref().unwrap())); + new_fd as isize +} diff --git a/os/src/syscall/gui.rs b/os/src/syscall/gui.rs new file mode 100644 index 0000000..c5ba8a4 --- /dev/null +++ b/os/src/syscall/gui.rs @@ -0,0 +1,34 @@ +use crate::drivers::GPU_DEVICE; +use crate::mm::{MapArea, MapPermission, MapType, PhysAddr, VirtAddr}; +use crate::task::current_process; + +const FB_VADDR: usize = 0x10000000; + +pub fn sys_framebuffer() -> isize { + let fb = GPU_DEVICE.get_framebuffer(); + let len = fb.len(); + // println!("[kernel] FrameBuffer: addr 0x{:X}, len {}", fb.as_ptr() as usize , len); + let fb_start_pa = PhysAddr::from(fb.as_ptr() as usize); + assert!(fb_start_pa.aligned()); + let fb_start_ppn = fb_start_pa.floor(); + let fb_start_vpn = VirtAddr::from(FB_VADDR).floor(); + let pn_offset = fb_start_ppn.0 as isize - fb_start_vpn.0 as isize; + + let current_process = current_process(); + let mut inner = current_process.inner_exclusive_access(); + inner.memory_set.push( + MapArea::new( + (FB_VADDR as usize).into(), + (FB_VADDR + len as usize).into(), + MapType::Linear(pn_offset), + MapPermission::R | MapPermission::W | MapPermission::U, + ), + None, + ); + FB_VADDR as isize +} + +pub fn sys_framebuffer_flush() -> isize { + GPU_DEVICE.flush(); + 0 +} diff --git a/os/src/syscall/input.rs b/os/src/syscall/input.rs new file mode 100644 index 0000000..ee86bd3 --- /dev/null +++ b/os/src/syscall/input.rs @@ -0,0 +1,28 @@ +//use crate::drivers::{KEYBOARD_DEVICE,MOUSE_DEVICE,INPUT_CONDVAR,read_input_event}; +use crate::drivers::{KEYBOARD_DEVICE, MOUSE_DEVICE}; + +pub fn sys_event_get() -> isize { + let kb = KEYBOARD_DEVICE.clone(); + let mouse = MOUSE_DEVICE.clone(); + //let input=INPUT_CONDVAR.clone(); + //read_input_event() as isize + if !kb.is_empty() { + kb.read_event() as isize + } else if !mouse.is_empty() { + mouse.read_event() as isize + } else { + 0 + } +} + +use crate::drivers::chardev::UART; + +/// check UART's read-buffer is empty or not +pub fn sys_key_pressed() -> isize { + let res = !UART.read_buffer_is_empty(); + if res { + 1 + } else { + 0 + } +} diff --git a/os/src/syscall/mod.rs b/os/src/syscall/mod.rs index 8bcb326..cd12de0 100644 --- a/os/src/syscall/mod.rs +++ b/os/src/syscall/mod.rs @@ -1,45 +1,90 @@ -//! Implementation of syscalls -//! -//! The single entry point to all system calls, [`syscall()`], is called -//! whenever userspace wishes to perform a system call using the `ecall` -//! instruction. In this case, the processor raises an 'Environment call from -//! U-mode' exception, which is handled as one of the cases in -//! [`crate::trap::trap_handler`]. -//! -//! For clarity, each single syscall is implemented as its own function, named -//! `sys_` then the name of the syscall. You can find functions like this in -//! submodules, and you should also implement syscalls this way. +const SYSCALL_DUP: usize = 24; +const SYSCALL_CONNECT: usize = 29; +const SYSCALL_LISTEN: usize = 30; +const SYSCALL_ACCEPT: usize = 31; const SYSCALL_OPEN: usize = 56; const SYSCALL_CLOSE: usize = 57; +const SYSCALL_PIPE: usize = 59; const SYSCALL_READ: usize = 63; const SYSCALL_WRITE: usize = 64; const SYSCALL_EXIT: usize = 93; +const SYSCALL_SLEEP: usize = 101; const SYSCALL_YIELD: usize = 124; +const SYSCALL_KILL: usize = 129; const SYSCALL_GET_TIME: usize = 169; const SYSCALL_GETPID: usize = 172; const SYSCALL_FORK: usize = 220; const SYSCALL_EXEC: usize = 221; const SYSCALL_WAITPID: usize = 260; +const SYSCALL_THREAD_CREATE: usize = 1000; +const SYSCALL_GETTID: usize = 1001; +const SYSCALL_WAITTID: usize = 1002; +const SYSCALL_MUTEX_CREATE: usize = 1010; +const SYSCALL_MUTEX_LOCK: usize = 1011; +const SYSCALL_MUTEX_UNLOCK: usize = 1012; +const SYSCALL_SEMAPHORE_CREATE: usize = 1020; +const SYSCALL_SEMAPHORE_UP: usize = 1021; +const SYSCALL_SEMAPHORE_DOWN: usize = 1022; +const SYSCALL_CONDVAR_CREATE: usize = 1030; +const SYSCALL_CONDVAR_SIGNAL: usize = 1031; +const SYSCALL_CONDVAR_WAIT: usize = 1032; +const SYSCALL_FRAMEBUFFER: usize = 2000; +const SYSCALL_FRAMEBUFFER_FLUSH: usize = 2001; +const SYSCALL_EVENT_GET: usize = 3000; +const SYSCALL_KEY_PRESSED: usize = 3001; mod fs; +mod gui; +mod input; +mod net; mod process; +mod sync; +mod thread; use fs::*; +use gui::*; +use input::*; +use net::*; use process::*; -/// handle syscall exception with `syscall_id` and other arguments +use sync::*; +use thread::*; + pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { match syscall_id { + SYSCALL_DUP => sys_dup(args[0]), + SYSCALL_CONNECT => sys_connect(args[0] as _, args[1] as _, args[2] as _), + SYSCALL_LISTEN => sys_listen(args[0] as _), + SYSCALL_ACCEPT => sys_accept(args[0] as _), SYSCALL_OPEN => sys_open(args[0] as *const u8, args[1] as u32), SYSCALL_CLOSE => sys_close(args[0]), + SYSCALL_PIPE => sys_pipe(args[0] as *mut usize), SYSCALL_READ => sys_read(args[0], args[1] as *const u8, args[2]), SYSCALL_WRITE => sys_write(args[0], args[1] as *const u8, args[2]), SYSCALL_EXIT => sys_exit(args[0] as i32), + SYSCALL_SLEEP => sys_sleep(args[0]), SYSCALL_YIELD => sys_yield(), + SYSCALL_KILL => sys_kill(args[0], args[1] as u32), SYSCALL_GET_TIME => sys_get_time(), SYSCALL_GETPID => sys_getpid(), SYSCALL_FORK => sys_fork(), - SYSCALL_EXEC => sys_exec(args[0] as *const u8), + SYSCALL_EXEC => sys_exec(args[0] as *const u8, args[1] as *const usize), SYSCALL_WAITPID => sys_waitpid(args[0] as isize, args[1] as *mut i32), + SYSCALL_THREAD_CREATE => sys_thread_create(args[0], args[1]), + SYSCALL_GETTID => sys_gettid(), + SYSCALL_WAITTID => sys_waittid(args[0]) as isize, + SYSCALL_MUTEX_CREATE => sys_mutex_create(args[0] == 1), + SYSCALL_MUTEX_LOCK => sys_mutex_lock(args[0]), + SYSCALL_MUTEX_UNLOCK => sys_mutex_unlock(args[0]), + SYSCALL_SEMAPHORE_CREATE => sys_semaphore_create(args[0]), + SYSCALL_SEMAPHORE_UP => sys_semaphore_up(args[0]), + SYSCALL_SEMAPHORE_DOWN => sys_semaphore_down(args[0]), + SYSCALL_CONDVAR_CREATE => sys_condvar_create(), + SYSCALL_CONDVAR_SIGNAL => sys_condvar_signal(args[0]), + SYSCALL_CONDVAR_WAIT => sys_condvar_wait(args[0], args[1]), + SYSCALL_FRAMEBUFFER => sys_framebuffer(), + SYSCALL_FRAMEBUFFER_FLUSH => sys_framebuffer_flush(), + SYSCALL_EVENT_GET => sys_event_get(), + SYSCALL_KEY_PRESSED => sys_key_pressed(), _ => panic!("Unsupported syscall_id: {}", syscall_id), } } diff --git a/os/src/syscall/net.rs b/os/src/syscall/net.rs new file mode 100644 index 0000000..c16b9ce --- /dev/null +++ b/os/src/syscall/net.rs @@ -0,0 +1,53 @@ +use crate::net::port_table::{accept, listen, port_acceptable, PortFd}; +use crate::net::udp::UDP; +use crate::net::{net_interrupt_handler, IPv4}; +use crate::task::{current_process, current_task, current_trap_cx}; +use alloc::sync::Arc; + +// just support udp +pub fn sys_connect(raddr: u32, lport: u16, rport: u16) -> isize { + let process = current_process(); + let mut inner = process.inner_exclusive_access(); + let fd = inner.alloc_fd(); + let udp_node = UDP::new(IPv4::from_u32(raddr), lport, rport); + inner.fd_table[fd] = Some(Arc::new(udp_node)); + fd as isize +} + +// listen a port +pub fn sys_listen(port: u16) -> isize { + match listen(port) { + Some(port_index) => { + let process = current_process(); + let mut inner = process.inner_exclusive_access(); + let fd = inner.alloc_fd(); + let port_fd = PortFd::new(port_index); + inner.fd_table[fd] = Some(Arc::new(port_fd)); + + // NOTICE: this return the port index, not the fd + port_index as isize + } + None => -1, + } +} + +// accept a tcp connection +pub fn sys_accept(port_index: usize) -> isize { + println!("accepting port {}", port_index); + + let task = current_task().unwrap(); + accept(port_index, task); + // block_current_and_run_next(); + + // NOTICE: There does not have interrupt handler, just call it munually. + loop { + net_interrupt_handler(); + + if !port_acceptable(port_index) { + break; + } + } + + let cx = current_trap_cx(); + cx.x[10] as isize +} diff --git a/os/src/syscall/process.rs b/os/src/syscall/process.rs index 61e78d6..7d5b67a 100644 --- a/os/src/syscall/process.rs +++ b/os/src/syscall/process.rs @@ -1,11 +1,13 @@ -use crate::fs::{OpenFlags, open_file}; -use crate::mm::{translated_refmut, translated_str}; +use crate::fs::{open_file, OpenFlags}; +use crate::mm::{translated_ref, translated_refmut, translated_str}; use crate::task::{ - add_task, current_task, current_user_token, exit_current_and_run_next, - suspend_current_and_run_next, + current_process, current_task, current_user_token, exit_current_and_run_next, pid2process, + suspend_current_and_run_next, SignalFlags, }; use crate::timer::get_time_ms; +use alloc::string::String; use alloc::sync::Arc; +use alloc::vec::Vec; pub fn sys_exit(exit_code: i32) -> ! { exit_current_and_run_next(exit_code); @@ -22,31 +24,44 @@ pub fn sys_get_time() -> isize { } pub fn sys_getpid() -> isize { - current_task().unwrap().pid.0 as isize + current_task().unwrap().process.upgrade().unwrap().getpid() as isize } pub fn sys_fork() -> isize { - let current_task = current_task().unwrap(); - let new_task = current_task.fork(); - let new_pid = new_task.pid.0; + let current_process = current_process(); + let new_process = current_process.fork(); + let new_pid = new_process.getpid(); // modify trap context of new_task, because it returns immediately after switching - let trap_cx = new_task.inner_exclusive_access().get_trap_cx(); + let new_process_inner = new_process.inner_exclusive_access(); + let task = new_process_inner.tasks[0].as_ref().unwrap(); + let trap_cx = task.inner_exclusive_access().get_trap_cx(); // we do not have to move to next instruction since we have done it before // for child process, fork returns 0 trap_cx.x[10] = 0; - // add new task to scheduler - add_task(new_task); new_pid as isize } -pub fn sys_exec(path: *const u8) -> isize { +pub fn sys_exec(path: *const u8, mut args: *const usize) -> isize { let token = current_user_token(); let path = translated_str(token, path); + let mut args_vec: Vec = Vec::new(); + loop { + let arg_str_ptr = *translated_ref(token, args); + if arg_str_ptr == 0 { + break; + } + args_vec.push(translated_str(token, arg_str_ptr as *const u8)); + unsafe { + args = args.add(1); + } + } if let Some(app_inode) = open_file(path.as_str(), OpenFlags::RDONLY) { let all_data = app_inode.read_all(); - let task = current_task().unwrap(); - task.exec(all_data.as_slice()); - 0 + let process = current_process(); + let argc = args_vec.len(); + process.exec(all_data.as_slice(), args_vec); + // return argc because cx.x[10] will be covered with it later + argc as isize } else { -1 } @@ -55,11 +70,10 @@ pub fn sys_exec(path: *const u8) -> isize { /// If there is not a child process whose pid is same as given, return -1. /// Else if there is a child process but it is still running, return -2. pub fn sys_waitpid(pid: isize, exit_code_ptr: *mut i32) -> isize { - let task = current_task().unwrap(); + let process = current_process(); // find a child process - // ---- access current PCB exclusively - let mut inner = task.inner_exclusive_access(); + let mut inner = process.inner_exclusive_access(); if !inner .children .iter() @@ -70,7 +84,7 @@ pub fn sys_waitpid(pid: isize, exit_code_ptr: *mut i32) -> isize { } let pair = inner.children.iter().enumerate().find(|(_, p)| { // ++++ temporarily access child PCB exclusively - p.inner_exclusive_access().is_zombie() && (pid == -1 || pid as usize == p.getpid()) + p.inner_exclusive_access().is_zombie && (pid == -1 || pid as usize == p.getpid()) // ++++ release child PCB }); if let Some((idx, _)) = pair { @@ -88,3 +102,16 @@ pub fn sys_waitpid(pid: isize, exit_code_ptr: *mut i32) -> isize { } // ---- release current PCB automatically } + +pub fn sys_kill(pid: usize, signal: u32) -> isize { + if let Some(process) = pid2process(pid) { + if let Some(flag) = SignalFlags::from_bits(signal) { + process.inner_exclusive_access().signals |= flag; + 0 + } else { + -1 + } + } else { + -1 + } +} diff --git a/os/src/syscall/sync.rs b/os/src/syscall/sync.rs new file mode 100644 index 0000000..1180669 --- /dev/null +++ b/os/src/syscall/sync.rs @@ -0,0 +1,134 @@ +use crate::sync::{Condvar, Mutex, MutexBlocking, MutexSpin, Semaphore}; +use crate::task::{block_current_and_run_next, current_process, current_task}; +use crate::timer::{add_timer, get_time_ms}; +use alloc::sync::Arc; + +pub fn sys_sleep(ms: usize) -> isize { + let expire_ms = get_time_ms() + ms; + let task = current_task().unwrap(); + add_timer(expire_ms, task); + block_current_and_run_next(); + 0 +} + +pub fn sys_mutex_create(blocking: bool) -> isize { + let process = current_process(); + let mutex: Option> = if !blocking { + Some(Arc::new(MutexSpin::new())) + } else { + Some(Arc::new(MutexBlocking::new())) + }; + let mut process_inner = process.inner_exclusive_access(); + if let Some(id) = process_inner + .mutex_list + .iter() + .enumerate() + .find(|(_, item)| item.is_none()) + .map(|(id, _)| id) + { + process_inner.mutex_list[id] = mutex; + id as isize + } else { + process_inner.mutex_list.push(mutex); + process_inner.mutex_list.len() as isize - 1 + } +} + +pub fn sys_mutex_lock(mutex_id: usize) -> isize { + let process = current_process(); + let process_inner = process.inner_exclusive_access(); + let mutex = Arc::clone(process_inner.mutex_list[mutex_id].as_ref().unwrap()); + drop(process_inner); + drop(process); + mutex.lock(); + 0 +} + +pub fn sys_mutex_unlock(mutex_id: usize) -> isize { + let process = current_process(); + let process_inner = process.inner_exclusive_access(); + let mutex = Arc::clone(process_inner.mutex_list[mutex_id].as_ref().unwrap()); + drop(process_inner); + drop(process); + mutex.unlock(); + 0 +} + +pub fn sys_semaphore_create(res_count: usize) -> isize { + let process = current_process(); + let mut process_inner = process.inner_exclusive_access(); + let id = if let Some(id) = process_inner + .semaphore_list + .iter() + .enumerate() + .find(|(_, item)| item.is_none()) + .map(|(id, _)| id) + { + process_inner.semaphore_list[id] = Some(Arc::new(Semaphore::new(res_count))); + id + } else { + process_inner + .semaphore_list + .push(Some(Arc::new(Semaphore::new(res_count)))); + process_inner.semaphore_list.len() - 1 + }; + id as isize +} + +pub fn sys_semaphore_up(sem_id: usize) -> isize { + let process = current_process(); + let process_inner = process.inner_exclusive_access(); + let sem = Arc::clone(process_inner.semaphore_list[sem_id].as_ref().unwrap()); + drop(process_inner); + sem.up(); + 0 +} + +pub fn sys_semaphore_down(sem_id: usize) -> isize { + let process = current_process(); + let process_inner = process.inner_exclusive_access(); + let sem = Arc::clone(process_inner.semaphore_list[sem_id].as_ref().unwrap()); + drop(process_inner); + sem.down(); + 0 +} + +pub fn sys_condvar_create() -> isize { + let process = current_process(); + let mut process_inner = process.inner_exclusive_access(); + let id = if let Some(id) = process_inner + .condvar_list + .iter() + .enumerate() + .find(|(_, item)| item.is_none()) + .map(|(id, _)| id) + { + process_inner.condvar_list[id] = Some(Arc::new(Condvar::new())); + id + } else { + process_inner + .condvar_list + .push(Some(Arc::new(Condvar::new()))); + process_inner.condvar_list.len() - 1 + }; + id as isize +} + +pub fn sys_condvar_signal(condvar_id: usize) -> isize { + let process = current_process(); + let process_inner = process.inner_exclusive_access(); + let condvar = Arc::clone(process_inner.condvar_list[condvar_id].as_ref().unwrap()); + drop(process_inner); + condvar.signal(); + 0 +} + +pub fn sys_condvar_wait(condvar_id: usize, mutex_id: usize) -> isize { + let process = current_process(); + let process_inner = process.inner_exclusive_access(); + let condvar = Arc::clone(process_inner.condvar_list[condvar_id].as_ref().unwrap()); + let mutex = Arc::clone(process_inner.mutex_list[mutex_id].as_ref().unwrap()); + drop(process_inner); + condvar.wait_with_mutex(mutex); + 0 +} diff --git a/os/src/syscall/thread.rs b/os/src/syscall/thread.rs new file mode 100644 index 0000000..3955d9d --- /dev/null +++ b/os/src/syscall/thread.rs @@ -0,0 +1,85 @@ +use crate::{ + mm::kernel_token, + task::{add_task, current_task, TaskControlBlock}, + trap::{trap_handler, TrapContext}, +}; +use alloc::sync::Arc; + +pub fn sys_thread_create(entry: usize, arg: usize) -> isize { + let task = current_task().unwrap(); + let process = task.process.upgrade().unwrap(); + // create a new thread + let new_task = Arc::new(TaskControlBlock::new( + Arc::clone(&process), + task.inner_exclusive_access() + .res + .as_ref() + .unwrap() + .ustack_base, + true, + )); + // add new task to scheduler + add_task(Arc::clone(&new_task)); + let new_task_inner = new_task.inner_exclusive_access(); + let new_task_res = new_task_inner.res.as_ref().unwrap(); + let new_task_tid = new_task_res.tid; + let mut process_inner = process.inner_exclusive_access(); + // add new thread to current process + let tasks = &mut process_inner.tasks; + while tasks.len() < new_task_tid + 1 { + tasks.push(None); + } + tasks[new_task_tid] = Some(Arc::clone(&new_task)); + let new_task_trap_cx = new_task_inner.get_trap_cx(); + *new_task_trap_cx = TrapContext::app_init_context( + entry, + new_task_res.ustack_top(), + kernel_token(), + new_task.kstack.get_top(), + trap_handler as usize, + ); + (*new_task_trap_cx).x[10] = arg; + new_task_tid as isize +} + +pub fn sys_gettid() -> isize { + current_task() + .unwrap() + .inner_exclusive_access() + .res + .as_ref() + .unwrap() + .tid as isize +} + +/// thread does not exist, return -1 +/// thread has not exited yet, return -2 +/// otherwise, return thread's exit code +pub fn sys_waittid(tid: usize) -> i32 { + let task = current_task().unwrap(); + let process = task.process.upgrade().unwrap(); + let task_inner = task.inner_exclusive_access(); + let mut process_inner = process.inner_exclusive_access(); + // a thread cannot wait for itself + if task_inner.res.as_ref().unwrap().tid == tid { + return -1; + } + let mut exit_code: Option = None; + let waited_task = process_inner.tasks[tid].as_ref(); + if let Some(waited_task) = waited_task { + if let Some(waited_exit_code) = waited_task.inner_exclusive_access().exit_code { + exit_code = Some(waited_exit_code); + } + } else { + // waited thread does not exist + return -1; + } + if let Some(exit_code) = exit_code { + // dealloc the exited thread + process_inner.tasks[tid] = None; + exit_code + } else { + // waited thread has not exited + -2 + } +} diff --git a/os/src/task/context.rs b/os/src/task/context.rs index 520bce1..e4f59d8 100644 --- a/os/src/task/context.rs +++ b/os/src/task/context.rs @@ -1,19 +1,13 @@ -//! Implementation of [`TaskContext`] use crate::trap::trap_return; #[repr(C)] -/// task context structure containing some registers pub struct TaskContext { - /// return address ( e.g. __restore ) of __switch ASM function ra: usize, - /// kernel stack pointer of app sp: usize, - /// s0-11 register, callee saved s: [usize; 12], } impl TaskContext { - /// init task context pub fn zero_init() -> Self { Self { ra: 0, @@ -21,7 +15,6 @@ impl TaskContext { s: [0; 12], } } - /// set Task Context{__restore ASM funciton: trap_return, sp: kstack_ptr, s: s_0..12} pub fn goto_trap_return(kstack_ptr: usize) -> Self { Self { ra: trap_return as usize, diff --git a/os/src/task/id.rs b/os/src/task/id.rs new file mode 100644 index 0000000..23ddf7b --- /dev/null +++ b/os/src/task/id.rs @@ -0,0 +1,226 @@ +use super::ProcessControlBlock; +use crate::config::{KERNEL_STACK_SIZE, PAGE_SIZE, TRAMPOLINE, TRAP_CONTEXT_BASE, USER_STACK_SIZE}; +use crate::mm::{MapPermission, PhysPageNum, VirtAddr, KERNEL_SPACE}; +use crate::sync::UPIntrFreeCell; +use alloc::{ + sync::{Arc, Weak}, + vec::Vec, +}; +use lazy_static::*; + +pub struct RecycleAllocator { + current: usize, + recycled: Vec, +} + +impl RecycleAllocator { + pub fn new() -> Self { + RecycleAllocator { + current: 0, + recycled: Vec::new(), + } + } + pub fn alloc(&mut self) -> usize { + if let Some(id) = self.recycled.pop() { + id + } else { + self.current += 1; + self.current - 1 + } + } + pub fn dealloc(&mut self, id: usize) { + assert!(id < self.current); + assert!( + !self.recycled.iter().any(|i| *i == id), + "id {} has been deallocated!", + id + ); + self.recycled.push(id); + } +} + +lazy_static! { + static ref PID_ALLOCATOR: UPIntrFreeCell = + unsafe { UPIntrFreeCell::new(RecycleAllocator::new()) }; + static ref KSTACK_ALLOCATOR: UPIntrFreeCell = + unsafe { UPIntrFreeCell::new(RecycleAllocator::new()) }; +} + +pub const IDLE_PID: usize = 0; + +pub struct PidHandle(pub usize); + +pub fn pid_alloc() -> PidHandle { + PidHandle(PID_ALLOCATOR.exclusive_access().alloc()) +} + +impl Drop for PidHandle { + fn drop(&mut self) { + PID_ALLOCATOR.exclusive_access().dealloc(self.0); + } +} + +/// Return (bottom, top) of a kernel stack in kernel space. +pub fn kernel_stack_position(kstack_id: usize) -> (usize, usize) { + let top = TRAMPOLINE - kstack_id * (KERNEL_STACK_SIZE + PAGE_SIZE); + let bottom = top - KERNEL_STACK_SIZE; + (bottom, top) +} + +pub struct KernelStack(pub usize); + +pub fn kstack_alloc() -> KernelStack { + let kstack_id = KSTACK_ALLOCATOR.exclusive_access().alloc(); + let (kstack_bottom, kstack_top) = kernel_stack_position(kstack_id); + KERNEL_SPACE.exclusive_access().insert_framed_area( + kstack_bottom.into(), + kstack_top.into(), + MapPermission::R | MapPermission::W, + ); + KernelStack(kstack_id) +} + +impl Drop for KernelStack { + fn drop(&mut self) { + let (kernel_stack_bottom, _) = kernel_stack_position(self.0); + let kernel_stack_bottom_va: VirtAddr = kernel_stack_bottom.into(); + KERNEL_SPACE + .exclusive_access() + .remove_area_with_start_vpn(kernel_stack_bottom_va.into()); + KSTACK_ALLOCATOR.exclusive_access().dealloc(self.0); + } +} + +impl KernelStack { + #[allow(unused)] + pub fn push_on_top(&self, value: T) -> *mut T + where + T: Sized, + { + let kernel_stack_top = self.get_top(); + let ptr_mut = (kernel_stack_top - core::mem::size_of::()) as *mut T; + unsafe { + *ptr_mut = value; + } + ptr_mut + } + pub fn get_top(&self) -> usize { + let (_, kernel_stack_top) = kernel_stack_position(self.0); + kernel_stack_top + } +} + +pub struct TaskUserRes { + pub tid: usize, + pub ustack_base: usize, + pub process: Weak, +} + +fn trap_cx_bottom_from_tid(tid: usize) -> usize { + TRAP_CONTEXT_BASE - tid * PAGE_SIZE +} + +fn ustack_bottom_from_tid(ustack_base: usize, tid: usize) -> usize { + ustack_base + tid * (PAGE_SIZE + USER_STACK_SIZE) +} + +impl TaskUserRes { + pub fn new( + process: Arc, + ustack_base: usize, + alloc_user_res: bool, + ) -> Self { + let tid = process.inner_exclusive_access().alloc_tid(); + let task_user_res = Self { + tid, + ustack_base, + process: Arc::downgrade(&process), + }; + if alloc_user_res { + task_user_res.alloc_user_res(); + } + task_user_res + } + + pub fn alloc_user_res(&self) { + let process = self.process.upgrade().unwrap(); + let mut process_inner = process.inner_exclusive_access(); + // alloc user stack + let ustack_bottom = ustack_bottom_from_tid(self.ustack_base, self.tid); + let ustack_top = ustack_bottom + USER_STACK_SIZE; + process_inner.memory_set.insert_framed_area( + ustack_bottom.into(), + ustack_top.into(), + MapPermission::R | MapPermission::W | MapPermission::U, + ); + // alloc trap_cx + let trap_cx_bottom = trap_cx_bottom_from_tid(self.tid); + let trap_cx_top = trap_cx_bottom + PAGE_SIZE; + process_inner.memory_set.insert_framed_area( + trap_cx_bottom.into(), + trap_cx_top.into(), + MapPermission::R | MapPermission::W, + ); + } + + fn dealloc_user_res(&self) { + // dealloc tid + let process = self.process.upgrade().unwrap(); + let mut process_inner = process.inner_exclusive_access(); + // dealloc ustack manually + let ustack_bottom_va: VirtAddr = ustack_bottom_from_tid(self.ustack_base, self.tid).into(); + process_inner + .memory_set + .remove_area_with_start_vpn(ustack_bottom_va.into()); + // dealloc trap_cx manually + let trap_cx_bottom_va: VirtAddr = trap_cx_bottom_from_tid(self.tid).into(); + process_inner + .memory_set + .remove_area_with_start_vpn(trap_cx_bottom_va.into()); + } + + #[allow(unused)] + pub fn alloc_tid(&mut self) { + self.tid = self + .process + .upgrade() + .unwrap() + .inner_exclusive_access() + .alloc_tid(); + } + + pub fn dealloc_tid(&self) { + let process = self.process.upgrade().unwrap(); + let mut process_inner = process.inner_exclusive_access(); + process_inner.dealloc_tid(self.tid); + } + + pub fn trap_cx_user_va(&self) -> usize { + trap_cx_bottom_from_tid(self.tid) + } + + pub fn trap_cx_ppn(&self) -> PhysPageNum { + let process = self.process.upgrade().unwrap(); + let process_inner = process.inner_exclusive_access(); + let trap_cx_bottom_va: VirtAddr = trap_cx_bottom_from_tid(self.tid).into(); + process_inner + .memory_set + .translate(trap_cx_bottom_va.into()) + .unwrap() + .ppn() + } + + pub fn ustack_base(&self) -> usize { + self.ustack_base + } + pub fn ustack_top(&self) -> usize { + ustack_bottom_from_tid(self.ustack_base, self.tid) + USER_STACK_SIZE + } +} + +impl Drop for TaskUserRes { + fn drop(&mut self) { + self.dealloc_tid(); + self.dealloc_user_res(); + } +} diff --git a/os/src/task/manager.rs b/os/src/task/manager.rs index 315cd22..7672cf5 100644 --- a/os/src/task/manager.rs +++ b/os/src/task/manager.rs @@ -1,41 +1,62 @@ -//!Implementation of [`TaskManager`] -use super::TaskControlBlock; -use crate::sync::UPSafeCell; -use alloc::collections::VecDeque; +use super::{ProcessControlBlock, TaskControlBlock, TaskStatus}; +use crate::sync::UPIntrFreeCell; +use alloc::collections::{BTreeMap, VecDeque}; use alloc::sync::Arc; use lazy_static::*; -///A array of `TaskControlBlock` that is thread-safe + pub struct TaskManager { ready_queue: VecDeque>, } /// A simple FIFO scheduler. impl TaskManager { - ///Creat an empty TaskManager pub fn new() -> Self { Self { ready_queue: VecDeque::new(), } } - ///Add a task to `TaskManager` pub fn add(&mut self, task: Arc) { self.ready_queue.push_back(task); } - ///Remove the first task and return it,or `None` if `TaskManager` is empty pub fn fetch(&mut self) -> Option> { self.ready_queue.pop_front() } } lazy_static! { - pub static ref TASK_MANAGER: UPSafeCell = - unsafe { UPSafeCell::new(TaskManager::new()) }; + pub static ref TASK_MANAGER: UPIntrFreeCell = + unsafe { UPIntrFreeCell::new(TaskManager::new()) }; + pub static ref PID2PCB: UPIntrFreeCell>> = + unsafe { UPIntrFreeCell::new(BTreeMap::new()) }; } -///Interface offered to add task + pub fn add_task(task: Arc) { TASK_MANAGER.exclusive_access().add(task); } -///Interface offered to pop the first task + +pub fn wakeup_task(task: Arc) { + let mut task_inner = task.inner_exclusive_access(); + task_inner.task_status = TaskStatus::Ready; + drop(task_inner); + add_task(task); +} + pub fn fetch_task() -> Option> { TASK_MANAGER.exclusive_access().fetch() } + +pub fn pid2process(pid: usize) -> Option> { + let map = PID2PCB.exclusive_access(); + map.get(&pid).map(Arc::clone) +} + +pub fn insert_into_pid2process(pid: usize, process: Arc) { + PID2PCB.exclusive_access().insert(pid, process); +} + +pub fn remove_from_pid2process(pid: usize) { + let mut map = PID2PCB.exclusive_access(); + if map.remove(&pid).is_none() { + panic!("cannot find pid {} in pid2task!", pid); + } +} diff --git a/os/src/task/mod.rs b/os/src/task/mod.rs index 47c13cd..fe1343b 100644 --- a/os/src/task/mod.rs +++ b/os/src/task/mod.rs @@ -1,44 +1,32 @@ -//! Task management implementation -//! -//! Everything about task management, like starting and switching tasks is -//! implemented here. -//! -//! A single global instance of [`TaskManager`] called `TASK_MANAGER` controls -//! all the tasks in the whole operating system. -//! -//! A single global instance of [`Processor`] called `PROCESSOR` monitors running -//! task(s) for each core. -//! -//! A single global instance of [`PidAllocator`] called `PID_ALLOCATOR` allocates -//! pid for user apps. -//! -//! Be careful when you see `__switch` ASM function in `switch.S`. Control flow around this function -//! might not be what you expect. mod context; +mod id; mod manager; -mod pid; +mod process; mod processor; +mod signal; mod switch; #[allow(clippy::module_inception)] -#[allow(rustdoc::private_intra_doc_links)] mod task; -use crate::fs::{OpenFlags, open_file}; +use self::id::TaskUserRes; +use crate::fs::{open_file, OpenFlags}; use crate::sbi::shutdown; -use alloc::sync::Arc; -pub use context::TaskContext; +use alloc::{sync::Arc, vec::Vec}; use lazy_static::*; -pub use manager::{TaskManager, fetch_task}; +use manager::fetch_task; +use process::ProcessControlBlock; use switch::__switch; -use task::{TaskControlBlock, TaskStatus}; -pub use manager::add_task; -pub use pid::{KernelStack, PidAllocator, PidHandle, pid_alloc}; +pub use context::TaskContext; +pub use id::{kstack_alloc, pid_alloc, KernelStack, PidHandle, IDLE_PID}; +pub use manager::{add_task, pid2process, remove_from_pid2process, wakeup_task}; pub use processor::{ - Processor, current_task, current_trap_cx, current_user_token, run_tasks, schedule, - take_current_task, + current_kstack_top, current_process, current_task, current_trap_cx, current_trap_cx_user_va, + current_user_token, run_tasks, schedule, take_current_task, }; -/// Suspend the current 'Running' task and run the next task in task list. +pub use signal::SignalFlags; +pub use task::{TaskControlBlock, TaskStatus}; + pub fn suspend_current_and_run_next() { // There must be an application running. let task = take_current_task().unwrap(); @@ -49,7 +37,7 @@ pub fn suspend_current_and_run_next() { // Change status to Ready task_inner.task_status = TaskStatus::Ready; drop(task_inner); - // ---- release current PCB + // ---- release current TCB // push back to ready queue. add_task(task); @@ -57,68 +45,122 @@ pub fn suspend_current_and_run_next() { schedule(task_cx_ptr); } -/// pid of usertests app in make run TEST=1 -pub const IDLE_PID: usize = 0; +/// This function must be followed by a schedule +pub fn block_current_task() -> *mut TaskContext { + let task = take_current_task().unwrap(); + let mut task_inner = task.inner_exclusive_access(); + task_inner.task_status = TaskStatus::Blocked; + &mut task_inner.task_cx as *mut TaskContext +} + +pub fn block_current_and_run_next() { + let task_cx_ptr = block_current_task(); + schedule(task_cx_ptr); +} /// Exit the current 'Running' task and run the next task in task list. pub fn exit_current_and_run_next(exit_code: i32) { - // take from Processor let task = take_current_task().unwrap(); - - let pid = task.getpid(); - if pid == IDLE_PID { - println!( - "[kernel] Idle process exit with exit_code {} ...", - exit_code - ); - if exit_code != 0 { - //crate::sbi::shutdown(255); //255 == -1 for err hint - shutdown(true) - } else { - //crate::sbi::shutdown(0); //0 for success hint - shutdown(false) - } - } - - // **** access current TCB exclusively - let mut inner = task.inner_exclusive_access(); - // Change status to Zombie - inner.task_status = TaskStatus::Zombie; - // Record exit code - inner.exit_code = exit_code; - // do not move to its parent but under initproc - - // ++++++ access initproc TCB exclusively - { - let mut initproc_inner = INITPROC.inner_exclusive_access(); - for child in inner.children.iter() { - child.inner_exclusive_access().parent = Some(Arc::downgrade(&INITPROC)); - initproc_inner.children.push(child.clone()); - } - } - // ++++++ release parent PCB - - inner.children.clear(); - // deallocate user space - inner.memory_set.recycle_data_pages(); - drop(inner); - // **** release current PCB - // drop task manually to maintain rc correctly + let mut task_inner = task.inner_exclusive_access(); + let process = task.process.upgrade().unwrap(); + let tid = task_inner.res.as_ref().unwrap().tid; + // record exit code + task_inner.exit_code = Some(exit_code); + task_inner.res = None; + // here we do not remove the thread since we are still using the kstack + // it will be deallocated when sys_waittid is called + drop(task_inner); drop(task); + // however, if this is the main thread of current process + // the process should terminate at once + if tid == 0 { + let pid = process.getpid(); + if pid == IDLE_PID { + println!( + "[kernel] Idle process exit with exit_code {} ...", + exit_code + ); + if exit_code != 0 { + //crate::sbi::shutdown(255); //255 == -1 for err hint + shutdown(true); + } else { + //crate::sbi::shutdown(0); //0 for success hint + shutdown(false); + } + } + remove_from_pid2process(pid); + let mut process_inner = process.inner_exclusive_access(); + // mark this process as a zombie process + process_inner.is_zombie = true; + // record exit code of main process + process_inner.exit_code = exit_code; + + { + // move all child processes under init process + let mut initproc_inner = INITPROC.inner_exclusive_access(); + for child in process_inner.children.iter() { + child.inner_exclusive_access().parent = Some(Arc::downgrade(&INITPROC)); + initproc_inner.children.push(child.clone()); + } + } + + // deallocate user res (including tid/trap_cx/ustack) of all threads + // it has to be done before we dealloc the whole memory_set + // otherwise they will be deallocated twice + let mut recycle_res = Vec::::new(); + for task in process_inner.tasks.iter().filter(|t| t.is_some()) { + let task = task.as_ref().unwrap(); + let mut task_inner = task.inner_exclusive_access(); + if let Some(res) = task_inner.res.take() { + recycle_res.push(res); + } + } + // dealloc_tid and dealloc_user_res require access to PCB inner, so we + // need to collect those user res first, then release process_inner + // for now to avoid deadlock/double borrow problem. + drop(process_inner); + recycle_res.clear(); + + let mut process_inner = process.inner_exclusive_access(); + process_inner.children.clear(); + // deallocate other data in user space i.e. program code/data section + process_inner.memory_set.recycle_data_pages(); + // drop file descriptors + process_inner.fd_table.clear(); + // Remove all tasks except for the main thread itself. + // This is because we are still using the kstack under the TCB + // of the main thread. This TCB, including its kstack, will be + // deallocated when the process is reaped via waitpid. + while process_inner.tasks.len() > 1 { + process_inner.tasks.pop(); + } + } + drop(process); // we do not have to save task context let mut _unused = TaskContext::zero_init(); schedule(&mut _unused as *mut _); } lazy_static! { - ///Globle process that init user shell - pub static ref INITPROC: Arc = Arc::new({ + pub static ref INITPROC: Arc = { let inode = open_file("initproc", OpenFlags::RDONLY).unwrap(); let v = inode.read_all(); - TaskControlBlock::new(v.as_slice()) - }); + ProcessControlBlock::new(v.as_slice()) + }; } -///Add init process to the manager + pub fn add_initproc() { - add_task(INITPROC.clone()); + let _initproc = INITPROC.clone(); +} + +pub fn check_signals_of_current() -> Option<(i32, &'static str)> { + let process = current_process(); + let process_inner = process.inner_exclusive_access(); + process_inner.signals.check_error() +} + +pub fn current_add_signal(signal: SignalFlags) { + let process = current_process(); + let mut process_inner = process.inner_exclusive_access(); + process_inner.signals |= signal; } diff --git a/os/src/task/pid.rs b/os/src/task/pid.rs deleted file mode 100644 index f0a5e2a..0000000 --- a/os/src/task/pid.rs +++ /dev/null @@ -1,111 +0,0 @@ -//!Implementation of [`PidAllocator`] -use crate::config::{KERNEL_STACK_SIZE, PAGE_SIZE, TRAMPOLINE}; -use crate::mm::{KERNEL_SPACE, MapPermission, VirtAddr}; -use crate::sync::UPSafeCell; -use alloc::vec::Vec; -use lazy_static::*; -///Pid Allocator struct -pub struct PidAllocator { - current: usize, - recycled: Vec, -} - -impl PidAllocator { - ///Create an empty `PidAllocator` - pub fn new() -> Self { - PidAllocator { - current: 0, - recycled: Vec::new(), - } - } - ///Allocate a pid - pub fn alloc(&mut self) -> PidHandle { - if let Some(pid) = self.recycled.pop() { - PidHandle(pid) - } else { - self.current += 1; - PidHandle(self.current - 1) - } - } - ///Recycle a pid - pub fn dealloc(&mut self, pid: usize) { - assert!(pid < self.current); - assert!( - !self.recycled.iter().any(|ppid| *ppid == pid), - "pid {} has been deallocated!", - pid - ); - self.recycled.push(pid); - } -} - -lazy_static! { - pub static ref PID_ALLOCATOR: UPSafeCell = - unsafe { UPSafeCell::new(PidAllocator::new()) }; -} -///Bind pid lifetime to `PidHandle` -pub struct PidHandle(pub usize); - -impl Drop for PidHandle { - fn drop(&mut self) { - //println!("drop pid {}", self.0); - PID_ALLOCATOR.exclusive_access().dealloc(self.0); - } -} -///Allocate a pid from PID_ALLOCATOR -pub fn pid_alloc() -> PidHandle { - PID_ALLOCATOR.exclusive_access().alloc() -} - -/// Return (bottom, top) of a kernel stack in kernel space. -pub fn kernel_stack_position(app_id: usize) -> (usize, usize) { - let top = TRAMPOLINE - app_id * (KERNEL_STACK_SIZE + PAGE_SIZE); - let bottom = top - KERNEL_STACK_SIZE; - (bottom, top) -} -///Kernelstack for app -pub struct KernelStack { - pid: usize, -} - -impl KernelStack { - ///Create a kernelstack from pid - pub fn new(pid_handle: &PidHandle) -> Self { - let pid = pid_handle.0; - let (kernel_stack_bottom, kernel_stack_top) = kernel_stack_position(pid); - KERNEL_SPACE.exclusive_access().insert_framed_area( - kernel_stack_bottom.into(), - kernel_stack_top.into(), - MapPermission::R | MapPermission::W, - ); - KernelStack { pid: pid_handle.0 } - } - #[allow(unused)] - ///Push a value on top of kernelstack - pub fn push_on_top(&self, value: T) -> *mut T - where - T: Sized, - { - let kernel_stack_top = self.get_top(); - let ptr_mut = (kernel_stack_top - core::mem::size_of::()) as *mut T; - unsafe { - *ptr_mut = value; - } - ptr_mut - } - ///Get the value on the top of kernelstack - pub fn get_top(&self) -> usize { - let (_, kernel_stack_top) = kernel_stack_position(self.pid); - kernel_stack_top - } -} - -impl Drop for KernelStack { - fn drop(&mut self) { - let (kernel_stack_bottom, _) = kernel_stack_position(self.pid); - let kernel_stack_bottom_va: VirtAddr = kernel_stack_bottom.into(); - KERNEL_SPACE - .exclusive_access() - .remove_area_with_start_vpn(kernel_stack_bottom_va.into()); - } -} diff --git a/os/src/task/process.rs b/os/src/task/process.rs new file mode 100644 index 0000000..b3976b7 --- /dev/null +++ b/os/src/task/process.rs @@ -0,0 +1,258 @@ +use super::id::RecycleAllocator; +use super::manager::insert_into_pid2process; +use super::TaskControlBlock; +use super::{add_task, SignalFlags}; +use super::{pid_alloc, PidHandle}; +use crate::fs::{File, Stdin, Stdout}; +use crate::mm::{translated_refmut, MemorySet, KERNEL_SPACE}; +use crate::sync::{Condvar, Mutex, Semaphore, UPIntrFreeCell, UPIntrRefMut}; +use crate::trap::{trap_handler, TrapContext}; +use alloc::string::String; +use alloc::sync::{Arc, Weak}; +use alloc::vec; +use alloc::vec::Vec; + +pub struct ProcessControlBlock { + // immutable + pub pid: PidHandle, + // mutable + inner: UPIntrFreeCell, +} + +pub struct ProcessControlBlockInner { + pub is_zombie: bool, + pub memory_set: MemorySet, + pub parent: Option>, + pub children: Vec>, + pub exit_code: i32, + pub fd_table: Vec>>, + pub signals: SignalFlags, + pub tasks: Vec>>, + pub task_res_allocator: RecycleAllocator, + pub mutex_list: Vec>>, + pub semaphore_list: Vec>>, + pub condvar_list: Vec>>, +} + +impl ProcessControlBlockInner { + #[allow(unused)] + pub fn get_user_token(&self) -> usize { + self.memory_set.token() + } + + pub fn alloc_fd(&mut self) -> usize { + if let Some(fd) = (0..self.fd_table.len()).find(|fd| self.fd_table[*fd].is_none()) { + fd + } else { + self.fd_table.push(None); + self.fd_table.len() - 1 + } + } + + pub fn alloc_tid(&mut self) -> usize { + self.task_res_allocator.alloc() + } + + pub fn dealloc_tid(&mut self, tid: usize) { + self.task_res_allocator.dealloc(tid) + } + + pub fn thread_count(&self) -> usize { + self.tasks.len() + } + + pub fn get_task(&self, tid: usize) -> Arc { + self.tasks[tid].as_ref().unwrap().clone() + } +} + +impl ProcessControlBlock { + pub fn inner_exclusive_access(&self) -> UPIntrRefMut<'_, ProcessControlBlockInner> { + self.inner.exclusive_access() + } + + pub fn new(elf_data: &[u8]) -> Arc { + // memory_set with elf program headers/trampoline/trap context/user stack + let (memory_set, ustack_base, entry_point) = MemorySet::from_elf(elf_data); + // allocate a pid + let pid_handle = pid_alloc(); + let process = Arc::new(Self { + pid: pid_handle, + inner: unsafe { + UPIntrFreeCell::new(ProcessControlBlockInner { + is_zombie: false, + memory_set, + parent: None, + children: Vec::new(), + exit_code: 0, + fd_table: vec![ + // 0 -> stdin + Some(Arc::new(Stdin)), + // 1 -> stdout + Some(Arc::new(Stdout)), + // 2 -> stderr + Some(Arc::new(Stdout)), + ], + signals: SignalFlags::empty(), + tasks: Vec::new(), + task_res_allocator: RecycleAllocator::new(), + mutex_list: Vec::new(), + semaphore_list: Vec::new(), + condvar_list: Vec::new(), + }) + }, + }); + // create a main thread, we should allocate ustack and trap_cx here + let task = Arc::new(TaskControlBlock::new( + Arc::clone(&process), + ustack_base, + true, + )); + // prepare trap_cx of main thread + let task_inner = task.inner_exclusive_access(); + let trap_cx = task_inner.get_trap_cx(); + let ustack_top = task_inner.res.as_ref().unwrap().ustack_top(); + let kstack_top = task.kstack.get_top(); + drop(task_inner); + *trap_cx = TrapContext::app_init_context( + entry_point, + ustack_top, + KERNEL_SPACE.exclusive_access().token(), + kstack_top, + trap_handler as usize, + ); + // add main thread to the process + let mut process_inner = process.inner_exclusive_access(); + process_inner.tasks.push(Some(Arc::clone(&task))); + drop(process_inner); + insert_into_pid2process(process.getpid(), Arc::clone(&process)); + // add main thread to scheduler + add_task(task); + process + } + + /// Only support processes with a single thread. + pub fn exec(self: &Arc, elf_data: &[u8], args: Vec) { + assert_eq!(self.inner_exclusive_access().thread_count(), 1); + // memory_set with elf program headers/trampoline/trap context/user stack + let (memory_set, ustack_base, entry_point) = MemorySet::from_elf(elf_data); + let new_token = memory_set.token(); + // substitute memory_set + self.inner_exclusive_access().memory_set = memory_set; + // then we alloc user resource for main thread again + // since memory_set has been changed + let task = self.inner_exclusive_access().get_task(0); + let mut task_inner = task.inner_exclusive_access(); + task_inner.res.as_mut().unwrap().ustack_base = ustack_base; + task_inner.res.as_mut().unwrap().alloc_user_res(); + task_inner.trap_cx_ppn = task_inner.res.as_mut().unwrap().trap_cx_ppn(); + // push arguments on user stack + let mut user_sp = task_inner.res.as_mut().unwrap().ustack_top(); + user_sp -= (args.len() + 1) * core::mem::size_of::(); + let argv_base = user_sp; + let mut argv: Vec<_> = (0..=args.len()) + .map(|arg| { + translated_refmut( + new_token, + (argv_base + arg * core::mem::size_of::()) as *mut usize, + ) + }) + .collect(); + *argv[args.len()] = 0; + for i in 0..args.len() { + user_sp -= args[i].len() + 1; + *argv[i] = user_sp; + let mut p = user_sp; + for c in args[i].as_bytes() { + *translated_refmut(new_token, p as *mut u8) = *c; + p += 1; + } + *translated_refmut(new_token, p as *mut u8) = 0; + } + // make the user_sp aligned to 8B for k210 platform + user_sp -= user_sp % core::mem::size_of::(); + // initialize trap_cx + let mut trap_cx = TrapContext::app_init_context( + entry_point, + user_sp, + KERNEL_SPACE.exclusive_access().token(), + task.kstack.get_top(), + trap_handler as usize, + ); + trap_cx.x[10] = args.len(); + trap_cx.x[11] = argv_base; + *task_inner.get_trap_cx() = trap_cx; + } + + /// Only support processes with a single thread. + pub fn fork(self: &Arc) -> Arc { + let mut parent = self.inner_exclusive_access(); + assert_eq!(parent.thread_count(), 1); + // clone parent's memory_set completely including trampoline/ustacks/trap_cxs + let memory_set = MemorySet::from_existed_user(&parent.memory_set); + // alloc a pid + let pid = pid_alloc(); + // copy fd table + let mut new_fd_table: Vec>> = Vec::new(); + for fd in parent.fd_table.iter() { + if let Some(file) = fd { + new_fd_table.push(Some(file.clone())); + } else { + new_fd_table.push(None); + } + } + // create child process pcb + let child = Arc::new(Self { + pid, + inner: unsafe { + UPIntrFreeCell::new(ProcessControlBlockInner { + is_zombie: false, + memory_set, + parent: Some(Arc::downgrade(self)), + children: Vec::new(), + exit_code: 0, + fd_table: new_fd_table, + signals: SignalFlags::empty(), + tasks: Vec::new(), + task_res_allocator: RecycleAllocator::new(), + mutex_list: Vec::new(), + semaphore_list: Vec::new(), + condvar_list: Vec::new(), + }) + }, + }); + // add child + parent.children.push(Arc::clone(&child)); + // create main thread of child process + let task = Arc::new(TaskControlBlock::new( + Arc::clone(&child), + parent + .get_task(0) + .inner_exclusive_access() + .res + .as_ref() + .unwrap() + .ustack_base(), + // here we do not allocate trap_cx or ustack again + // but mention that we allocate a new kstack here + false, + )); + // attach task to child process + let mut child_inner = child.inner_exclusive_access(); + child_inner.tasks.push(Some(Arc::clone(&task))); + drop(child_inner); + // modify kstack_top in trap_cx of this thread + let task_inner = task.inner_exclusive_access(); + let trap_cx = task_inner.get_trap_cx(); + trap_cx.kernel_sp = task.kstack.get_top(); + drop(task_inner); + insert_into_pid2process(child.getpid(), Arc::clone(&child)); + // add this thread to scheduler + add_task(task); + child + } + + pub fn getpid(&self) -> usize { + self.pid.0 + } +} diff --git a/os/src/task/processor.rs b/os/src/task/processor.rs index 7c05a21..96361d7 100644 --- a/os/src/task/processor.rs +++ b/os/src/task/processor.rs @@ -1,92 +1,110 @@ -//!Implementation of [`Processor`] and Intersection of control flow use super::__switch; -use super::{TaskContext, TaskControlBlock}; -use super::{TaskStatus, fetch_task}; -use crate::sync::UPSafeCell; +use super::{fetch_task, TaskStatus}; +use super::{ProcessControlBlock, TaskContext, TaskControlBlock}; +use crate::sync::UPIntrFreeCell; use crate::trap::TrapContext; use alloc::sync::Arc; +use core::arch::asm; use lazy_static::*; -///Processor management structure + pub struct Processor { - ///The task currently executing on the current processor current: Option>, - ///The basic control flow of each core, helping to select and switch process idle_task_cx: TaskContext, } impl Processor { - ///Create an empty Processor pub fn new() -> Self { Self { current: None, idle_task_cx: TaskContext::zero_init(), } } - ///Get mutable reference to `idle_task_cx` fn get_idle_task_cx_ptr(&mut self) -> *mut TaskContext { &mut self.idle_task_cx as *mut _ } - ///Get current task in moving semanteme pub fn take_current(&mut self) -> Option> { self.current.take() } - ///Get current task in cloning semanteme pub fn current(&self) -> Option> { self.current.as_ref().map(Arc::clone) } } lazy_static! { - pub static ref PROCESSOR: UPSafeCell = unsafe { UPSafeCell::new(Processor::new()) }; + pub static ref PROCESSOR: UPIntrFreeCell = + unsafe { UPIntrFreeCell::new(Processor::new()) }; } -///The main part of process execution and scheduling -///Loop `fetch_task` to get the process that needs to run, and switch the process through `__switch` + pub fn run_tasks() { loop { let mut processor = PROCESSOR.exclusive_access(); if let Some(task) = fetch_task() { let idle_task_cx_ptr = processor.get_idle_task_cx_ptr(); // access coming task TCB exclusively - let mut task_inner = task.inner_exclusive_access(); - let next_task_cx_ptr = &task_inner.task_cx as *const TaskContext; - task_inner.task_status = TaskStatus::Running; - drop(task_inner); - // release coming task TCB manually + let next_task_cx_ptr = task.inner.exclusive_session(|task_inner| { + task_inner.task_status = TaskStatus::Running; + &task_inner.task_cx as *const TaskContext + }); processor.current = Some(task); // release processor manually drop(processor); unsafe { __switch(idle_task_cx_ptr, next_task_cx_ptr); } + } else { + println!("no tasks available in run_tasks"); } } } -///Take the current task,leaving a None in its place + pub fn take_current_task() -> Option> { PROCESSOR.exclusive_access().take_current() } -///Get running task + pub fn current_task() -> Option> { PROCESSOR.exclusive_access().current() } -///Get token of the address space of current task + +pub fn current_process() -> Arc { + current_task().unwrap().process.upgrade().unwrap() +} + pub fn current_user_token() -> usize { let task = current_task().unwrap(); - let token = task.inner_exclusive_access().get_user_token(); - token + task.get_user_token() } -///Get the mutable reference to trap context of current task + pub fn current_trap_cx() -> &'static mut TrapContext { current_task() .unwrap() .inner_exclusive_access() .get_trap_cx() } -///Return to idle control flow for new scheduling + +pub fn current_trap_cx_user_va() -> usize { + current_task() + .unwrap() + .inner_exclusive_access() + .res + .as_ref() + .unwrap() + .trap_cx_user_va() +} + +pub fn current_kstack_top() -> usize { + if let Some(task) = current_task() { + task.kstack.get_top() + } else { + let mut boot_stack_top; + unsafe { asm!("la {},boot_stack_top",out(reg) boot_stack_top) }; + boot_stack_top + } + // current_task().unwrap().kstack.get_top() +} + pub fn schedule(switched_task_cx_ptr: *mut TaskContext) { - let mut processor = PROCESSOR.exclusive_access(); - let idle_task_cx_ptr = processor.get_idle_task_cx_ptr(); - drop(processor); + let idle_task_cx_ptr = + PROCESSOR.exclusive_session(|processor| processor.get_idle_task_cx_ptr()); unsafe { __switch(switched_task_cx_ptr, idle_task_cx_ptr); } diff --git a/os/src/task/signal.rs b/os/src/task/signal.rs new file mode 100644 index 0000000..46f1ad9 --- /dev/null +++ b/os/src/task/signal.rs @@ -0,0 +1,29 @@ +use bitflags::*; + +bitflags! { + pub struct SignalFlags: u32 { + const SIGINT = 1 << 2; + const SIGILL = 1 << 4; + const SIGABRT = 1 << 6; + const SIGFPE = 1 << 8; + const SIGSEGV = 1 << 11; + } +} + +impl SignalFlags { + pub fn check_error(&self) -> Option<(i32, &'static str)> { + if self.contains(Self::SIGINT) { + Some((-2, "Killed, SIGINT=2")) + } else if self.contains(Self::SIGILL) { + Some((-4, "Illegal Instruction, SIGILL=4")) + } else if self.contains(Self::SIGABRT) { + Some((-6, "Aborted, SIGABRT=6")) + } else if self.contains(Self::SIGFPE) { + Some((-8, "Erroneous Arithmetic Operation, SIGFPE=8")) + } else if self.contains(Self::SIGSEGV) { + Some((-11, "Segmentation Fault, SIGSEGV=11")) + } else { + None + } + } +} diff --git a/os/src/task/switch.rs b/os/src/task/switch.rs index 8f69fdf..59f8b1a 100644 --- a/os/src/task/switch.rs +++ b/os/src/task/switch.rs @@ -1,14 +1,8 @@ -//!Wrap `switch.S` as a function use super::TaskContext; use core::arch::global_asm; global_asm!(include_str!("switch.S")); -unsafe extern "C" { - /// Switch to the context of `next_task_cx_ptr`, saving the current context - /// in `current_task_cx_ptr`. - pub unsafe fn __switch( - current_task_cx_ptr: *mut TaskContext, - next_task_cx_ptr: *const TaskContext, - ); +extern "C" { + pub fn __switch(current_task_cx_ptr: *mut TaskContext, next_task_cx_ptr: *const TaskContext); } diff --git a/os/src/task/task.rs b/os/src/task/task.rs index 87d287a..ffc5c0e 100644 --- a/os/src/task/task.rs +++ b/os/src/task/task.rs @@ -1,191 +1,74 @@ -//!Implementation of [`TaskControlBlock`] -use super::TaskContext; -use super::{KernelStack, PidHandle, pid_alloc}; -use crate::config::TRAP_CONTEXT; -use crate::fs::{File, Stdin, Stdout}; -use crate::mm::{KERNEL_SPACE, MemorySet, PhysPageNum, VirtAddr}; -use crate::sync::UPSafeCell; -use crate::trap::{TrapContext, trap_handler}; +use super::id::TaskUserRes; +use super::{kstack_alloc, KernelStack, ProcessControlBlock, TaskContext}; +use crate::trap::TrapContext; +use crate::{ + mm::PhysPageNum, + sync::{UPIntrFreeCell, UPIntrRefMut}, +}; use alloc::sync::{Arc, Weak}; -use alloc::vec; -use alloc::vec::Vec; -use core::cell::RefMut; pub struct TaskControlBlock { // immutable - pub pid: PidHandle, - pub kernel_stack: KernelStack, + pub process: Weak, + pub kstack: KernelStack, // mutable - inner: UPSafeCell, + pub inner: UPIntrFreeCell, +} + +impl TaskControlBlock { + pub fn inner_exclusive_access(&self) -> UPIntrRefMut<'_, TaskControlBlockInner> { + self.inner.exclusive_access() + } + + pub fn get_user_token(&self) -> usize { + let process = self.process.upgrade().unwrap(); + let inner = process.inner_exclusive_access(); + inner.memory_set.token() + } } pub struct TaskControlBlockInner { + pub res: Option, pub trap_cx_ppn: PhysPageNum, - #[allow(unused)] - pub base_size: usize, pub task_cx: TaskContext, pub task_status: TaskStatus, - pub memory_set: MemorySet, - pub parent: Option>, - pub children: Vec>, - pub exit_code: i32, - pub fd_table: Vec>>, - - // New: User - pub user: String, + pub exit_code: Option, } impl TaskControlBlockInner { pub fn get_trap_cx(&self) -> &'static mut TrapContext { self.trap_cx_ppn.get_mut() } - pub fn get_user_token(&self) -> usize { - self.memory_set.token() - } + + #[allow(unused)] fn get_status(&self) -> TaskStatus { self.task_status } - pub fn is_zombie(&self) -> bool { - self.get_status() == TaskStatus::Zombie - } - pub fn alloc_fd(&mut self) -> usize { - if let Some(fd) = (0..self.fd_table.len()).find(|fd| self.fd_table[*fd].is_none()) { - fd - } else { - self.fd_table.push(None); - self.fd_table.len() - 1 - } - } } impl TaskControlBlock { - pub fn inner_exclusive_access(&self) -> RefMut<'_, TaskControlBlockInner> { - self.inner.exclusive_access() - } - pub fn new(elf_data: &[u8]) -> Self { - // memory_set with elf program headers/trampoline/trap context/user stack - let (memory_set, user_sp, entry_point) = MemorySet::from_elf(elf_data); - let trap_cx_ppn = memory_set - .translate(VirtAddr::from(TRAP_CONTEXT).into()) - .unwrap() - .ppn(); - // alloc a pid and a kernel stack in kernel space - let pid_handle = pid_alloc(); - let kernel_stack = KernelStack::new(&pid_handle); - let kernel_stack_top = kernel_stack.get_top(); - let task_control_block = Self { - pid: pid_handle, - kernel_stack, + pub fn new( + process: Arc, + ustack_base: usize, + alloc_user_res: bool, + ) -> Self { + let res = TaskUserRes::new(Arc::clone(&process), ustack_base, alloc_user_res); + let trap_cx_ppn = res.trap_cx_ppn(); + let kstack = kstack_alloc(); + let kstack_top = kstack.get_top(); + Self { + process: Arc::downgrade(&process), + kstack, inner: unsafe { - UPSafeCell::new(TaskControlBlockInner { + UPIntrFreeCell::new(TaskControlBlockInner { + res: Some(res), trap_cx_ppn, - base_size: user_sp, - task_cx: TaskContext::goto_trap_return(kernel_stack_top), + task_cx: TaskContext::goto_trap_return(kstack_top), task_status: TaskStatus::Ready, - memory_set, - parent: None, - children: Vec::new(), - exit_code: 0, - fd_table: vec![ - // 0 -> stdin - Some(Arc::new(Stdin)), - // 1 -> stdout - Some(Arc::new(Stdout)), - // 2 -> stderr - Some(Arc::new(Stdout)), - ], + exit_code: None, }) }, - }; - // prepare TrapContext in user space - let trap_cx = task_control_block.inner_exclusive_access().get_trap_cx(); - *trap_cx = TrapContext::app_init_context( - entry_point, - user_sp, - KERNEL_SPACE.exclusive_access().token(), - kernel_stack_top, - trap_handler as usize, - ); - task_control_block - } - pub fn exec(&self, elf_data: &[u8]) { - // memory_set with elf program headers/trampoline/trap context/user stack - let (memory_set, user_sp, entry_point) = MemorySet::from_elf(elf_data); - let trap_cx_ppn = memory_set - .translate(VirtAddr::from(TRAP_CONTEXT).into()) - .unwrap() - .ppn(); - - // **** access current TCB exclusively - let mut inner = self.inner_exclusive_access(); - // substitute memory_set - inner.memory_set = memory_set; - // update trap_cx ppn - inner.trap_cx_ppn = trap_cx_ppn; - // initialize trap_cx - let trap_cx = TrapContext::app_init_context( - entry_point, - user_sp, - KERNEL_SPACE.exclusive_access().token(), - self.kernel_stack.get_top(), - trap_handler as usize, - ); - *inner.get_trap_cx() = trap_cx; - // **** release current PCB - } - pub fn fork(self: &Arc) -> Arc { - // ---- hold parent PCB lock - let mut parent_inner = self.inner_exclusive_access(); - // copy user space(include trap context) - let memory_set = MemorySet::from_existed_user(&parent_inner.memory_set); - let trap_cx_ppn = memory_set - .translate(VirtAddr::from(TRAP_CONTEXT).into()) - .unwrap() - .ppn(); - // alloc a pid and a kernel stack in kernel space - let pid_handle = pid_alloc(); - let kernel_stack = KernelStack::new(&pid_handle); - let kernel_stack_top = kernel_stack.get_top(); - // copy fd table - let mut new_fd_table: Vec>> = Vec::new(); - for fd in parent_inner.fd_table.iter() { - if let Some(file) = fd { - new_fd_table.push(Some(file.clone())); - } else { - new_fd_table.push(None); - } } - let task_control_block = Arc::new(TaskControlBlock { - pid: pid_handle, - kernel_stack, - inner: unsafe { - UPSafeCell::new(TaskControlBlockInner { - trap_cx_ppn, - base_size: parent_inner.base_size, - task_cx: TaskContext::goto_trap_return(kernel_stack_top), - task_status: TaskStatus::Ready, - memory_set, - parent: Some(Arc::downgrade(self)), - children: Vec::new(), - exit_code: 0, - fd_table: new_fd_table, - user: username.to_string(), // Init User name - }) - }, - }); - // add child - parent_inner.children.push(task_control_block.clone()); - // modify kernel_sp in trap_cx - // **** access child PCB exclusively - let trap_cx = task_control_block.inner_exclusive_access().get_trap_cx(); - trap_cx.kernel_sp = kernel_stack_top; - // return - task_control_block - // **** release child PCB - // ---- release parent PCB - } - pub fn getpid(&self) -> usize { - self.pid.0 } } @@ -193,5 +76,5 @@ impl TaskControlBlock { pub enum TaskStatus { Ready, Running, - Zombie, + Blocked, } diff --git a/os/src/timer.rs b/os/src/timer.rs index 50ce53a..83c969d 100644 --- a/os/src/timer.rs +++ b/os/src/timer.rs @@ -1,20 +1,74 @@ -//! RISC-V timer-related functionality +use core::cmp::Ordering; use crate::config::CLOCK_FREQ; use crate::sbi::set_timer; +use crate::sync::UPIntrFreeCell; +use crate::task::{wakeup_task, TaskControlBlock}; +use alloc::collections::BinaryHeap; +use alloc::sync::Arc; +use lazy_static::*; use riscv::register::time; const TICKS_PER_SEC: usize = 100; const MSEC_PER_SEC: usize = 1000; -///get current time + pub fn get_time() -> usize { time::read() } -/// get current time in microseconds + pub fn get_time_ms() -> usize { time::read() / (CLOCK_FREQ / MSEC_PER_SEC) } -/// set the next timer interrupt + pub fn set_next_trigger() { set_timer(get_time() + CLOCK_FREQ / TICKS_PER_SEC); } + +pub struct TimerCondVar { + pub expire_ms: usize, + pub task: Arc, +} + +impl PartialEq for TimerCondVar { + fn eq(&self, other: &Self) -> bool { + self.expire_ms == other.expire_ms + } +} +impl Eq for TimerCondVar {} +impl PartialOrd for TimerCondVar { + fn partial_cmp(&self, other: &Self) -> Option { + let a = -(self.expire_ms as isize); + let b = -(other.expire_ms as isize); + Some(a.cmp(&b)) + } +} + +impl Ord for TimerCondVar { + fn cmp(&self, other: &Self) -> Ordering { + self.partial_cmp(other).unwrap() + } +} + +lazy_static! { + static ref TIMERS: UPIntrFreeCell> = + unsafe { UPIntrFreeCell::new(BinaryHeap::::new()) }; +} + +pub fn add_timer(expire_ms: usize, task: Arc) { + let mut timers = TIMERS.exclusive_access(); + timers.push(TimerCondVar { expire_ms, task }); +} + +pub fn check_timer() { + let current_ms = get_time_ms(); + TIMERS.exclusive_session(|timers| { + while let Some(timer) = timers.peek() { + if timer.expire_ms <= current_ms { + wakeup_task(Arc::clone(&timer.task)); + timers.pop(); + } else { + break; + } + } + }); +} diff --git a/os/src/trap/context.rs b/os/src/trap/context.rs index 5f968e5..011b7fb 100644 --- a/os/src/trap/context.rs +++ b/os/src/trap/context.rs @@ -1,30 +1,20 @@ -//! Implementation of [`TrapContext`] -use riscv::register::sstatus::{self, SPP, Sstatus}; +use riscv::register::sstatus::{self, Sstatus, SPP}; #[repr(C)] #[derive(Debug)] -///trap context structure containing sstatus, sepc and registers pub struct TrapContext { - /// general regs[0..31] pub x: [usize; 32], - /// CSR sstatus pub sstatus: Sstatus, - /// CSR sepc pub sepc: usize, - /// Addr of Page Table pub kernel_satp: usize, - /// kernel stack pub kernel_sp: usize, - /// Addr of trap_handler function pub trap_handler: usize, } impl TrapContext { - ///set stack pointer to x_2 reg (sp) pub fn set_sp(&mut self, sp: usize) { self.x[2] = sp; } - ///init app context pub fn app_init_context( entry: usize, sp: usize, diff --git a/os/src/trap/mod.rs b/os/src/trap/mod.rs index 26e7895..3287011 100644 --- a/os/src/trap/mod.rs +++ b/os/src/trap/mod.rs @@ -1,40 +1,34 @@ -//! Trap handling functionality -//! -//! For rCore, we have a single trap entry point, namely `__alltraps`. At -//! initialization in [`init()`], we set the `stvec` CSR to point to it. -//! -//! All traps go through `__alltraps`, which is defined in `trap.S`. The -//! assembly language code does just enough work restore the kernel space -//! context, ensuring that Rust code safely runs, and transfers control to -//! [`trap_handler()`]. -//! -//! It then calls different functionality based on what exactly the exception -//! was. For example, timer interrupts trigger task preemption, and syscalls go -//! to [`syscall()`]. mod context; -use crate::config::{TRAMPOLINE, TRAP_CONTEXT}; +use crate::config::TRAMPOLINE; use crate::syscall::syscall; use crate::task::{ - current_trap_cx, current_user_token, exit_current_and_run_next, suspend_current_and_run_next, + check_signals_of_current, current_add_signal, current_trap_cx, current_trap_cx_user_va, + current_user_token, exit_current_and_run_next, suspend_current_and_run_next, SignalFlags, }; -use crate::timer::set_next_trigger; +use crate::timer::{check_timer, set_next_trigger}; use core::arch::{asm, global_asm}; use riscv::register::{ mtvec::TrapMode, scause::{self, Exception, Interrupt, Trap}, - sie, stval, stvec, + sie, sip, sscratch, sstatus, stval, stvec, }; global_asm!(include_str!("trap.S")); -/// initialize CSR `stvec` as the entry of `__alltraps` + pub fn init() { set_kernel_trap_entry(); } fn set_kernel_trap_entry() { + extern "C" { + fn __alltraps(); + fn __alltraps_k(); + } + let __alltraps_k_va = __alltraps_k as usize - __alltraps as usize + TRAMPOLINE; unsafe { - stvec::write(trap_from_kernel as usize, TrapMode::Direct); + stvec::write(__alltraps_k_va, TrapMode::Direct); + sscratch::write(trap_from_kernel as usize); } } @@ -43,24 +37,39 @@ fn set_user_trap_entry() { stvec::write(TRAMPOLINE as usize, TrapMode::Direct); } } -/// enable timer interrupt in sie CSR + pub fn enable_timer_interrupt() { unsafe { sie::set_stimer(); } } -#[unsafe(no_mangle)] -/// handle an interrupt, exception, or system call from user space +fn enable_supervisor_interrupt() { + unsafe { + sstatus::set_sie(); + } +} + +fn disable_supervisor_interrupt() { + unsafe { + sstatus::clear_sie(); + } +} + +#[no_mangle] pub fn trap_handler() -> ! { set_kernel_trap_entry(); let scause = scause::read(); let stval = stval::read(); + // println!("into {:?}", scause.cause()); match scause.cause() { Trap::Exception(Exception::UserEnvCall) => { // jump to next instruction anyway let mut cx = current_trap_cx(); cx.sepc += 4; + + enable_supervisor_interrupt(); + // get system call return value let result = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]); // cx is changed during sys_exec, so we have to call it again @@ -73,24 +82,27 @@ pub fn trap_handler() -> ! { | Trap::Exception(Exception::InstructionPageFault) | Trap::Exception(Exception::LoadFault) | Trap::Exception(Exception::LoadPageFault) => { + /* println!( "[kernel] {:?} in application, bad addr = {:#x}, bad instruction = {:#x}, kernel killed it.", scause.cause(), stval, current_trap_cx().sepc, ); - // page fault exit code - exit_current_and_run_next(-2); + */ + current_add_signal(SignalFlags::SIGSEGV); } Trap::Exception(Exception::IllegalInstruction) => { - println!("[kernel] IllegalInstruction in application, kernel killed it."); - // illegal instruction exit code - exit_current_and_run_next(-3); + current_add_signal(SignalFlags::SIGILL); } Trap::Interrupt(Interrupt::SupervisorTimer) => { set_next_trigger(); + check_timer(); suspend_current_and_run_next(); } + Trap::Interrupt(Interrupt::SupervisorExternal) => { + crate::board::irq_handler(); + } _ => { panic!( "Unsupported trap {:?}, stval = {:#x}!", @@ -99,42 +111,59 @@ pub fn trap_handler() -> ! { ); } } - //println!("before trap_return"); + // check signals + if let Some((errno, msg)) = check_signals_of_current() { + println!("[kernel] {}", msg); + exit_current_and_run_next(errno); + } trap_return(); } -#[unsafe(no_mangle)] -/// set the new addr of __restore asm function in TRAMPOLINE page, -/// set the reg a0 = trap_cx_ptr, reg a1 = phy addr of usr page table, -/// finally, jump to new addr of __restore asm function +#[no_mangle] pub fn trap_return() -> ! { + disable_supervisor_interrupt(); set_user_trap_entry(); - let trap_cx_ptr = TRAP_CONTEXT; + let trap_cx_user_va = current_trap_cx_user_va(); let user_satp = current_user_token(); - unsafe extern "C" { - unsafe fn __alltraps(); - unsafe fn __restore(); + extern "C" { + fn __alltraps(); + fn __restore(); } let restore_va = __restore as usize - __alltraps as usize + TRAMPOLINE; + //println!("before return"); unsafe { asm!( "fence.i", "jr {restore_va}", restore_va = in(reg) restore_va, - in("a0") trap_cx_ptr, + in("a0") trap_cx_user_va, in("a1") user_satp, options(noreturn) ); } } -#[unsafe(no_mangle)] -/// Unimplement: traps/interrupts/exceptions from kernel mode -/// Todo: Chapter 9: I/O device -pub fn trap_from_kernel() -> ! { - use riscv::register::sepc; - println!("stval = {:#x}, sepc = {:#x}", stval::read(), sepc::read()); - panic!("a trap {:?} from kernel!", scause::read().cause()); +#[no_mangle] +pub fn trap_from_kernel(_trap_cx: &TrapContext) { + let scause = scause::read(); + let stval = stval::read(); + match scause.cause() { + Trap::Interrupt(Interrupt::SupervisorExternal) => { + crate::board::irq_handler(); + } + Trap::Interrupt(Interrupt::SupervisorTimer) => { + set_next_trigger(); + check_timer(); + // do not schedule now + } + _ => { + panic!( + "Unsupported trap from kernel: {:?}, stval = {:#x}!", + scause.cause(), + stval + ); + } + } } pub use context::TrapContext; diff --git a/os/src/trap/trap.S b/os/src/trap/trap.S index c0e2d15..407307c 100644 --- a/os/src/trap/trap.S +++ b/os/src/trap/trap.S @@ -8,6 +8,8 @@ .section .text.trampoline .globl __alltraps .globl __restore + .globl __alltraps_k + .globl __restore_k .align 2 __alltraps: csrrw sp, sscratch, sp @@ -67,3 +69,36 @@ __restore: # back to user stack ld sp, 2*8(sp) sret + + .align 2 +__alltraps_k: + addi sp, sp, -34*8 + sd x1, 1*8(sp) + sd x3, 3*8(sp) + .set n, 5 + .rept 27 + SAVE_GP %n + .set n, n+1 + .endr + csrr t0, sstatus + csrr t1, sepc + sd t0, 32*8(sp) + sd t1, 33*8(sp) + mv a0, sp + csrr t2, sscratch + jalr t2 + +__restore_k: + ld t0, 32*8(sp) + ld t1, 33*8(sp) + csrw sstatus, t0 + csrw sepc, t1 + ld x1, 1*8(sp) + ld x3, 3*8(sp) + .set n, 5 + .rept 27 + LOAD_GP %n + .set n, n+1 + .endr + addi sp, sp, 34*8 + sret diff --git a/ping.py b/ping.py new file mode 100644 index 0000000..c68fc71 --- /dev/null +++ b/ping.py @@ -0,0 +1,18 @@ +import socket +import sys +import time + +sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +addr = ('localhost', 26099) +sock.bind(addr) + + +print("pinging...", file=sys.stderr) +while True: + buf, raddr = sock.recvfrom(4096) + print("receive: " + buf.decode("utf-8")) + buf = "this is a ping to port 6200!".encode('utf-8') + sock.sendto(buf, ("127.0.0.1", 6200)) + buf = "this is a ping to reply!".encode('utf-8') + sock.sendto(buf, raddr) + time.sleep(1) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index e6f10a1..c65d258 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] profile = "minimal" # use the nightly version of the last stable toolchain, see -channel = "nightly-2025-02-18" +channel = "nightly-2024-05-01" components = ["rust-src", "llvm-tools", "rustfmt", "clippy"] targets = ["riscv64gc-unknown-none-elf"] diff --git a/setenv.sh b/setenv.sh new file mode 100644 index 0000000..e3842d5 --- /dev/null +++ b/setenv.sh @@ -0,0 +1,2 @@ +export PATH=$(rustc --print sysroot)/bin:$PATH +export RUST_SRC_PATH=$(rustc --print sysroot)/lib/rustlib/src/rust/library/ diff --git a/user/Cargo.toml b/user/Cargo.toml index 92f3f7e..1d7b2ef 100644 --- a/user/Cargo.toml +++ b/user/Cargo.toml @@ -2,17 +2,18 @@ name = "user_lib" version = "0.1.0" authors = ["Yifan Wu "] -edition = "2024" +edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] buddy_system_allocator = "0.6" bitflags = "1.2.1" +riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] } +lazy_static = { version = "1.4.0", features = ["spin_no_std"] } +embedded-graphics = "0.7.1" +oorandom ="11" +virtio-input-decoder = "0.1.4" [profile.release] debug = true - -# [features] -# board_qemu = [] -# board_k210 = [] \ No newline at end of file diff --git a/user/src/bin/adder.rs b/user/src/bin/adder.rs new file mode 100644 index 0000000..1cdce95 --- /dev/null +++ b/user/src/bin/adder.rs @@ -0,0 +1,58 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use core::ptr::addr_of_mut; +use user_lib::{exit, get_time, thread_create, waittid}; + +static mut A: usize = 0; +const PER_THREAD_DEFAULT: usize = 10000; +const THREAD_COUNT_DEFAULT: usize = 16; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = addr_of_mut!(A); + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} + +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + critical_section(&mut t); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { + PER_THREAD = per_thread; + } + let start = get_time(); + let mut v = Vec::new(); + for _ in 0..thread_count { + v.push(thread_create(f as usize, 0) as usize); + } + for tid in v.into_iter() { + waittid(tid); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/adder_atomic.rs b/user/src/bin/adder_atomic.rs new file mode 100644 index 0000000..5e216c6 --- /dev/null +++ b/user/src/bin/adder_atomic.rs @@ -0,0 +1,75 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use core::ptr::addr_of_mut; +use core::sync::atomic::{AtomicBool, Ordering}; +use user_lib::{exit, get_time, thread_create, waittid, yield_}; + +static mut A: usize = 0; +static OCCUPIED: AtomicBool = AtomicBool::new(false); +const PER_THREAD_DEFAULT: usize = 10000; +const THREAD_COUNT_DEFAULT: usize = 16; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = addr_of_mut!(A); + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} + +fn lock() { + while OCCUPIED + .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed) + .is_err() + { + yield_(); + } +} + +fn unlock() { + OCCUPIED.store(false, Ordering::Relaxed); +} + +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + lock(); + critical_section(&mut t); + unlock(); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { + PER_THREAD = per_thread; + } + let start = get_time(); + let mut v = Vec::new(); + for _ in 0..thread_count { + v.push(thread_create(f as usize, 0) as usize); + } + for tid in v.into_iter() { + waittid(tid); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/adder_mutex_blocking.rs b/user/src/bin/adder_mutex_blocking.rs new file mode 100644 index 0000000..7fcb80b --- /dev/null +++ b/user/src/bin/adder_mutex_blocking.rs @@ -0,0 +1,62 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use core::ptr::addr_of_mut; +use user_lib::{exit, get_time, thread_create, waittid}; +use user_lib::{mutex_blocking_create, mutex_lock, mutex_unlock}; + +static mut A: usize = 0; +const PER_THREAD_DEFAULT: usize = 10000; +const THREAD_COUNT_DEFAULT: usize = 16; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = addr_of_mut!(A); + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + mutex_lock(0); + critical_section(&mut t); + mutex_unlock(0); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { + PER_THREAD = per_thread; + } + + let start = get_time(); + assert_eq!(mutex_blocking_create(), 0); + let mut v = Vec::new(); + for _ in 0..thread_count { + v.push(thread_create(f as usize, 0) as usize); + } + for tid in v.into_iter() { + waittid(tid); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/adder_mutex_spin.rs b/user/src/bin/adder_mutex_spin.rs new file mode 100644 index 0000000..f5750af --- /dev/null +++ b/user/src/bin/adder_mutex_spin.rs @@ -0,0 +1,63 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use core::ptr::addr_of_mut; +use user_lib::{exit, get_time, thread_create, waittid}; +use user_lib::{mutex_create, mutex_lock, mutex_unlock}; + +static mut A: usize = 0; +const PER_THREAD_DEFAULT: usize = 10000; +const THREAD_COUNT_DEFAULT: usize = 16; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = addr_of_mut!(A); + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} + +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + mutex_lock(0); + critical_section(&mut t); + mutex_unlock(0); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { + PER_THREAD = per_thread; + } + + let start = get_time(); + assert_eq!(mutex_create(), 0); + let mut v = Vec::new(); + for _ in 0..thread_count { + v.push(thread_create(f as usize, 0) as usize); + } + for tid in v.into_iter() { + waittid(tid); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/adder_peterson_spin.rs b/user/src/bin/adder_peterson_spin.rs new file mode 100644 index 0000000..44e8096 --- /dev/null +++ b/user/src/bin/adder_peterson_spin.rs @@ -0,0 +1,95 @@ +//! It only works on a single CPU! + +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use core::ptr::addr_of_mut; +use core::sync::atomic::{compiler_fence, Ordering}; +use user_lib::{exit, get_time, thread_create, waittid}; + +static mut A: usize = 0; +static mut FLAG: [bool; 2] = [false; 2]; +static mut TURN: usize = 0; +const PER_THREAD_DEFAULT: usize = 2000; +const THREAD_COUNT_DEFAULT: usize = 2; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = addr_of_mut!(A); + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} + +unsafe fn lock(id: usize) { + FLAG[id] = true; + let j = 1 - id; + TURN = j; + // Tell the compiler not to reorder memory operations + // across this fence. + compiler_fence(Ordering::SeqCst); + // Why do we need to use volatile_read here? + // Otherwise the compiler will assume that they will never + // be changed on this thread. Thus, they will be accessed + // only once! + while vload!(FLAG[j]) && vload!(TURN) == j {} +} + +unsafe fn unlock(id: usize) { + FLAG[id] = false; +} + +unsafe fn f(id: usize) -> ! { + let mut t = 2usize; + for _iter in 0..PER_THREAD { + lock(id); + critical_section(&mut t); + unlock(id); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { + PER_THREAD = per_thread; + } + + // uncomment this if you want to check the assembly + // println!( + // "addr: lock={:#x}, unlock={:#x}", + // lock as usize, + // unlock as usize + // ); + let start = get_time(); + let mut v = Vec::new(); + assert_eq!( + thread_count, 2, + "Peterson works when there are only 2 threads." + ); + for id in 0..thread_count { + v.push(thread_create(f as usize, id) as usize); + } + let mut time_cost = Vec::new(); + for tid in v.iter() { + time_cost.push(waittid(*tid)); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/adder_peterson_yield.rs b/user/src/bin/adder_peterson_yield.rs new file mode 100644 index 0000000..b25adf6 --- /dev/null +++ b/user/src/bin/adder_peterson_yield.rs @@ -0,0 +1,96 @@ +//! It only works on a single CPU! + +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use core::{ + ptr::addr_of_mut, + sync::atomic::{compiler_fence, Ordering}, +}; +use user_lib::{exit, get_time, thread_create, waittid, yield_}; + +static mut A: usize = 0; +static mut FLAG: [bool; 2] = [false; 2]; +static mut TURN: usize = 0; +const PER_THREAD_DEFAULT: usize = 2000; +const THREAD_COUNT_DEFAULT: usize = 2; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = addr_of_mut!(A); + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} + +unsafe fn lock(id: usize) { + FLAG[id] = true; + let j = 1 - id; + TURN = j; + // Tell the compiler not to reorder memory operations + // across this fence. + compiler_fence(Ordering::SeqCst); + while FLAG[j] && TURN == j { + yield_(); + } +} + +unsafe fn unlock(id: usize) { + FLAG[id] = false; +} + +unsafe fn f(id: usize) -> ! { + let mut t = 2usize; + for _iter in 0..PER_THREAD { + lock(id); + critical_section(&mut t); + unlock(id); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { + PER_THREAD = per_thread; + } + + // uncomment this if you want to check the assembly + // println!( + // "addr: lock={:#x}, unlock={:#x}", + // lock as usize, + // unlock as usize + // ); + + let start = get_time(); + let mut v = Vec::new(); + assert_eq!( + thread_count, 2, + "Peterson works when there are only 2 threads." + ); + for id in 0..thread_count { + v.push(thread_create(f as usize, id) as usize); + } + let mut time_cost = Vec::new(); + for tid in v.iter() { + time_cost.push(waittid(*tid)); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/adder_simple_spin.rs b/user/src/bin/adder_simple_spin.rs new file mode 100644 index 0000000..be99d28 --- /dev/null +++ b/user/src/bin/adder_simple_spin.rs @@ -0,0 +1,70 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use core::ptr::addr_of_mut; +use user_lib::{exit, get_time, thread_create, waittid}; + +static mut A: usize = 0; +static mut OCCUPIED: bool = false; +const PER_THREAD_DEFAULT: usize = 10000; +const THREAD_COUNT_DEFAULT: usize = 16; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = addr_of_mut!(A); + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} + +unsafe fn lock() { + while vload!(OCCUPIED) {} + OCCUPIED = true; +} + +unsafe fn unlock() { + OCCUPIED = false; +} + +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + lock(); + critical_section(&mut t); + unlock(); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { + PER_THREAD = per_thread; + } + let start = get_time(); + let mut v = Vec::new(); + for _ in 0..thread_count { + v.push(thread_create(f as usize, 0) as usize); + } + for tid in v.into_iter() { + waittid(tid); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/adder_simple_yield.rs b/user/src/bin/adder_simple_yield.rs new file mode 100644 index 0000000..2de7924 --- /dev/null +++ b/user/src/bin/adder_simple_yield.rs @@ -0,0 +1,72 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use core::ptr::addr_of_mut; +use user_lib::{exit, get_time, thread_create, waittid, yield_}; + +static mut A: usize = 0; +static mut OCCUPIED: bool = false; +const PER_THREAD_DEFAULT: usize = 10000; +const THREAD_COUNT_DEFAULT: usize = 16; +static mut PER_THREAD: usize = 0; + +unsafe fn critical_section(t: &mut usize) { + let a = addr_of_mut!(A); + let cur = a.read_volatile(); + for _ in 0..500 { + *t = (*t) * (*t) % 10007; + } + a.write_volatile(cur + 1); +} + +unsafe fn lock() { + while OCCUPIED { + yield_(); + } + OCCUPIED = true; +} + +unsafe fn unlock() { + OCCUPIED = false; +} + +unsafe fn f() -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + lock(); + critical_section(&mut t); + unlock(); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let mut thread_count = THREAD_COUNT_DEFAULT; + let mut per_thread = PER_THREAD_DEFAULT; + if argc >= 2 { + thread_count = argv[1].parse().unwrap(); + if argc >= 3 { + per_thread = argv[2].parse().unwrap(); + } + } + unsafe { + PER_THREAD = per_thread; + } + let start = get_time(); + let mut v = Vec::new(); + for _ in 0..thread_count { + v.push(thread_create(f as usize, 0) as usize); + } + for tid in v.into_iter() { + waittid(tid); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, unsafe { PER_THREAD } * thread_count); + 0 +} diff --git a/user/src/bin/barrier_condvar.rs b/user/src/bin/barrier_condvar.rs new file mode 100644 index 0000000..7157772 --- /dev/null +++ b/user/src/bin/barrier_condvar.rs @@ -0,0 +1,83 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use core::cell::UnsafeCell; +use lazy_static::*; +use user_lib::{ + condvar_create, condvar_signal, condvar_wait, exit, mutex_create, mutex_lock, mutex_unlock, + thread_create, waittid, +}; + +const THREAD_NUM: usize = 3; + +struct Barrier { + mutex_id: usize, + condvar_id: usize, + count: UnsafeCell, +} + +impl Barrier { + pub fn new() -> Self { + Self { + mutex_id: mutex_create() as usize, + condvar_id: condvar_create() as usize, + count: UnsafeCell::new(0), + } + } + pub fn block(&self) { + mutex_lock(self.mutex_id); + let count = self.count.get(); + // SAFETY: Here, the accesses of the count is in the + // critical section protected by the mutex. + unsafe { + *count = *count + 1; + } + if unsafe { *count } == THREAD_NUM { + condvar_signal(self.condvar_id); + } else { + condvar_wait(self.condvar_id, self.mutex_id); + condvar_signal(self.condvar_id); + } + mutex_unlock(self.mutex_id); + } +} + +unsafe impl Sync for Barrier {} + +lazy_static! { + static ref BARRIER_AB: Barrier = Barrier::new(); + static ref BARRIER_BC: Barrier = Barrier::new(); +} + +fn thread_fn() { + for _ in 0..300 { + print!("a"); + } + BARRIER_AB.block(); + for _ in 0..300 { + print!("b"); + } + BARRIER_BC.block(); + for _ in 0..300 { + print!("c"); + } + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + let mut v: Vec = Vec::new(); + for _ in 0..THREAD_NUM { + v.push(thread_create(thread_fn as usize, 0)); + } + for tid in v.into_iter() { + waittid(tid as usize); + } + println!("\nOK!"); + 0 +} diff --git a/user/src/bin/barrier_fail.rs b/user/src/bin/barrier_fail.rs new file mode 100644 index 0000000..11604ca --- /dev/null +++ b/user/src/bin/barrier_fail.rs @@ -0,0 +1,33 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use user_lib::{exit, thread_create, waittid}; + +const THREAD_NUM: usize = 3; + +fn thread_fn() { + for ch in 'a'..='c' { + for _ in 0..300 { + print!("{}", ch); + } + } + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + let mut v: Vec = Vec::new(); + for _ in 0..THREAD_NUM { + v.push(thread_create(thread_fn as usize, 0)); + } + for tid in v.into_iter() { + waittid(tid as usize); + } + println!("\nOK!"); + 0 +} diff --git a/user/src/bin/cat.rs b/user/src/bin/cat.rs new file mode 100644 index 0000000..fea5a24 --- /dev/null +++ b/user/src/bin/cat.rs @@ -0,0 +1,32 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use user_lib::{close, open, read, OpenFlags}; + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + println!("argc = {}", argc); + for (i, arg) in argv.iter().enumerate() { + println!("argv[{}] = {}", i, arg); + } + assert!(argc == 2); + let fd = open(argv[1], OpenFlags::RDONLY); + if fd == -1 { + panic!("Error occurred when opening file"); + } + let fd = fd as usize; + let mut buf = [0u8; 256]; + loop { + let size = read(fd, &mut buf) as usize; + if size == 0 { + break; + } + print!("{}", core::str::from_utf8(&buf[..size]).unwrap()); + } + close(fd); + 0 +} diff --git a/user/src/bin/cat_filea.rs b/user/src/bin/cat_filea.rs deleted file mode 100644 index 20daf77..0000000 --- a/user/src/bin/cat_filea.rs +++ /dev/null @@ -1,27 +0,0 @@ -#![no_std] -#![no_main] - -#[macro_use] -extern crate user_lib; -extern crate alloc; - -use user_lib::{OpenFlags, close, open, read}; - -#[unsafe(no_mangle)] -pub fn main() -> i32 { - let fd = open("filea\0", OpenFlags::RDONLY); - if fd == -1 { - panic!("Error occured when opening file"); - } - let fd = fd as usize; - let mut buf = [0u8; 256]; - loop { - let size = read(fd, &mut buf) as usize; - if size == 0 { - break; - } - println!("{}", core::str::from_utf8(&buf[..size]).unwrap()); - } - close(fd); - 0 -} diff --git a/user/src/bin/cmdline_args.rs b/user/src/bin/cmdline_args.rs new file mode 100644 index 0000000..9a22745 --- /dev/null +++ b/user/src/bin/cmdline_args.rs @@ -0,0 +1,16 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +#[macro_use] +extern crate user_lib; + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + println!("argc = {}", argc); + for (i, arg) in argv.iter().enumerate() { + println!("argv[{}] = {}", i, arg); + } + 0 +} diff --git a/user/src/bin/condsync_condvar.rs b/user/src/bin/condsync_condvar.rs new file mode 100644 index 0000000..78605ad --- /dev/null +++ b/user/src/bin/condsync_condvar.rs @@ -0,0 +1,59 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +extern crate alloc; + +use alloc::vec; +use user_lib::exit; +use user_lib::{ + condvar_create, condvar_signal, condvar_wait, mutex_blocking_create, mutex_lock, mutex_unlock, +}; +use user_lib::{sleep, thread_create, waittid}; + +static mut A: usize = 0; + +const CONDVAR_ID: usize = 0; +const MUTEX_ID: usize = 0; + +unsafe fn first() -> ! { + sleep(10); + println!("First work, Change A --> 1 and wakeup Second"); + mutex_lock(MUTEX_ID); + A = 1; + condvar_signal(CONDVAR_ID); + mutex_unlock(MUTEX_ID); + exit(0) +} + +unsafe fn second() -> ! { + println!("Second want to continue,but need to wait A=1"); + mutex_lock(MUTEX_ID); + while A == 0 { + println!("Second: A is {}", A); + condvar_wait(CONDVAR_ID, MUTEX_ID); + } + println!("A is {}, Second can work now", A); + mutex_unlock(MUTEX_ID); + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + // create condvar & mutex + assert_eq!(condvar_create() as usize, CONDVAR_ID); + assert_eq!(mutex_blocking_create() as usize, MUTEX_ID); + // create threads + let threads = vec![ + thread_create(first as usize, 0), + thread_create(second as usize, 0), + ]; + // wait for all threads to complete + for thread in threads.iter() { + waittid(*thread as usize); + } + println!("test_condvar passed!"); + 0 +} diff --git a/user/src/bin/condsync_sem.rs b/user/src/bin/condsync_sem.rs new file mode 100644 index 0000000..ee08fac --- /dev/null +++ b/user/src/bin/condsync_sem.rs @@ -0,0 +1,64 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +extern crate alloc; + +use alloc::vec; +use user_lib::exit; +use user_lib::{ + mutex_blocking_create, mutex_lock, mutex_unlock, semaphore_create, semaphore_down, semaphore_up, +}; +use user_lib::{sleep, thread_create, waittid}; + +static mut A: usize = 0; + +const SEM_ID: usize = 0; +const MUTEX_ID: usize = 0; + +unsafe fn first() -> ! { + sleep(10); + println!("First work, Change A --> 1 and wakeup Second"); + mutex_lock(MUTEX_ID); + A = 1; + semaphore_up(SEM_ID); + mutex_unlock(MUTEX_ID); + exit(0) +} + +unsafe fn second() -> ! { + println!("Second want to continue,but need to wait A=1"); + loop { + mutex_lock(MUTEX_ID); + if A == 0 { + println!("Second: A is {}", A); + mutex_unlock(MUTEX_ID); + semaphore_down(SEM_ID); + } else { + mutex_unlock(MUTEX_ID); + break; + } + } + println!("A is {}, Second can work now", A); + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + // create semaphore & mutex + assert_eq!(semaphore_create(0) as usize, SEM_ID); + assert_eq!(mutex_blocking_create() as usize, MUTEX_ID); + // create threads + let threads = vec![ + thread_create(first as usize, 0), + thread_create(second as usize, 0), + ]; + // wait for all threads to complete + for thread in threads.iter() { + waittid(*thread as usize); + } + println!("test_condvar passed!"); + 0 +} diff --git a/user/src/bin/count_lines.rs b/user/src/bin/count_lines.rs new file mode 100644 index 0000000..276e12e --- /dev/null +++ b/user/src/bin/count_lines.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::read; + +#[no_mangle] +pub fn main(_argc: usize, _argv: &[&str]) -> i32 { + let mut buf = [0u8; 256]; + let mut lines = 0usize; + let mut total_size = 0usize; + loop { + let len = read(0, &mut buf) as usize; + if len == 0 { + break; + } + total_size += len; + let string = core::str::from_utf8(&buf[..len]).unwrap(); + lines += string + .chars() + .fold(0, |acc, c| acc + if c == '\n' { 1 } else { 0 }); + } + if total_size > 0 { + lines += 1; + } + println!("{}", lines); + 0 +} diff --git a/user/src/bin/eisenberg.rs b/user/src/bin/eisenberg.rs new file mode 100644 index 0000000..49a1d45 --- /dev/null +++ b/user/src/bin/eisenberg.rs @@ -0,0 +1,139 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; +extern crate core; + +use alloc::vec::Vec; +use core::{ + sync::atomic::{AtomicUsize, Ordering}, +}; +use user_lib::{exit, sleep, thread_create, waittid}; + +const N: usize = 2; +const THREAD_NUM: usize = 10; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum FlagState { + Out, + Want, + In, +} + +static mut TURN: usize = 0; +static mut FLAG: [FlagState; THREAD_NUM] = [FlagState::Out; THREAD_NUM]; + +static GUARD: AtomicUsize = AtomicUsize::new(0); + +fn critical_test_enter() { + assert_eq!(GUARD.fetch_add(1, Ordering::SeqCst), 0); +} + +fn critical_test_claim() { + assert_eq!(GUARD.load(Ordering::SeqCst), 1); +} + +fn critical_test_exit() { + assert_eq!(GUARD.fetch_sub(1, Ordering::SeqCst), 1); +} + +unsafe fn eisenberg_enter_critical(id: usize) { + /* announce that we want to enter */ + loop { + println!("Thread[{}] try enter", id); + vstore!(FLAG[id], FlagState::Want); + loop { + /* check if any with higher priority is `Want` or `In` */ + let mut prior_thread: Option = None; + let turn = vload!(TURN); + let ring_id = if id < turn { id + THREAD_NUM } else { id }; + // FLAG.iter() may lead to some errors, use for-loop instead + for i in turn..ring_id { + if vload!(FLAG[i % THREAD_NUM]) != FlagState::Out { + prior_thread = Some(i % THREAD_NUM); + break; + } + } + if prior_thread.is_none() { + break; + } + println!( + "Thread[{}]: prior thread {} exist, sleep and retry", + id, + prior_thread.unwrap() + ); + sleep(1); + } + /* now tentatively claim the resource */ + vstore!(FLAG[id], FlagState::In); + /* enforce the order of `claim` and `conflict check`*/ + memory_fence!(); + /* check if anthor thread is also `In`, which imply a conflict*/ + let mut conflict = false; + for i in 0..THREAD_NUM { + if i != id && vload!(FLAG[i]) == FlagState::In { + conflict = true; + } + } + if !conflict { + break; + } + println!("Thread[{}]: CONFLECT!", id); + /* no need to sleep */ + } + /* clain the trun */ + vstore!(TURN, id); + println!("Thread[{}] enter", id); +} + +unsafe fn eisenberg_exit_critical(id: usize) { + /* find next one who wants to enter and give the turn to it*/ + let mut next = id; + let ring_id = id + THREAD_NUM; + for i in (id + 1)..ring_id { + let idx = i % THREAD_NUM; + if vload!(FLAG[idx]) == FlagState::Want { + next = idx; + break; + } + } + vstore!(TURN, next); + /* All done */ + vstore!(FLAG[id], FlagState::Out); + println!("Thread[{}] exit, give turn to {}", id, next); +} + +pub unsafe fn thread_fn(id: usize) -> ! { + println!("Thread[{}] init.", id); + for _ in 0..N { + eisenberg_enter_critical(id); + critical_test_enter(); + for _ in 0..3 { + critical_test_claim(); + sleep(2); + } + critical_test_exit(); + eisenberg_exit_critical(id); + } + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + let mut v = Vec::new(); + // TODO: really shuffle + assert_eq!(THREAD_NUM, 10); + let shuffle: [usize; 10] = [0, 7, 4, 6, 2, 9, 8, 1, 3, 5]; + for i in 0..THREAD_NUM { + v.push(thread_create(thread_fn as usize, shuffle[i])); + } + for tid in v.iter() { + let exit_code = waittid(*tid as usize); + assert_eq!(exit_code, 0, "thread conflict happened!"); + println!("thread#{} exited with code {}", tid, exit_code); + } + println!("main thread exited."); + 0 +} diff --git a/user/src/bin/exit.rs b/user/src/bin/exit.rs index 8e19542..60510c9 100644 --- a/user/src/bin/exit.rs +++ b/user/src/bin/exit.rs @@ -7,7 +7,7 @@ use user_lib::{exit, fork, wait, waitpid, yield_}; const MAGIC: i32 = -0x10384; -#[unsafe(no_mangle)] +#[no_mangle] pub fn main() -> i32 { println!("I am the parent. Forking the child..."); let pid = fork(); diff --git a/user/src/bin/fantastic_text.rs b/user/src/bin/fantastic_text.rs index 0850162..a3402ff 100644 --- a/user/src/bin/fantastic_text.rs +++ b/user/src/bin/fantastic_text.rs @@ -5,12 +5,12 @@ extern crate user_lib; macro_rules! color_text { - ($text:expr, $color:expr) => { + ($text:expr, $color:expr) => {{ format_args!("\x1b[{}m{}\x1b[0m", $color, $text) - }; + }}; } -#[unsafe(no_mangle)] +#[no_mangle] pub fn main() -> i32 { println!( "{}{}{}{}{} {}{}{}{} {}{}{}{}{}{}", diff --git a/user/src/bin/filetest_simple.rs b/user/src/bin/filetest_simple.rs index b192156..3406d55 100644 --- a/user/src/bin/filetest_simple.rs +++ b/user/src/bin/filetest_simple.rs @@ -4,9 +4,9 @@ #[macro_use] extern crate user_lib; -use user_lib::{OpenFlags, close, open, read, write}; +use user_lib::{close, open, read, write, OpenFlags}; -#[unsafe(no_mangle)] +#[no_mangle] pub fn main() -> i32 { let test_str = "Hello, world!"; let filea = "filea\0"; diff --git a/user/src/bin/forktest.rs b/user/src/bin/forktest.rs index f6003c5..5374a56 100644 --- a/user/src/bin/forktest.rs +++ b/user/src/bin/forktest.rs @@ -8,7 +8,7 @@ use user_lib::{exit, fork, wait}; const MAX_CHILD: usize = 30; -#[unsafe(no_mangle)] +#[no_mangle] pub fn main() -> i32 { for i in 0..MAX_CHILD { let pid = fork(); diff --git a/user/src/bin/forktest2.rs b/user/src/bin/forktest2.rs index ee452cc..c91ce15 100644 --- a/user/src/bin/forktest2.rs +++ b/user/src/bin/forktest2.rs @@ -8,7 +8,7 @@ use user_lib::{exit, fork, get_time, getpid, sleep, wait}; static NUM: usize = 30; -#[unsafe(no_mangle)] +#[no_mangle] pub fn main() -> i32 { for _ in 0..NUM { let pid = fork(); diff --git a/user/src/bin/forktest_simple.rs b/user/src/bin/forktest_simple.rs index b9cf587..29a624b 100644 --- a/user/src/bin/forktest_simple.rs +++ b/user/src/bin/forktest_simple.rs @@ -6,7 +6,7 @@ extern crate user_lib; use user_lib::{fork, getpid, wait}; -#[unsafe(no_mangle)] +#[no_mangle] pub fn main() -> i32 { assert_eq!(wait(&mut 0i32), -1); println!("sys_wait without child process test passed!"); diff --git a/user/src/bin/forktree.rs b/user/src/bin/forktree.rs index bcca4e6..6b120ee 100644 --- a/user/src/bin/forktree.rs +++ b/user/src/bin/forktree.rs @@ -4,7 +4,7 @@ #[macro_use] extern crate user_lib; -use user_lib::{exit, fork, getpid, sleep, yield_}; +use user_lib::{exit, fork, getpid, sleep, wait, yield_}; const DEPTH: usize = 4; @@ -27,11 +27,19 @@ fn fork_tree(cur: &str) { println!("pid{}: {}", getpid(), cur); fork_child(cur, '0'); fork_child(cur, '1'); + let mut exit_code: i32 = 0; + for _ in 0..2 { + wait(&mut exit_code); + } } -#[unsafe(no_mangle)] +#[no_mangle] pub fn main() -> i32 { fork_tree(""); + let mut exit_code: i32 = 0; + for _ in 0..2 { + wait(&mut exit_code); + } sleep(3000); 0 } diff --git a/user/src/bin/gui_move.rs b/user/src/bin/gui_move.rs new file mode 100644 index 0000000..240ff8f --- /dev/null +++ b/user/src/bin/gui_move.rs @@ -0,0 +1,82 @@ +#![no_std] +#![no_main] + +extern crate user_lib; +extern crate alloc; + +use user_lib::console::getchar; +use user_lib::{Display, VIRTGPU_XRES, VIRTGPU_YRES}; + +use embedded_graphics::pixelcolor::Rgb888; +use embedded_graphics::prelude::{Drawable, Point, RgbColor, Size}; +use embedded_graphics::primitives::Primitive; +use embedded_graphics::primitives::{PrimitiveStyle, Rectangle}; +use embedded_graphics::draw_target::DrawTarget; + +const INIT_X: i32 = 640; +const INIT_Y: i32 = 400; +const RECT_SIZE: u32 = 40; + +pub struct DrawingBoard { + disp: Display, + latest_pos: Point, +} + +impl DrawingBoard { + pub fn new() -> Self { + Self { + disp: Display::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES)), + latest_pos: Point::new(INIT_X, INIT_Y), + } + } + fn paint(&mut self) { + Rectangle::with_center(self.latest_pos, Size::new(RECT_SIZE, RECT_SIZE)) + .into_styled(PrimitiveStyle::with_stroke(Rgb888::WHITE, 1)) + .draw(&mut self.disp) + .ok(); + } + fn unpaint(&mut self) { + Rectangle::with_center(self.latest_pos, Size::new(RECT_SIZE, RECT_SIZE)) + .into_styled(PrimitiveStyle::with_stroke(Rgb888::BLACK, 1)) + .draw(&mut self.disp) + .ok(); + } + pub fn move_rect(&mut self, dx: i32, dy: i32) { + let new_x = self.latest_pos.x + dx; + let new_y = self.latest_pos.y + dy; + let r = (RECT_SIZE / 2) as i32; + if new_x > r && new_x + r < (VIRTGPU_XRES as i32) && new_y > r && new_y + r < (VIRTGPU_YRES as i32) { + self.unpaint(); + self.latest_pos.x = new_x; + self.latest_pos.y = new_y; + self.paint(); + } + } +} + +const LF: u8 = 0x0au8; +const CR: u8 = 0x0du8; +#[no_mangle] +pub fn main() -> i32 { + let mut board = DrawingBoard::new(); + let _ = board.disp.clear(Rgb888::BLACK).unwrap(); + board.disp.flush(); + loop { + let c = getchar(); + if c == LF || c == CR { + break; + } + let mut moved = true; + match c { + b'w' => board.move_rect(0, -10), + b'a' => board.move_rect(-10, 0), + b's' => board.move_rect(0, 10), + b'd' => board.move_rect(10, 0), + _ => moved = false, + } + if moved { + board.disp.flush(); + } + } + 0 +} diff --git a/user/src/bin/gui_shape.rs b/user/src/bin/gui_shape.rs new file mode 100644 index 0000000..df03df3 --- /dev/null +++ b/user/src/bin/gui_shape.rs @@ -0,0 +1,59 @@ +#![no_std] +#![no_main] + +extern crate alloc; +extern crate user_lib; + +use user_lib::{Display, VIRTGPU_XRES, VIRTGPU_YRES}; + +use embedded_graphics::pixelcolor::Rgb888; +use embedded_graphics::prelude::{DrawTarget, Drawable, Point, RgbColor, Size}; +use embedded_graphics::primitives::{Circle, Primitive, PrimitiveStyle, Rectangle, Triangle}; + +const INIT_X: i32 = 80; +const INIT_Y: i32 = 400; +const RECT_SIZE: u32 = 150; + +pub struct DrawingBoard { + disp: Display, + latest_pos: Point, +} + +impl DrawingBoard { + pub fn new() -> Self { + Self { + disp: Display::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES)), + latest_pos: Point::new(INIT_X, INIT_Y), + } + } + fn paint(&mut self) { + Rectangle::with_center(self.latest_pos, Size::new(RECT_SIZE, RECT_SIZE)) + .into_styled(PrimitiveStyle::with_stroke(Rgb888::RED, 10)) + .draw(&mut self.disp) + .ok(); + Circle::new(self.latest_pos + Point::new(-70, -300), 150) + .into_styled(PrimitiveStyle::with_fill(Rgb888::BLUE)) + .draw(&mut self.disp) + .ok(); + Triangle::new( + self.latest_pos + Point::new(0, 150), + self.latest_pos + Point::new(80, 200), + self.latest_pos + Point::new(-120, 300), + ) + .into_styled(PrimitiveStyle::with_stroke(Rgb888::GREEN, 10)) + .draw(&mut self.disp) + .ok(); + } +} + +#[no_mangle] +pub fn main() -> i32 { + let mut board = DrawingBoard::new(); + let _ = board.disp.clear(Rgb888::BLACK).unwrap(); + for _ in 0..5 { + board.latest_pos.x += RECT_SIZE as i32 + 20; + board.paint(); + } + board.disp.flush(); + 0 +} diff --git a/user/src/bin/gui_snake.rs b/user/src/bin/gui_snake.rs new file mode 100644 index 0000000..6dd4052 --- /dev/null +++ b/user/src/bin/gui_snake.rs @@ -0,0 +1,352 @@ +#![no_std] +#![no_main] + +extern crate alloc; +extern crate user_lib; + +use user_lib::console::getchar; +use user_lib::{key_pressed, sleep, Display, VIRTGPU_XRES, VIRTGPU_YRES}; + +use embedded_graphics::pixelcolor::*; +use embedded_graphics::prelude::{Drawable, Point, RgbColor, Size}; +use embedded_graphics::primitives::Primitive; +use embedded_graphics::primitives::{PrimitiveStyle, Rectangle}; +use embedded_graphics::Pixel; +use embedded_graphics::{draw_target::DrawTarget, prelude::OriginDimensions}; +use oorandom; //random generator + +struct Snake { + parts: [Pixel; MAX_SIZE], + len: usize, + direction: Direction, + size_x: u32, + size_y: u32, +} + +struct SnakeIntoIterator<'a, T: PixelColor, const MAX_SIZE: usize> { + snake: &'a Snake, + index: usize, +} + +impl<'a, T: PixelColor, const MAX_SIZE: usize> IntoIterator for &'a Snake { + type Item = Pixel; + type IntoIter = SnakeIntoIterator<'a, T, MAX_SIZE>; + + fn into_iter(self) -> Self::IntoIter { + SnakeIntoIterator { + snake: self, + index: 0, + } + } +} + +impl<'a, T: PixelColor, const MAX_SIZE: usize> Iterator for SnakeIntoIterator<'a, T, MAX_SIZE> { + type Item = Pixel; + + fn next(&mut self) -> Option { + let cur = self.snake.parts[self.index]; + if self.index < self.snake.len { + self.index += 1; + return Some(cur); + } + None + } +} + +impl Snake { + fn new(color: T, size_x: u32, size_y: u32) -> Snake { + Snake { + parts: [Pixel::(Point { x: 0, y: 0 }, color); MAX_SIZE], + len: 1, + direction: Direction::None, + size_x, + size_y, + } + } + fn set_direction(&mut self, direction: Direction) { + self.direction = direction; + } + fn contains(&self, this: Point) -> bool { + for part in self.into_iter() { + if part.0 == this { + return true; + }; + } + false + } + fn grow(&mut self) { + if self.len < MAX_SIZE - 1 { + self.len += 1; + } + } + fn make_step(&mut self) { + let mut i = self.len; + while i > 0 { + self.parts[i] = self.parts[i - 1]; + i -= 1; + } + match self.direction { + Direction::Left => { + if self.parts[0].0.x == 0 { + self.parts[0].0.x = (self.size_x - 1) as i32; + } else { + self.parts[0].0.x -= 1; + } + } + Direction::Right => { + if self.parts[0].0.x == (self.size_x - 1) as i32 { + self.parts[0].0.x = 0; + } else { + self.parts[0].0.x += 1; + } + } + Direction::Up => { + if self.parts[0].0.y == 0 { + self.parts[0].0.y = (self.size_y - 1) as i32; + } else { + self.parts[0].0.y -= 1; + } + } + Direction::Down => { + if self.parts[0].0.y == (self.size_y - 1) as i32 { + self.parts[0].0.y = 0; + } else { + self.parts[0].0.y += 1; + } + } + Direction::None => {} + } + } +} + +struct Food { + size_x: u32, + size_y: u32, + place: Pixel, + rng: oorandom::Rand32, +} + +impl Food { + pub fn new(color: T, size_x: u32, size_y: u32) -> Self { + let seed = 4; + let rng = oorandom::Rand32::new(seed); + Food { + size_x, + size_y, + place: Pixel(Point { x: 0, y: 0 }, color), + rng, + } + } + fn replace<'a, const MAX_SIZE: usize>(&mut self, iter_source: &Snake) { + let mut p: Point; + 'outer: loop { + let random_number = self.rng.rand_u32(); + let blocked_positions = iter_source.into_iter(); + p = Point { + x: ((random_number >> 24) as u16 % self.size_x as u16).into(), + y: ((random_number >> 16) as u16 % self.size_y as u16).into(), + }; + for blocked_position in blocked_positions { + if p == blocked_position.0 { + continue 'outer; + } + } + break; + } + self.place = Pixel:: { + 0: p, + 1: self.place.1, + } + } + fn get_pixel(&self) -> Pixel { + self.place + } +} + +#[derive(PartialEq, Debug, Clone, Copy)] +pub enum Direction { + Left, + Right, + Up, + Down, + None, +} + +pub struct SnakeGame { + snake: Snake, + food: Food, + food_age: u32, + food_lifetime: u32, + size_x: u32, + size_y: u32, + scale_x: u32, + scale_y: u32, +} + +impl SnakeGame { + pub fn new( + size_x: u32, + size_y: u32, + scale_x: u32, + scale_y: u32, + snake_color: T, + food_color: T, + food_lifetime: u32, + ) -> Self { + let snake = Snake::::new(snake_color, size_x / scale_x, size_y / scale_y); + let mut food = Food::::new(food_color, size_x / scale_x, size_y / scale_y); + food.replace(&snake); + SnakeGame { + snake, + food, + food_age: 0, + food_lifetime, + size_x, + size_y, + scale_x, + scale_y, + } + } + pub fn set_direction(&mut self, direction: Direction) { + self.snake.set_direction(direction); + } + pub fn draw(&mut self, target: &mut D) -> () + where + D: DrawTarget, + { + self.snake.make_step(); + let hit = self.snake.contains(self.food.get_pixel().0); + if hit { + self.snake.grow(); + } + self.food_age += 1; + if self.food_age >= self.food_lifetime || hit { + self.food.replace(&self.snake); + self.food_age = 0; + } + + let mut scaled_display = ScaledDisplay:: { + real_display: target, + size_x: self.size_x / self.scale_x, + size_y: self.size_y / self.scale_y, + scale_x: self.scale_x, + scale_y: self.scale_y, + }; + + for part in self.snake.into_iter() { + _ = part.draw(&mut scaled_display); + } + _ = self.food.get_pixel().draw(&mut scaled_display); + } +} + +/// A dummy DrawTarget implementation that can magnify each pixel so the user code does not need to adapt for scaling things +struct ScaledDisplay<'a, T: DrawTarget> { + real_display: &'a mut T, + size_x: u32, + size_y: u32, + scale_x: u32, + scale_y: u32, +} + +impl<'a, T: DrawTarget> DrawTarget for ScaledDisplay<'a, T> { + type Color = T::Color; + type Error = T::Error; + + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + for pixel in pixels { + let style = PrimitiveStyle::with_fill(pixel.1); + Rectangle::new( + Point::new( + pixel.0.x * self.scale_x as i32, + pixel.0.y * self.scale_y as i32, + ), + Size::new(self.scale_x as u32, self.scale_y as u32), + ) + .into_styled(style) + .draw(self.real_display)?; + } + Ok(()) + } +} + +impl<'a, T: DrawTarget> OriginDimensions for ScaledDisplay<'a, T> { + fn size(&self) -> Size { + Size::new(self.size_x as u32, self.size_y as u32) + } +} + +#[cfg(test)] +mod tests { + + use crate::Snake; + use embedded_graphics::pixelcolor::*; + use embedded_graphics::prelude::*; + + #[test] + fn snake_basic() { + let mut snake = Snake::::new(Rgb888::RED, 8, 8); + snake.set_direction(crate::Direction::Right); + assert_eq!( + Pixel::(Point { x: 0, y: 0 }, Rgb888::RED), + snake.into_iter().next().unwrap() + ); + snake.make_step(); + assert_eq!( + Pixel::(Point { x: 1, y: 0 }, Rgb888::RED), + snake.into_iter().nth(0).unwrap() + ); + assert_eq!( + Pixel::(Point { x: 0, y: 0 }, Rgb888::RED), + snake.into_iter().nth(1).unwrap() + ); + snake.set_direction(crate::Direction::Down); + snake.make_step(); + assert_eq!( + Pixel::(Point { x: 1, y: 1 }, Rgb888::RED), + snake.into_iter().nth(0).unwrap() + ); + assert_eq!( + Pixel::(Point { x: 1, y: 0 }, Rgb888::RED), + snake.into_iter().nth(1).unwrap() + ); + assert_eq!( + Pixel::(Point { x: 0, y: 0 }, Rgb888::RED), + snake.into_iter().nth(2).unwrap() + ); + assert_eq!(true, snake.contains(Point { x: 0, y: 0 })); + assert_eq!(true, snake.contains(Point { x: 1, y: 0 })); + assert_eq!(true, snake.contains(Point { x: 1, y: 1 })); + } +} + +const LF: u8 = 0x0au8; +const CR: u8 = 0x0du8; +#[no_mangle] +pub fn main() -> i32 { + let mut disp = Display::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES)); + let mut game = SnakeGame::<20, Rgb888>::new(1280, 800, 20, 20, Rgb888::RED, Rgb888::YELLOW, 200); + let _ = disp.clear(Rgb888::BLACK).unwrap(); + loop { + if key_pressed() { + let c = getchar(); + match c { + LF => break, + CR => break, + b'w' => game.set_direction(Direction::Up), + b's' => game.set_direction(Direction::Down), + b'a' => game.set_direction(Direction::Left), + b'd' => game.set_direction(Direction::Right), + _ => (), + } + } + let _ = disp.clear(Rgb888::BLACK).unwrap(); + game.draw(&mut disp); + disp.flush(); + sleep(40); + } + 0 +} diff --git a/user/src/bin/gui_tri.rs b/user/src/bin/gui_tri.rs new file mode 100644 index 0000000..69d47f5 --- /dev/null +++ b/user/src/bin/gui_tri.rs @@ -0,0 +1,24 @@ +#![no_std] +#![no_main] + +extern crate user_lib; + +use embedded_graphics::prelude::Size; +use user_lib::{Display, VIRTGPU_XRES, VIRTGPU_YRES}; + +#[no_mangle] +pub fn main() -> i32 { + let mut disp = Display::new(Size::new(VIRTGPU_XRES, VIRTGPU_YRES)); + disp.paint_on_framebuffer(|fb| { + for y in 0..VIRTGPU_YRES as usize { + for x in 0..VIRTGPU_XRES as usize { + let idx = (y * VIRTGPU_XRES as usize + x) * 4; + fb[idx] = x as u8; + fb[idx + 1] = y as u8; + fb[idx + 2] = (x + y) as u8; + } + } + }); + disp.flush(); + 0 +} diff --git a/user/src/bin/hello_world.rs b/user/src/bin/hello_world.rs index cc651e7..10d3f26 100644 --- a/user/src/bin/hello_world.rs +++ b/user/src/bin/hello_world.rs @@ -4,7 +4,7 @@ #[macro_use] extern crate user_lib; -#[unsafe(no_mangle)] +#[no_mangle] pub fn main() -> i32 { println!("Hello world from user mode program!"); 0 diff --git a/user/src/bin/huge_write.rs b/user/src/bin/huge_write.rs index 9327261..2a977c9 100644 --- a/user/src/bin/huge_write.rs +++ b/user/src/bin/huge_write.rs @@ -4,9 +4,9 @@ #[macro_use] extern crate user_lib; -use user_lib::{OpenFlags, close, get_time, open, write}; +use user_lib::{close, get_time, open, write, OpenFlags}; -#[unsafe(no_mangle)] +#[no_mangle] pub fn main() -> i32 { let mut buffer = [0u8; 1024]; // 1KiB for (i, ch) in buffer.iter_mut().enumerate() { @@ -24,7 +24,7 @@ pub fn main() -> i32 { } close(f); let time_ms = (get_time() - start) as usize; - let speed_kbs = size_mb * 1000000 / time_ms; + let speed_kbs = (size_mb << 20) / time_ms; println!( "{}MiB written, time cost = {}ms, write speed = {}KiB/s", size_mb, time_ms, speed_kbs diff --git a/user/src/bin/huge_write_mt.rs b/user/src/bin/huge_write_mt.rs new file mode 100644 index 0000000..0a60fd8 --- /dev/null +++ b/user/src/bin/huge_write_mt.rs @@ -0,0 +1,56 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::{fmt::format, vec::Vec}; +use user_lib::{close, get_time, gettid, open, write, OpenFlags}; +use user_lib::{exit, thread_create, waittid}; + +fn worker(size_kib: usize) { + let mut buffer = [0u8; 1024]; // 1KiB + for (i, ch) in buffer.iter_mut().enumerate() { + *ch = i as u8; + } + let filename = format(format_args!("testf{}\0", gettid())); + let f = open(filename.as_str(), OpenFlags::CREATE | OpenFlags::WRONLY); + if f < 0 { + panic!("Open test file failed!"); + } + let f = f as usize; + for _ in 0..size_kib { + write(f, &buffer); + } + close(f); + exit(0) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + assert_eq!(argc, 2, "wrong argument"); + let size_mb = 1usize; + let size_kb = size_mb << 10; + let workers = argv[1].parse::().expect("wrong argument"); + assert!(workers >= 1 && size_kb % workers == 0, "wrong argument"); + + let start = get_time(); + + let mut v = Vec::new(); + let size_mb = 1usize; + for _ in 0..workers { + v.push(thread_create(worker as usize, size_kb / workers)); + } + for tid in v.iter() { + assert_eq!(0, waittid(*tid as usize)); + } + + let time_ms = (get_time() - start) as usize; + let speed_kbs = size_kb * 1000 / time_ms; + println!( + "{}MiB written by {} threads, time cost = {}ms, write speed = {}KiB/s", + size_mb, workers, time_ms, speed_kbs + ); + 0 +} diff --git a/user/src/bin/infloop.rs b/user/src/bin/infloop.rs new file mode 100644 index 0000000..1f24853 --- /dev/null +++ b/user/src/bin/infloop.rs @@ -0,0 +1,10 @@ +#![no_std] +#![no_main] +#![allow(clippy::empty_loop)] + +extern crate user_lib; + +#[no_mangle] +pub fn main(_argc: usize, _argv: &[&str]) -> ! { + loop {} +} diff --git a/user/src/bin/initproc.rs b/user/src/bin/initproc.rs index d522b77..d25aee1 100644 --- a/user/src/bin/initproc.rs +++ b/user/src/bin/initproc.rs @@ -1,15 +1,14 @@ #![no_std] #![no_main] -#[macro_use] extern crate user_lib; use user_lib::{exec, fork, wait, yield_}; -#[unsafe(no_mangle)] +#[no_mangle] fn main() -> i32 { if fork() == 0 { - exec("user_shell\0"); + exec("user_shell\0", &[core::ptr::null::()]); } else { loop { let mut exit_code: i32 = 0; @@ -18,10 +17,13 @@ fn main() -> i32 { yield_(); continue; } + /* println!( "[initproc] Released a zombie process, pid={}, exit_code={}", - pid, exit_code, + pid, + exit_code, ); + */ } } 0 diff --git a/user/src/bin/inputdev_event.rs b/user/src/bin/inputdev_event.rs new file mode 100644 index 0000000..ea56f3a --- /dev/null +++ b/user/src/bin/inputdev_event.rs @@ -0,0 +1,25 @@ +#![no_std] +#![no_main] + +use user_lib::{event_get, DecodeType, Key, KeyType}; + +#[macro_use] +extern crate user_lib; + +#[no_mangle] +pub fn main() -> i32 { + println!("Input device event test"); + loop { + if let Some(event) = event_get() { + if let Some(decoder_type) = event.decode() { + println!("{:?}", decoder_type); + if let DecodeType::Key(key, keytype) = decoder_type { + if key == Key::Enter && keytype == KeyType::Press { + break; + } + } + } + } + } + 0 +} diff --git a/user/src/bin/matrix.rs b/user/src/bin/matrix.rs index 846f4c6..9ebf48f 100644 --- a/user/src/bin/matrix.rs +++ b/user/src/bin/matrix.rs @@ -44,7 +44,7 @@ fn work(times: isize) { exit(0); } -#[unsafe(no_mangle)] +#[no_mangle] pub fn main() -> i32 { for _ in 0..NUM { let pid = fork(); diff --git a/user/src/bin/mpsc_sem.rs b/user/src/bin/mpsc_sem.rs new file mode 100644 index 0000000..7b72bbb --- /dev/null +++ b/user/src/bin/mpsc_sem.rs @@ -0,0 +1,73 @@ +#![no_std] +#![no_main] +#![allow(clippy::println_empty_string)] + +#[macro_use] +extern crate user_lib; + +extern crate alloc; + +use alloc::vec::Vec; +use user_lib::exit; +use user_lib::{semaphore_create, semaphore_down, semaphore_up}; +use user_lib::{thread_create, waittid}; + +const SEM_MUTEX: usize = 0; +const SEM_EMPTY: usize = 1; +const SEM_AVAIL: usize = 2; +const BUFFER_SIZE: usize = 8; +static mut BUFFER: [usize; BUFFER_SIZE] = [0; BUFFER_SIZE]; +static mut FRONT: usize = 0; +static mut TAIL: usize = 0; +const PRODUCER_COUNT: usize = 4; +const NUMBER_PER_PRODUCER: usize = 100; + +unsafe fn producer(id: *const usize) -> ! { + let id = *id; + for _ in 0..NUMBER_PER_PRODUCER { + semaphore_down(SEM_EMPTY); + semaphore_down(SEM_MUTEX); + BUFFER[TAIL] = id; + TAIL = (TAIL + 1) % BUFFER_SIZE; + semaphore_up(SEM_MUTEX); + semaphore_up(SEM_AVAIL); + } + exit(0) +} + +unsafe fn consumer() -> ! { + for _ in 0..PRODUCER_COUNT * NUMBER_PER_PRODUCER { + semaphore_down(SEM_AVAIL); + semaphore_down(SEM_MUTEX); + print!("{} ", BUFFER[FRONT]); + FRONT = (FRONT + 1) % BUFFER_SIZE; + semaphore_up(SEM_MUTEX); + semaphore_up(SEM_EMPTY); + } + println!(""); + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + // create semaphores + assert_eq!(semaphore_create(1) as usize, SEM_MUTEX); + assert_eq!(semaphore_create(BUFFER_SIZE) as usize, SEM_EMPTY); + assert_eq!(semaphore_create(0) as usize, SEM_AVAIL); + // create threads + let ids: Vec<_> = (0..PRODUCER_COUNT).collect(); + let mut threads = Vec::new(); + for i in 0..PRODUCER_COUNT { + threads.push(thread_create( + producer as usize, + &ids.as_slice()[i] as *const _ as usize, + )); + } + threads.push(thread_create(consumer as usize, 0)); + // wait for all threads to complete + for thread in threads.iter() { + waittid(*thread as usize); + } + println!("mpsc_sem passed!"); + 0 +} diff --git a/user/src/bin/peterson.rs b/user/src/bin/peterson.rs new file mode 100644 index 0000000..e03e2a4 --- /dev/null +++ b/user/src/bin/peterson.rs @@ -0,0 +1,79 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; +extern crate core; + +use alloc::vec::Vec; +use core::sync::atomic::{AtomicUsize, Ordering}; +use user_lib::{exit, sleep, thread_create, waittid}; +const N: usize = 1000; + +static mut TURN: usize = 0; +static mut FLAG: [bool; 2] = [false; 2]; +static GUARD: AtomicUsize = AtomicUsize::new(0); + +fn critical_test_enter() { + assert_eq!(GUARD.fetch_add(1, Ordering::SeqCst), 0); +} + +fn critical_test_claim() { + assert_eq!(GUARD.load(Ordering::SeqCst), 1); +} + +fn critical_test_exit() { + assert_eq!(GUARD.fetch_sub(1, Ordering::SeqCst), 1); +} + +unsafe fn peterson_enter_critical(id: usize, peer_id: usize) { + // println!("Thread[{}] try enter", id); + vstore!(FLAG[id], true); + vstore!(TURN, peer_id); + memory_fence!(); + while vload!(FLAG[peer_id]) && vload!(TURN) == peer_id { + // println!("Thread[{}] enter fail", id); + sleep(1); + // println!("Thread[{}] retry enter", id); + } + // println!("Thread[{}] enter", id); +} + +unsafe fn peterson_exit_critical(id: usize) { + vstore!(FLAG[id], false); + // println!("Thread[{}] exit", id); +} + +pub unsafe fn thread_fn(id: usize) -> ! { + // println!("Thread[{}] init.", id); + let peer_id: usize = id ^ 1; + for iter in 0..N { + if iter % 10 == 0 { + println!("[{}] it={}", id, iter); + } + peterson_enter_critical(id, peer_id); + critical_test_enter(); + for _ in 0..3 { + critical_test_claim(); + sleep(2); + } + critical_test_exit(); + peterson_exit_critical(id); + } + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + let mut v = Vec::new(); + v.push(thread_create(thread_fn as usize, 0)); + // v.push(thread_create(thread_fn as usize, 1)); + for tid in v.iter() { + let exit_code = waittid(*tid as usize); + assert_eq!(exit_code, 0, "thread conflict happened!"); + println!("thread#{} exited with code {}", tid, exit_code); + } + println!("main thread exited."); + 0 +} diff --git a/user/src/bin/phil_din_mutex.rs b/user/src/bin/phil_din_mutex.rs new file mode 100644 index 0000000..8e7b566 --- /dev/null +++ b/user/src/bin/phil_din_mutex.rs @@ -0,0 +1,107 @@ +#![no_std] +#![no_main] +#![allow(clippy::println_empty_string)] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use user_lib::{exit, get_time, sleep}; +use user_lib::{mutex_blocking_create, mutex_lock, mutex_unlock}; +use user_lib::{thread_create, waittid}; + +const N: usize = 5; +const ROUND: usize = 4; +// A round: think -> wait for forks -> eat +const GRAPH_SCALE: usize = 100; + +fn get_time_u() -> usize { + get_time() as usize +} + +// Time unit: ms +const ARR: [[usize; ROUND * 2]; N] = [ + [700, 800, 1000, 400, 500, 600, 200, 400], + [300, 600, 200, 700, 1000, 100, 300, 600], + [500, 200, 900, 200, 400, 600, 1200, 400], + [500, 1000, 600, 500, 800, 600, 200, 900], + [600, 100, 600, 600, 200, 500, 600, 200], +]; +static mut THINK: [[usize; ROUND * 2]; N] = [[0; ROUND * 2]; N]; +static mut EAT: [[usize; ROUND * 2]; N] = [[0; ROUND * 2]; N]; + +fn philosopher_dining_problem(id: *const usize) { + let id = unsafe { *id }; + let left = id; + let right = if id == N - 1 { 0 } else { id + 1 }; + let min = if left < right { left } else { right }; + let max = left + right - min; + for round in 0..ROUND { + // thinking + unsafe { + THINK[id][2 * round] = get_time_u(); + } + sleep(ARR[id][2 * round]); + unsafe { + THINK[id][2 * round + 1] = get_time_u(); + } + // wait for forks + mutex_lock(min); + mutex_lock(max); + // eating + unsafe { + EAT[id][2 * round] = get_time_u(); + } + sleep(ARR[id][2 * round + 1]); + unsafe { + EAT[id][2 * round + 1] = get_time_u(); + } + mutex_unlock(max); + mutex_unlock(min); + } + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + let mut v = Vec::new(); + let ids: Vec<_> = (0..N).collect(); + let start = get_time_u(); + for i in 0..N { + assert_eq!(mutex_blocking_create(), i as isize); + v.push(thread_create( + philosopher_dining_problem as usize, + &ids.as_slice()[i] as *const _ as usize, + )); + } + for tid in v.iter() { + waittid(*tid as usize); + } + let time_cost = get_time_u() - start; + println!("time cost = {}", time_cost); + println!("'-' -> THINKING; 'x' -> EATING; ' ' -> WAITING "); + for id in (0..N).into_iter().chain(0..=0) { + print!("#{}:", id); + for j in 0..time_cost / GRAPH_SCALE { + let current_time = j * GRAPH_SCALE + start; + if (0..ROUND).any(|round| unsafe { + let start_thinking = THINK[id][2 * round]; + let end_thinking = THINK[id][2 * round + 1]; + start_thinking <= current_time && current_time <= end_thinking + }) { + print!("-"); + } else if (0..ROUND).any(|round| unsafe { + let start_eating = EAT[id][2 * round]; + let end_eating = EAT[id][2 * round + 1]; + start_eating <= current_time && current_time <= end_eating + }) { + print!("x"); + } else { + print!(" "); + }; + } + println!(""); + } + 0 +} diff --git a/user/src/bin/pipe_large_test.rs b/user/src/bin/pipe_large_test.rs new file mode 100644 index 0000000..e85d0a6 --- /dev/null +++ b/user/src/bin/pipe_large_test.rs @@ -0,0 +1,71 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +extern crate alloc; + +use alloc::format; +use user_lib::{close, fork, get_time, pipe, read, wait, write}; + +const LENGTH: usize = 3000; +#[no_mangle] +pub fn main() -> i32 { + // create pipes + // parent write to child + let mut down_pipe_fd = [0usize; 2]; + // child write to parent + let mut up_pipe_fd = [0usize; 2]; + pipe(&mut down_pipe_fd); + pipe(&mut up_pipe_fd); + let mut random_str = [0u8; LENGTH]; + if fork() == 0 { + // close write end of down pipe + close(down_pipe_fd[1]); + // close read end of up pipe + close(up_pipe_fd[0]); + assert_eq!(read(down_pipe_fd[0], &mut random_str) as usize, LENGTH); + close(down_pipe_fd[0]); + let sum: usize = random_str.iter().map(|v| *v as usize).sum::(); + println!("sum = {}(child)", sum); + let sum_str = format!("{}", sum); + write(up_pipe_fd[1], sum_str.as_bytes()); + close(up_pipe_fd[1]); + println!("Child process exited!"); + 0 + } else { + // close read end of down pipe + close(down_pipe_fd[0]); + // close write end of up pipe + close(up_pipe_fd[1]); + // generate a long random string + for ch in random_str.iter_mut() { + *ch = get_time() as u8; + } + // send it + assert_eq!( + write(down_pipe_fd[1], &random_str) as usize, + random_str.len() + ); + // close write end of down pipe + close(down_pipe_fd[1]); + // calculate sum(parent) + let sum: usize = random_str.iter().map(|v| *v as usize).sum::(); + println!("sum = {}(parent)", sum); + // recv sum(child) + let mut child_result = [0u8; 32]; + let result_len = read(up_pipe_fd[0], &mut child_result) as usize; + close(up_pipe_fd[0]); + // check + assert_eq!( + sum, + str::parse::(core::str::from_utf8(&child_result[..result_len]).unwrap()) + .unwrap() + ); + let mut _unused: i32 = 0; + wait(&mut _unused); + println!("pipe_large_test passed!"); + 0 + } +} diff --git a/user/src/bin/pipetest.rs b/user/src/bin/pipetest.rs new file mode 100644 index 0000000..5436c63 --- /dev/null +++ b/user/src/bin/pipetest.rs @@ -0,0 +1,44 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{close, fork, pipe, read, wait, write}; + +static STR: &str = "Hello, world!"; + +#[no_mangle] +pub fn main() -> i32 { + // create pipe + let mut pipe_fd = [0usize; 2]; + pipe(&mut pipe_fd); + // read end + assert_eq!(pipe_fd[0], 3); + // write end + assert_eq!(pipe_fd[1], 4); + if fork() == 0 { + // child process, read from parent + // close write_end + close(pipe_fd[1]); + let mut buffer = [0u8; 32]; + let len_read = read(pipe_fd[0], &mut buffer) as usize; + // close read_end + close(pipe_fd[0]); + assert_eq!(core::str::from_utf8(&buffer[..len_read]).unwrap(), STR); + println!("Read OK, child process exited!"); + 0 + } else { + // parent process, write to child + // close read end + close(pipe_fd[0]); + assert_eq!(write(pipe_fd[1], STR.as_bytes()), STR.len() as isize); + // close write end + close(pipe_fd[1]); + let mut child_exit_code: i32 = 0; + wait(&mut child_exit_code); + assert_eq!(child_exit_code, 0); + println!("pipetest passed!"); + 0 + } +} diff --git a/user/src/bin/priv_csr.rs b/user/src/bin/priv_csr.rs new file mode 100644 index 0000000..fbd678f --- /dev/null +++ b/user/src/bin/priv_csr.rs @@ -0,0 +1,17 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use riscv::register::sstatus::{self, SPP}; + +#[no_mangle] +fn main() -> i32 { + println!("Try to access privileged CSR in U Mode"); + println!("Kernel should kill this application!"); + unsafe { + sstatus::set_spp(SPP::User); + } + 0 +} diff --git a/user/src/bin/priv_inst.rs b/user/src/bin/priv_inst.rs new file mode 100644 index 0000000..04dac37 --- /dev/null +++ b/user/src/bin/priv_inst.rs @@ -0,0 +1,17 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use core::arch::asm; + +#[no_mangle] +fn main() -> i32 { + println!("Try to execute privileged instruction in U Mode"); + println!("Kernel should kill this application!"); + unsafe { + asm!("sret"); + } + 0 +} diff --git a/user/src/bin/race_adder_arg.rs b/user/src/bin/race_adder_arg.rs new file mode 100644 index 0000000..f33c660 --- /dev/null +++ b/user/src/bin/race_adder_arg.rs @@ -0,0 +1,57 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use crate::alloc::string::ToString; +use alloc::vec::Vec; +use core::ptr::addr_of_mut; +use user_lib::{exit, get_time, thread_create, waittid}; + +static mut A: usize = 0; +const PER_THREAD: usize = 1000; +const THREAD_COUNT: usize = 16; + +unsafe fn f(count: usize) -> ! { + let mut t = 2usize; + for _ in 0..PER_THREAD { + let a = addr_of_mut!(A); + let cur = a.read_volatile(); + for _ in 0..count { + t = t * t % 10007; + } + a.write_volatile(cur + 1); + } + exit(t as i32) +} + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + let count: usize; + if argc == 1 { + count = THREAD_COUNT; + } else if argc == 2 { + count = argv[1].to_string().parse::().unwrap(); + } else { + println!( + "ERROR in argv, argc is {}, argv[0] {} , argv[1] {} , argv[2] {}", + argc, argv[0], argv[1], argv[2] + ); + exit(-1); + } + + let start = get_time(); + let mut v = Vec::new(); + for _ in 0..THREAD_COUNT { + v.push(thread_create(f as usize, count) as usize); + } + let mut time_cost = Vec::new(); + for tid in v.iter() { + time_cost.push(waittid(*tid)); + } + println!("time cost is {}ms", get_time() - start); + assert_eq!(unsafe { A }, PER_THREAD * THREAD_COUNT); + 0 +} diff --git a/user/src/bin/random_num.rs b/user/src/bin/random_num.rs new file mode 100644 index 0000000..d68bb13 --- /dev/null +++ b/user/src/bin/random_num.rs @@ -0,0 +1,16 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +use oorandom; + +#[no_mangle] +pub fn main() -> i32 { + println!("random num program!"); + let seed = 4; + let mut rng = oorandom::Rand32::new(seed); + println!("OORandom: Random number 32bit: {}", rng.rand_i32()); + println!("OORandom: Random number range: {}", rng.rand_range(1..100)); + 0 +} diff --git a/user/src/bin/run_pipe_test.rs b/user/src/bin/run_pipe_test.rs new file mode 100644 index 0000000..d3e329d --- /dev/null +++ b/user/src/bin/run_pipe_test.rs @@ -0,0 +1,21 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{exec, fork, wait}; + +#[no_mangle] +pub fn main() -> i32 { + for i in 0..5 { + if fork() == 0 { + exec("pipe_large_test\0", &[core::ptr::null::()]); + } else { + let mut _unused: i32 = 0; + wait(&mut _unused); + println!("Iter {} OK.", i); + } + } + 0 +} diff --git a/user/src/bin/sleep.rs b/user/src/bin/sleep.rs index e02b3ed..641a4f9 100644 --- a/user/src/bin/sleep.rs +++ b/user/src/bin/sleep.rs @@ -15,7 +15,7 @@ fn sleepy() { exit(0); } -#[unsafe(no_mangle)] +#[no_mangle] pub fn main() -> i32 { let current_time = get_time(); let pid = fork(); diff --git a/user/src/bin/sleep_simple.rs b/user/src/bin/sleep_simple.rs index 9f6e2e4..7015a3d 100644 --- a/user/src/bin/sleep_simple.rs +++ b/user/src/bin/sleep_simple.rs @@ -6,7 +6,7 @@ extern crate user_lib; use user_lib::{get_time, sleep}; -#[unsafe(no_mangle)] +#[no_mangle] pub fn main() -> i32 { println!("into sleep test!"); let start = get_time(); diff --git a/user/src/bin/stack_overflow.rs b/user/src/bin/stack_overflow.rs index 731982a..3bec557 100644 --- a/user/src/bin/stack_overflow.rs +++ b/user/src/bin/stack_overflow.rs @@ -12,7 +12,7 @@ fn f(depth: usize) { f(depth + 1); } -#[unsafe(no_mangle)] +#[no_mangle] pub fn main() -> i32 { println!("It should trigger segmentation fault!"); f(0); diff --git a/user/src/bin/stackful_coroutine.rs b/user/src/bin/stackful_coroutine.rs new file mode 100644 index 0000000..3f61979 --- /dev/null +++ b/user/src/bin/stackful_coroutine.rs @@ -0,0 +1,350 @@ +// we porting below codes to Rcore Tutorial v3 +// https://cfsamson.gitbook.io/green-threads-explained-in-200-lines-of-rust/ +// https://github.com/cfsamson/example-greenthreads +#![no_std] +#![no_main] +#![feature(naked_functions)] +//#![feature(asm)] + +extern crate alloc; +#[macro_use] +extern crate user_lib; + +use core::arch::asm; + +//#[macro_use] +use alloc::vec; +use alloc::vec::Vec; + +use user_lib::exit; + +// In our simple example we set most constraints here. +const DEFAULT_STACK_SIZE: usize = 4096; //128 got SEGFAULT, 256(1024, 4096) got right results. +const MAX_TASKS: usize = 5; +static mut RUNTIME: usize = 0; + +pub struct Runtime { + tasks: Vec, + current: usize, +} + +#[derive(PartialEq, Eq, Debug)] +enum State { + Available, + Running, + Ready, +} + +struct Task { + id: usize, + stack: Vec, + ctx: TaskContext, + state: State, +} + +#[derive(Debug, Default)] +#[repr(C)] // not strictly needed but Rust ABI is not guaranteed to be stable +pub struct TaskContext { + // 15 u64 + x1: u64, //ra: return addres + x2: u64, //sp + x8: u64, //s0,fp + x9: u64, //s1 + x18: u64, //x18-27: s2-11 + x19: u64, + x20: u64, + x21: u64, + x22: u64, + x23: u64, + x24: u64, + x25: u64, + x26: u64, + x27: u64, + nx1: u64, //new return addres +} + +impl Task { + fn new(id: usize) -> Self { + // We initialize each task here and allocate the stack. This is not neccesary, + // we can allocate memory for it later, but it keeps complexity down and lets us focus on more interesting parts + // to do it here. The important part is that once allocated it MUST NOT move in memory. + Task { + id: id, + stack: vec![0_u8; DEFAULT_STACK_SIZE], + ctx: TaskContext::default(), + state: State::Available, + } + } +} + +impl Runtime { + pub fn new() -> Self { + // This will be our base task, which will be initialized in the `running` state + let base_task = Task { + id: 0, + stack: vec![0_u8; DEFAULT_STACK_SIZE], + ctx: TaskContext::default(), + state: State::Running, + }; + + // We initialize the rest of our tasks. + let mut tasks = vec![base_task]; + let mut available_tasks: Vec = (1..MAX_TASKS).map(|i| Task::new(i)).collect(); + tasks.append(&mut available_tasks); + + Runtime { tasks, current: 0 } + } + + /// This is cheating a bit, but we need a pointer to our Runtime stored so we can call yield on it even if + /// we don't have a reference to it. + pub fn init(&self) { + unsafe { + let r_ptr: *const Runtime = self; + RUNTIME = r_ptr as usize; + } + } + + /// This is where we start running our runtime. If it is our base task, we call yield until + /// it returns false (which means that there are no tasks scheduled) and we are done. + pub fn run(&mut self) { + while self.t_yield() {} + println!("All tasks finished!"); + } + + /// This is our return function. The only place we use this is in our `guard` function. + /// If the current task is not our base task we set its state to Available. It means + /// we're finished with it. Then we yield which will schedule a new task to be run. + fn t_return(&mut self) { + if self.current != 0 { + self.tasks[self.current].state = State::Available; + self.t_yield(); + } + } + + /// This is the heart of our runtime. Here we go through all tasks and see if anyone is in the `Ready` state. + /// If no task is `Ready` we're all done. This is an extremely simple scheduler using only a round-robin algorithm. + /// + /// If we find a task that's ready to be run we change the state of the current task from `Running` to `Ready`. + /// Then we call switch which will save the current context (the old context) and load the new context + /// into the CPU which then resumes based on the context it was just passed. + /// + /// NOITCE: if we comment below `#[inline(never)]`, we can not get the corrent running result + #[inline(never)] + fn t_yield(&mut self) -> bool { + let mut pos = self.current; + while self.tasks[pos].state != State::Ready { + pos += 1; + if pos == self.tasks.len() { + pos = 0; + } + if pos == self.current { + return false; + } + } + + if self.tasks[self.current].state != State::Available { + self.tasks[self.current].state = State::Ready; + } + + self.tasks[pos].state = State::Running; + let old_pos = self.current; + self.current = pos; + + unsafe { + switch(&mut self.tasks[old_pos].ctx, &self.tasks[pos].ctx); + } + + // NOTE: this might look strange and it is. Normally we would just mark this as `unreachable!()` but our compiler + // is too smart for it's own good so it optimized our code away on release builds. Curiously this happens on windows + // and not on linux. This is a common problem in tests so Rust has a `black_box` function in the `test` crate that + // will "pretend" to use a value we give it to prevent the compiler from eliminating code. I'll just do this instead, + // this code will never be run anyways and if it did it would always be `true`. + self.tasks.len() > 0 + } + + /// While `yield` is the logically interesting function I think this the technically most interesting. + /// + /// When we spawn a new task we first check if there are any available tasks (tasks in `Parked` state). + /// If we run out of tasks we panic in this scenario but there are several (better) ways to handle that. + /// We keep things simple for now. + /// + /// When we find an available task we get the stack length and a pointer to our u8 bytearray. + /// + /// The next part we have to use some unsafe functions. First we write an address to our `guard` function + /// that will be called if the function we provide returns. Then we set the address to the function we + /// pass inn. + /// + /// Third, we set the value of `sp` which is the stack pointer to the address of our provided function so we start + /// executing that first when we are scheuled to run. + /// + /// Lastly we set the state as `Ready` which means we have work to do and is ready to do it. + pub fn spawn(&mut self, f: fn()) { + let available = self + .tasks + .iter_mut() + .find(|t| t.state == State::Available) + .expect("no available task."); + + println!("RUNTIME: spawning task {}\n", available.id); + let size = available.stack.len(); + unsafe { + let s_ptr = available.stack.as_mut_ptr().offset(size as isize); + + // make sure our stack itself is 8 byte aligned - it will always + // offset to a lower memory address. Since we know we're at the "high" + // memory address of our allocated space, we know that offsetting to + // a lower one will be a valid address (given that we actually allocated) + // enough space to actually get an aligned pointer in the first place). + let s_ptr = (s_ptr as usize & !7) as *mut u8; + + available.ctx.x1 = guard as u64; //ctx.x1 is old return address + available.ctx.nx1 = f as u64; //ctx.nx2 is new return address + available.ctx.x2 = s_ptr.offset(-32) as u64; //cxt.x2 is sp + } + available.state = State::Ready; + } +} + +/// This is our guard function that we place on top of the stack. All this function does is set the +/// state of our current task and then `yield` which will then schedule a new task to be run. +fn guard() { + unsafe { + let rt_ptr = RUNTIME as *mut Runtime; + (*rt_ptr).t_return(); + }; +} + +/// We know that Runtime is alive the length of the program and that we only access from one core +/// (so no datarace). We yield execution of the current task by dereferencing a pointer to our +/// Runtime and then calling `t_yield` +pub fn yield_task() { + unsafe { + let rt_ptr = RUNTIME as *mut Runtime; + (*rt_ptr).t_yield(); + }; +} + +/// So here is our inline Assembly. As you remember from our first example this is just a bit more elaborate where we first +/// read out the values of all the registers we need and then sets all the register values to the register values we +/// saved when we suspended exceution on the "new" task. +/// +/// This is essentially all we need to do to save and resume execution. +/// +/// Some details about inline assembly. +/// +/// The assembly commands in the string literal is called the assemblt template. It is preceeded by +/// zero or up to four segments indicated by ":": +/// +/// - First ":" we have our output parameters, this parameters that this function will return. +/// - Second ":" we have the input parameters which is our contexts. We only read from the "new" context +/// but we modify the "old" context saving our registers there (see volatile option below) +/// - Third ":" This our clobber list, this is information to the compiler that these registers can't be used freely +/// - Fourth ":" This is options we can pass inn, Rust has 3: "alignstack", "volatile" and "intel" +/// +/// For this to work on windows we need to use "alignstack" where the compiler adds the neccesary padding to +/// make sure our stack is aligned. Since we modify one of our inputs, our assembly has "side effects" +/// therefore we should use the `volatile` option. I **think** this is actually set for us by default +/// when there are no output parameters given (my own assumption after going through the source code) +/// for the `asm` macro, but we should make it explicit anyway. +/// +/// One last important part (it will not work without this) is the #[naked] attribute. Basically this lets us have full +/// control over the stack layout since normal functions has a prologue-and epilogue added by the +/// compiler that will cause trouble for us. We avoid this by marking the funtion as "Naked". +/// For this to work on `release` builds we also need to use the `#[inline(never)] attribute or else +/// the compiler decides to inline this function (curiously this currently only happens on Windows). +/// If the function is inlined we get a curious runtime error where it fails when switching back +/// to as saved context and in general our assembly will not work as expected. +/// +/// see: https://github.com/rust-lang/rfcs/blob/master/text/1201-naked-fns.md +/// see: https://doc.rust-lang.org/nightly/reference/inline-assembly.html +/// see: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html +#[naked] +#[no_mangle] +unsafe extern "C" fn switch(old: *mut TaskContext, new: *const TaskContext) { + // a0: _old, a1: _new + asm!( + " + sd x1, 0x00(a0) + sd x2, 0x08(a0) + sd x8, 0x10(a0) + sd x9, 0x18(a0) + sd x18, 0x20(a0) + sd x19, 0x28(a0) + sd x20, 0x30(a0) + sd x21, 0x38(a0) + sd x22, 0x40(a0) + sd x23, 0x48(a0) + sd x24, 0x50(a0) + sd x25, 0x58(a0) + sd x26, 0x60(a0) + sd x27, 0x68(a0) + sd x1, 0x70(a0) + + ld x1, 0x00(a1) + ld x2, 0x08(a1) + ld x8, 0x10(a1) + ld x9, 0x18(a1) + ld x18, 0x20(a1) + ld x19, 0x28(a1) + ld x20, 0x30(a1) + ld x21, 0x38(a1) + ld x22, 0x40(a1) + ld x23, 0x48(a1) + ld x24, 0x50(a1) + ld x25, 0x58(a1) + ld x26, 0x60(a1) + ld x27, 0x68(a1) + ld t0, 0x70(a1) + + jr t0 + ", + options(noreturn) + ); +} + +#[no_mangle] +pub fn main() { + println!("stackful_coroutine begin..."); + println!("TASK 0(Runtime) STARTING"); + let mut runtime = Runtime::new(); + runtime.init(); + runtime.spawn(|| { + println!("TASK 1 STARTING"); + let id = 1; + for i in 0..4 { + println!("task: {} counter: {}", id, i); + yield_task(); + } + println!("TASK 1 FINISHED"); + }); + runtime.spawn(|| { + println!("TASK 2 STARTING"); + let id = 2; + for i in 0..8 { + println!("task: {} counter: {}", id, i); + yield_task(); + } + println!("TASK 2 FINISHED"); + }); + runtime.spawn(|| { + println!("TASK 3 STARTING"); + let id = 3; + for i in 0..12 { + println!("task: {} counter: {}", id, i); + yield_task(); + } + println!("TASK 3 FINISHED"); + }); + runtime.spawn(|| { + println!("TASK 4 STARTING"); + let id = 4; + for i in 0..16 { + println!("task: {} counter: {}", id, i); + yield_task(); + } + println!("TASK 4 FINISHED"); + }); + runtime.run(); + println!("stackful_coroutine PASSED"); + exit(0); +} diff --git a/user/src/bin/stackless_coroutine.rs b/user/src/bin/stackless_coroutine.rs new file mode 100644 index 0000000..43aeb2d --- /dev/null +++ b/user/src/bin/stackless_coroutine.rs @@ -0,0 +1,129 @@ +// https://blog.aloni.org/posts/a-stack-less-rust-coroutine-100-loc/ +// https://github.com/chyyuu/example-coroutine-and-thread/tree/stackless-coroutine-x86 +#![no_std] +#![no_main] + +use core::future::Future; +use core::pin::Pin; +use core::task::{Context, Poll}; +use core::task::{RawWaker, RawWakerVTable, Waker}; + +extern crate alloc; +use alloc::collections::VecDeque; + +use alloc::boxed::Box; + +#[macro_use] +extern crate user_lib; + +enum State { + Halted, + Running, +} + +struct Task { + state: State, +} + +impl Task { + fn waiter<'a>(&'a mut self) -> Waiter<'a> { + Waiter { task: self } + } +} + +struct Waiter<'a> { + task: &'a mut Task, +} + +impl<'a> Future for Waiter<'a> { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, _cx: &mut Context) -> Poll { + match self.task.state { + State::Halted => { + self.task.state = State::Running; + Poll::Ready(()) + } + State::Running => { + self.task.state = State::Halted; + Poll::Pending + } + } + } +} + +struct Executor { + tasks: VecDeque>>>, +} + +impl Executor { + fn new() -> Self { + Executor { + tasks: VecDeque::new(), + } + } + + fn push(&mut self, closure: C) + where + F: Future + 'static, + C: FnOnce(Task) -> F, + { + let task = Task { + state: State::Running, + }; + self.tasks.push_back(Box::pin(closure(task))); + } + + fn run(&mut self) { + let waker = create_waker(); + let mut context = Context::from_waker(&waker); + + while let Some(mut task) = self.tasks.pop_front() { + match task.as_mut().poll(&mut context) { + Poll::Pending => { + self.tasks.push_back(task); + } + Poll::Ready(()) => {} + } + } + } +} + +pub fn create_waker() -> Waker { + // Safety: The waker points to a vtable with functions that do nothing. Doing + // nothing is memory-safe. + unsafe { Waker::from_raw(RAW_WAKER) } +} + +const RAW_WAKER: RawWaker = RawWaker::new(core::ptr::null(), &VTABLE); +const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop); + +unsafe fn clone(_: *const ()) -> RawWaker { + RAW_WAKER +} +unsafe fn wake(_: *const ()) {} +unsafe fn wake_by_ref(_: *const ()) {} +unsafe fn drop(_: *const ()) {} + +#[no_mangle] +pub fn main() -> i32 { + println!("stackless coroutine Begin.."); + let mut exec = Executor::new(); + println!(" Create futures"); + for instance in 1..=3 { + exec.push(move |mut task| async move { + println!(" Task {}: begin state", instance); + task.waiter().await; + println!(" Task {}: next state", instance); + task.waiter().await; + println!(" Task {}: end state", instance); + }); + } + + println!(" Running"); + exec.run(); + println!(" Done"); + println!("stackless coroutine PASSED"); + + 0 +} diff --git a/user/src/bin/store_fault.rs b/user/src/bin/store_fault.rs new file mode 100644 index 0000000..f8023eb --- /dev/null +++ b/user/src/bin/store_fault.rs @@ -0,0 +1,15 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +#[no_mangle] +fn main() -> i32 { + println!("Into Test store_fault, we will insert an invalid store operation..."); + println!("Kernel should kill this application!"); + unsafe { + core::ptr::null_mut::().write_volatile(0); + } + 0 +} diff --git a/user/src/bin/sync_sem.rs b/user/src/bin/sync_sem.rs new file mode 100644 index 0000000..b8d1f79 --- /dev/null +++ b/user/src/bin/sync_sem.rs @@ -0,0 +1,45 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +extern crate alloc; + +use alloc::vec; +use user_lib::exit; +use user_lib::{semaphore_create, semaphore_down, semaphore_up}; +use user_lib::{sleep, thread_create, waittid}; + +const SEM_SYNC: usize = 0; + +unsafe fn first() -> ! { + sleep(10); + println!("First work and wakeup Second"); + semaphore_up(SEM_SYNC); + exit(0) +} + +unsafe fn second() -> ! { + println!("Second want to continue,but need to wait first"); + semaphore_down(SEM_SYNC); + println!("Second can work now"); + exit(0) +} + +#[no_mangle] +pub fn main() -> i32 { + // create semaphores + assert_eq!(semaphore_create(0) as usize, SEM_SYNC); + // create threads + let threads = vec![ + thread_create(first as usize, 0), + thread_create(second as usize, 0), + ]; + // wait for all threads to complete + for thread in threads.iter() { + waittid(*thread as usize); + } + println!("sync_sem passed!"); + 0 +} diff --git a/user/src/bin/tcp_simplehttp.rs b/user/src/bin/tcp_simplehttp.rs new file mode 100644 index 0000000..3e61950 --- /dev/null +++ b/user/src/bin/tcp_simplehttp.rs @@ -0,0 +1,188 @@ +#![no_std] +#![no_main] + +use alloc::string::{String, ToString}; + +#[macro_use] +extern crate user_lib; +#[macro_use] +extern crate alloc; + +// use http://localhost:6201/ to access the http server + +use user_lib::{accept, listen, read, write}; + +// get url from the tcp request list. +fn get_url_from_tcp_request(req: &[u8]) -> String { + let mut index = 0; + for i in 4..req.len() { + if req[i] == 0x20 { + index = i; + break; + } + } + + String::from_utf8_lossy(&req[4..index]).to_string() +} + +// just receive GET requests +fn handle_tcp_client(client_fd: usize) -> bool { + // a buf to receive the data from the server + let mut buf = vec![0u8; 1024]; + + let len = read(client_fd as usize, &mut buf); + + println!("receive {} bytes", len); + hexdump(&buf[..len as usize]); + + // verify whether it is a valid HTTP request simply, [0x47,0x45,0x54, 0x20] is GET + if len < 4 || buf[..4] != [0x47, 0x45, 0x54, 0x20] { + println!("it's not a valid http request"); + return false; + } + + let url = get_url_from_tcp_request(&buf); + + if url == "/close" { + let content = r#" + + + + + + + + + + +
+

server closed

+
+ + "#; + let response = format!("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: {}\r\nConnecion: Close\r\n\r\n{}", content.len(),content); + write(client_fd, response.as_bytes()); + // terminate the connection immediately. + return true; + } + + let content = r#" + + + + + + + + + + +
+

rCore-tutorial-V3

+

rCore-tutorial-V3 是一个 基于 RISC-V 架构的 类 Unix 内核.

+
+ +
+
+
+

Rust

+

Rust

+

Rust是一门系统编程语言,专注于安全,尤其是并发安全,支持函数式和命令式以及泛型等编程范式的多范式语言

+
+
+

仓库地址

+

repo url

+

https://github.com/rcore-os/rCore-Tutorial-v3

+
+
+

QQ 群号

+

Official QQ group number

+

735045051

+
+
+
+ +
+

点击下列按钮即可关闭服务器。

+ +
+ + + "#; + + let response = format!("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: {}\r\nConnecion: Close\r\n\r\n{}", content.len(),content); + + // write a response + write(client_fd, response.as_bytes()); + + false +} + +#[no_mangle] +pub fn main() -> i32 { + println!("This is a very simple http server"); + + let tcp_fd = listen(80); + + if tcp_fd < 0 { + println!("Failed to listen on port 80"); + return -1; + } + + loop { + let client = accept(tcp_fd as usize); + println!("client connected: {}", client); + + if client < 1 { + println!("Failed to accept a client on port 80"); + return -1; + } + + if handle_tcp_client(client as usize) { + break; + } + } + + println!("finish tcp test"); + + // String::from_utf8_lossy(&buf[..len as usize]) + + 0 +} + +#[allow(unused)] +pub fn hexdump(data: &[u8]) { + const PRELAND_WIDTH: usize = 70; + println!("{:-^1$}", " hexdump ", PRELAND_WIDTH); + for offset in (0..data.len()).step_by(16) { + for i in 0..16 { + if offset + i < data.len() { + print!("{:02x} ", data[offset + i]); + } else { + print!("{:02} ", ""); + } + } + + print!("{:>6}", ' '); + + for i in 0..16 { + if offset + i < data.len() { + let c = data[offset + i]; + if c >= 0x20 && c <= 0x7e { + print!("{}", c as char); + } else { + print!("."); + } + } else { + print!("{:02} ", ""); + } + } + + println!(""); + } + println!("{:-^1$}", " hexdump end ", PRELAND_WIDTH); +} diff --git a/user/src/bin/threads.rs b/user/src/bin/threads.rs new file mode 100644 index 0000000..3d374fe --- /dev/null +++ b/user/src/bin/threads.rs @@ -0,0 +1,45 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec; +use user_lib::{exit, thread_create, waittid}; + +pub fn thread_a() -> ! { + for _ in 0..1000 { + print!("a"); + } + exit(1) +} + +pub fn thread_b() -> ! { + for _ in 0..1000 { + print!("b"); + } + exit(2) +} + +pub fn thread_c() -> ! { + for _ in 0..1000 { + print!("c"); + } + exit(3) +} + +#[no_mangle] +pub fn main() -> i32 { + let v = vec![ + thread_create(thread_a as usize, 0), + thread_create(thread_b as usize, 0), + thread_create(thread_c as usize, 0), + ]; + for tid in v.iter() { + let exit_code = waittid(*tid as usize); + println!("thread#{} exited with code {}", tid, exit_code); + } + println!("main thread exited."); + 0 +} diff --git a/user/src/bin/threads_arg.rs b/user/src/bin/threads_arg.rs new file mode 100644 index 0000000..c5b26ed --- /dev/null +++ b/user/src/bin/threads_arg.rs @@ -0,0 +1,44 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use alloc::vec::Vec; +use user_lib::{exit, thread_create, waittid}; + +struct Argument { + pub ch: char, + pub rc: i32, +} + +fn thread_print(arg: *const Argument) -> ! { + let arg = unsafe { &*arg }; + for _ in 0..1000 { + print!("{}", arg.ch); + } + exit(arg.rc) +} + +#[no_mangle] +pub fn main() -> i32 { + let mut v = Vec::new(); + let args = [ + Argument { ch: 'a', rc: 1 }, + Argument { ch: 'b', rc: 2 }, + Argument { ch: 'c', rc: 3 }, + ]; + for arg in args.iter() { + v.push(thread_create( + thread_print as usize, + arg as *const _ as usize, + )); + } + for tid in v.iter() { + let exit_code = waittid(*tid as usize); + println!("thread#{} exited with code {}", tid, exit_code); + } + println!("main thread exited."); + 0 +} diff --git a/user/src/bin/udp.rs b/user/src/bin/udp.rs new file mode 100644 index 0000000..68a1a10 --- /dev/null +++ b/user/src/bin/udp.rs @@ -0,0 +1,46 @@ +#![no_std] +#![no_main] + +use alloc::string::String; + +#[macro_use] +extern crate user_lib; +#[macro_use] +extern crate alloc; + +use user_lib::{connect, read, write}; + +#[no_mangle] +pub fn main() -> i32 { + println!("udp test open!"); + + let udp_fd = connect(10 << 24 | 0 << 16 | 2 << 8 | 2, 2001, 26099); + + if udp_fd < 0 { + println!("failed to create udp connection."); + return -1; + } + + let buf = "Hello rCoreOS user program!"; + + println!("send <{}>", buf); + + write(udp_fd as usize, buf.as_bytes()); + + println!("udp send done, waiting for reply."); + + let mut buf = vec![0u8; 1024]; + + let len = read(udp_fd as usize, &mut buf); + + if len < 0 { + println!("can't receive udp packet"); + return -1; + } + + let recv_str = String::from_utf8_lossy(&buf[..len as usize]); + + println!("receive reply <{}>", recv_str); + + 0 +} diff --git a/user/src/bin/until_timeout.rs b/user/src/bin/until_timeout.rs new file mode 100644 index 0000000..a0007cb --- /dev/null +++ b/user/src/bin/until_timeout.rs @@ -0,0 +1,46 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; + +use user_lib::{exec, fork, get_time, kill, waitpid, waitpid_nb, SignalFlags}; + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + assert_eq!(argc, 3, "argc must be 3!"); + let timeout_ms = argv[2] + .parse::() + .expect("Error when parsing timeout!"); + let pid = fork() as usize; + if pid == 0 { + if exec(argv[1], &[core::ptr::null::()]) != 0 { + println!("Error when executing '{}'", argv[1]); + return -4; + } + } else { + let start_time = get_time(); + let mut child_exited = false; + let mut exit_code: i32 = 0; + loop { + if get_time() - start_time > timeout_ms { + break; + } + if waitpid_nb(pid, &mut exit_code) as usize == pid { + child_exited = true; + println!( + "child exited in {}ms, exit_code = {}", + get_time() - start_time, + exit_code, + ); + } + } + if !child_exited { + println!("child has run for {}ms, kill it!", timeout_ms); + kill(pid, SignalFlags::SIGINT.bits()); + assert_eq!(waitpid(pid, &mut exit_code) as usize, pid); + println!("exit code of the child is {}", exit_code); + } + } + 0 +} diff --git a/user/src/bin/user_shell.rs b/user/src/bin/user_shell.rs index 1ca120b..4288248 100644 --- a/user/src/bin/user_shell.rs +++ b/user/src/bin/user_shell.rs @@ -11,40 +11,191 @@ const LF: u8 = 0x0au8; const CR: u8 = 0x0du8; const DL: u8 = 0x7fu8; const BS: u8 = 0x08u8; +const LINE_START: &str = ">> "; use alloc::string::String; +use alloc::vec::Vec; use user_lib::console::getchar; -use user_lib::{exec, fork, waitpid}; +use user_lib::{close, dup, exec, fork, open, pipe, waitpid, OpenFlags}; -#[unsafe(no_mangle)] +#[derive(Debug)] +struct ProcessArguments { + input: String, + output: String, + args_copy: Vec, + args_addr: Vec<*const u8>, +} + +impl ProcessArguments { + pub fn new(command: &str) -> Self { + let args: Vec<_> = command.split(' ').collect(); + let mut args_copy: Vec = args + .iter() + .filter(|&arg| !arg.is_empty()) + .map(|&arg| { + let mut string = String::new(); + string.push_str(arg); + string.push('\0'); + string + }) + .collect(); + + // redirect input + let mut input = String::new(); + if let Some((idx, _)) = args_copy + .iter() + .enumerate() + .find(|(_, arg)| arg.as_str() == "<\0") + { + input = args_copy[idx + 1].clone(); + args_copy.drain(idx..=idx + 1); + } + + // redirect output + let mut output = String::new(); + if let Some((idx, _)) = args_copy + .iter() + .enumerate() + .find(|(_, arg)| arg.as_str() == ">\0") + { + output = args_copy[idx + 1].clone(); + args_copy.drain(idx..=idx + 1); + } + + let mut args_addr: Vec<*const u8> = args_copy.iter().map(|arg| arg.as_ptr()).collect(); + args_addr.push(core::ptr::null::()); + + Self { + input, + output, + args_copy, + args_addr, + } + } +} + +#[no_mangle] pub fn main() -> i32 { println!("Rust user shell"); let mut line: String = String::new(); - print!(">> "); + print!("{}", LINE_START); loop { let c = getchar(); match c { LF | CR => { println!(""); if !line.is_empty() { - line.push('\0'); - let pid = fork(); - if pid == 0 { - // child process - if exec(line.as_str()) == -1 { - println!("Error when executing!"); - return -4; + let splited: Vec<_> = line.as_str().split('|').collect(); + let process_arguments_list: Vec<_> = splited + .iter() + .map(|&cmd| ProcessArguments::new(cmd)) + .collect(); + let mut valid = true; + for (i, process_args) in process_arguments_list.iter().enumerate() { + if i == 0 { + if !process_args.output.is_empty() { + valid = false; + } + } else if i == process_arguments_list.len() - 1 { + if !process_args.input.is_empty() { + valid = false; + } + } else if !process_args.output.is_empty() || !process_args.input.is_empty() + { + valid = false; } - unreachable!(); + } + if process_arguments_list.len() == 1 { + valid = true; + } + if !valid { + println!("Invalid command: Inputs/Outputs cannot be correctly binded!"); } else { + // create pipes + let mut pipes_fd: Vec<[usize; 2]> = Vec::new(); + if !process_arguments_list.is_empty() { + for _ in 0..process_arguments_list.len() - 1 { + let mut pipe_fd = [0usize; 2]; + pipe(&mut pipe_fd); + pipes_fd.push(pipe_fd); + } + } + let mut children: Vec<_> = Vec::new(); + for (i, process_argument) in process_arguments_list.iter().enumerate() { + let pid = fork(); + if pid == 0 { + let input = &process_argument.input; + let output = &process_argument.output; + let args_copy = &process_argument.args_copy; + let args_addr = &process_argument.args_addr; + // redirect input + if !input.is_empty() { + let input_fd = open(input.as_str(), OpenFlags::RDONLY); + if input_fd == -1 { + println!("Error when opening file {}", input); + return -4; + } + let input_fd = input_fd as usize; + close(0); + assert_eq!(dup(input_fd), 0); + close(input_fd); + } + // redirect output + if !output.is_empty() { + let output_fd = open( + output.as_str(), + OpenFlags::CREATE | OpenFlags::WRONLY, + ); + if output_fd == -1 { + println!("Error when opening file {}", output); + return -4; + } + let output_fd = output_fd as usize; + close(1); + assert_eq!(dup(output_fd), 1); + close(output_fd); + } + // receive input from the previous process + if i > 0 { + close(0); + let read_end = pipes_fd.get(i - 1).unwrap()[0]; + assert_eq!(dup(read_end), 0); + } + // send output to the next process + if i < process_arguments_list.len() - 1 { + close(1); + let write_end = pipes_fd.get(i).unwrap()[1]; + assert_eq!(dup(write_end), 1); + } + // close all pipe ends inherited from the parent process + for pipe_fd in pipes_fd.iter() { + close(pipe_fd[0]); + close(pipe_fd[1]); + } + // execute new application + if exec(args_copy[0].as_str(), args_addr.as_slice()) == -1 { + println!("Error when executing!"); + return -4; + } + unreachable!(); + } else { + children.push(pid); + } + } + for pipe_fd in pipes_fd.iter() { + close(pipe_fd[0]); + close(pipe_fd[1]); + } let mut exit_code: i32 = 0; - let exit_pid = waitpid(pid as usize, &mut exit_code); - assert_eq!(pid, exit_pid); - println!("Shell: Process {} exited with code {}", pid, exit_code); + for pid in children.into_iter() { + let exit_pid = waitpid(pid as usize, &mut exit_code); + assert_eq!(pid, exit_pid); + //println!("Shell: Process {} exited with code {}", pid, exit_code); + } } line.clear(); } - print!(">> "); + print!("{}", LINE_START); } BS | DL => { if !line.is_empty() { diff --git a/user/src/bin/usertests.rs b/user/src/bin/usertests.rs index d47dc20..b522af2 100644 --- a/user/src/bin/usertests.rs +++ b/user/src/bin/usertests.rs @@ -10,7 +10,9 @@ extern crate user_lib; // item of TESTS : app_name(argv_0), argv_1, argv_2, argv_3, exit_code static SUCC_TESTS: &[(&str, &str, &str, &str, i32)] = &[ ("filetest_simple\0", "\0", "\0", "\0", 0), - ("cat_filea\0", "\0", "\0", "\0", 0), + ("cat\0", "filea\0", "\0", "\0", 0), + ("cmdline_args\0", "1\0", "2\0", "3\0", 0), + ("eisenberg\0", "\0", "\0", "\0", 0), ("exit\0", "\0", "\0", "\0", 0), ("fantastic_text\0", "\0", "\0", "\0", 0), ("forktest_simple\0", "\0", "\0", "\0", 0), @@ -20,12 +22,40 @@ static SUCC_TESTS: &[(&str, &str, &str, &str, i32)] = &[ ("hello_world\0", "\0", "\0", "\0", 0), ("huge_write\0", "\0", "\0", "\0", 0), ("matrix\0", "\0", "\0", "\0", 0), + ("mpsc_sem\0", "\0", "\0", "\0", 0), + ("peterson\0", "\0", "\0", "\0", 0), + ("phil_din_mutex\0", "\0", "\0", "\0", 0), + ("pipe_large_test\0", "\0", "\0", "\0", 0), + ("pipetest\0", "\0", "\0", "\0", 0), + ("adder_peterson_spin\0", "\0", "\0", "\0", 0), + ("adder_peterson_yield\0", "\0", "\0", "\0", 0), + ("adder_mutex_blocking\0", "\0", "\0", "\0", 0), + ("adder_mutex_spin\0", "\0", "\0", "\0", 0), + ("run_pipe_test\0", "\0", "\0", "\0", 0), ("sleep_simple\0", "\0", "\0", "\0", 0), ("sleep\0", "\0", "\0", "\0", 0), + ("sleep_simple\0", "\0", "\0", "\0", 0), + ("sync_sem\0", "\0", "\0", "\0", 0), + ("condsync_sem\0", "\0", "\0", "\0", 0), + ("condsync_condvar\0", "\0", "\0", "\0", 0), + ("threads_arg\0", "\0", "\0", "\0", 0), + ("threads\0", "\0", "\0", "\0", 0), ("yield\0", "\0", "\0", "\0", 0), + ("barrier_fail\0", "\0", "\0", "\0", 0), + ("barrier_condvar\0", "\0", "\0", "\0", 0), ]; -static FAIL_TESTS: &[(&str, &str, &str, &str, i32)] = &[("stack_overflow\0", "\0", "\0", "\0", -2)]; +static FAIL_TESTS: &[(&str, &str, &str, &str, i32)] = &[ + ("stack_overflow\0", "\0", "\0", "\0", -11), + ("race_adder_loop\0", "\0", "\0", "\0", -6), + ("priv_csr\0", "\0", "\0", "\0", -4), + ("priv_inst\0", "\0", "\0", "\0", -4), + ("store_fault\0", "\0", "\0", "\0", -11), + ("until_timeout\0", "\0", "\0", "\0", -6), + ("adder\0", "\0", "\0", "\0", -6), + ("adder_simple_spin\0", "\0", "\0", "\0", -6), + ("adder_simple_yield\0", "\0", "\0", "\0", -6), +]; use user_lib::{exec, fork, waitpid}; @@ -65,7 +95,7 @@ fn run_tests(tests: &[(&str, &str, &str, &str, i32)]) -> i32 { let pid = fork(); if pid == 0 { - exec(test.0); + exec(test.0, &arr[..]); panic!("unreachable!"); } else { let mut exit_code: i32 = Default::default(); @@ -84,7 +114,7 @@ fn run_tests(tests: &[(&str, &str, &str, &str, i32)]) -> i32 { pass_num } -#[unsafe(no_mangle)] +#[no_mangle] pub fn main() -> i32 { let succ_num = run_tests(SUCC_TESTS); let err_num = run_tests(FAIL_TESTS); diff --git a/user/src/bin/usertests_simple.rs b/user/src/bin/usertests_simple.rs deleted file mode 100644 index 0a73edd..0000000 --- a/user/src/bin/usertests_simple.rs +++ /dev/null @@ -1,43 +0,0 @@ -#![no_std] -#![no_main] - -#[macro_use] -extern crate user_lib; - -static TESTS: &[&str] = &[ - "exit\0", - "fantastic_text\0", - "forktest\0", - "forktest2\0", - "forktest_simple\0", - "hello_world\0", - "matrix\0", - "sleep\0", - "sleep_simple\0", - "stack_overflow\0", - "yield\0", -]; - -use user_lib::{exec, fork, waitpid}; - -#[unsafe(no_mangle)] -pub fn main() -> i32 { - for test in TESTS { - println!("Usertests: Running {}", test); - let pid = fork(); - if pid == 0 { - exec(*test); - panic!("unreachable!"); - } else { - let mut exit_code: i32 = Default::default(); - let wait_pid = waitpid(pid as usize, &mut exit_code); - assert_eq!(pid, wait_pid); - println!( - "\x1b[32mUsertests: Test {} in Process {} exited with code {}\x1b[0m", - test, pid, exit_code - ); - } - } - println!("Usertests passed!"); - 0 -} diff --git a/user/src/bin/yield.rs b/user/src/bin/yield.rs index b9558ef..78b1468 100644 --- a/user/src/bin/yield.rs +++ b/user/src/bin/yield.rs @@ -5,7 +5,7 @@ extern crate user_lib; use user_lib::{getpid, yield_}; -#[unsafe(no_mangle)] +#[no_mangle] pub fn main() -> i32 { println!("Hello, I am process {}.", getpid()); for i in 0..5 { diff --git a/user/src/file.rs b/user/src/file.rs new file mode 100644 index 0000000..19e2564 --- /dev/null +++ b/user/src/file.rs @@ -0,0 +1,30 @@ +use super::*; + +bitflags! { + pub struct OpenFlags: u32 { + const RDONLY = 0; + const WRONLY = 1 << 0; + const RDWR = 1 << 1; + const CREATE = 1 << 9; + const TRUNC = 1 << 10; + } +} + +pub fn dup(fd: usize) -> isize { + sys_dup(fd) +} +pub fn open(path: &str, flags: OpenFlags) -> isize { + sys_open(path, flags.bits) +} +pub fn close(fd: usize) -> isize { + sys_close(fd) +} +pub fn pipe(pipe_fd: &mut [usize]) -> isize { + sys_pipe(pipe_fd) +} +pub fn read(fd: usize, buf: &mut [u8]) -> isize { + sys_read(fd, buf) +} +pub fn write(fd: usize, buf: &[u8]) -> isize { + sys_write(fd, buf) +} diff --git a/user/src/io.rs b/user/src/io.rs new file mode 100644 index 0000000..d2c2a0a --- /dev/null +++ b/user/src/io.rs @@ -0,0 +1,117 @@ +use super::*; +use embedded_graphics::pixelcolor::Rgb888; +use embedded_graphics::prelude::{RgbColor, Size}; +use embedded_graphics::{draw_target::DrawTarget, prelude::OriginDimensions}; +use virtio_input_decoder::Decoder; +pub use virtio_input_decoder::{DecodeType, Key, KeyType, Mouse}; + +pub const VIRTGPU_XRES: u32 = 1280; +pub const VIRTGPU_YRES: u32 = 800; +pub const VIRTGPU_LEN: usize = (VIRTGPU_XRES * VIRTGPU_YRES * 4) as usize; + +pub fn framebuffer() -> isize { + sys_framebuffer() +} +pub fn framebuffer_flush() -> isize { + sys_framebuffer_flush() +} + +pub struct Display { + pub size: Size, + pub fb: &'static mut [u8], +} + +impl Display { + pub fn new(size: Size) -> Self { + let fb_ptr = framebuffer() as *mut u8; + let fb = unsafe { core::slice::from_raw_parts_mut(fb_ptr, VIRTGPU_LEN as usize) }; + Self { size, fb } + } + pub fn framebuffer(&mut self) -> &mut [u8] { + self.fb + } + pub fn paint_on_framebuffer(&mut self, p: impl FnOnce(&mut [u8]) -> ()) { + p(self.framebuffer()); + } + pub fn flush(&self) { + framebuffer_flush(); + } +} + +impl OriginDimensions for Display { + fn size(&self) -> Size { + self.size + } +} + +impl DrawTarget for Display { + type Color = Rgb888; + + type Error = core::convert::Infallible; + + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + pixels.into_iter().for_each(|px| { + let idx = (px.0.y * VIRTGPU_XRES as i32 + px.0.x) as usize * 4; + if idx + 2 >= self.fb.len() { + return; + } + self.fb[idx] = px.1.b(); + self.fb[idx + 1] = px.1.g(); + self.fb[idx + 2] = px.1.r(); + }); + Ok(()) + } +} + +pub fn event_get() -> Option { + let raw_value = sys_event_get(); + if raw_value == 0 { + None + } else { + Some((raw_value as u64).into()) + } +} + +pub fn key_pressed() -> bool { + if sys_key_pressed() == 1 { + true + } else { + false + } +} + +#[repr(C)] +pub struct InputEvent { + pub event_type: u16, + pub code: u16, + pub value: u32, +} + +impl From for InputEvent { + fn from(mut v: u64) -> Self { + let value = v as u32; + v >>= 32; + let code = v as u16; + v >>= 16; + let event_type = v as u16; + Self { + event_type, + code, + value, + } + } +} + +impl InputEvent { + pub fn decode(&self) -> Option { + Decoder::decode( + self.event_type as usize, + self.code as usize, + self.value as usize, + ) + .ok() + } +} diff --git a/user/src/lang_items.rs b/user/src/lang_items.rs index b03e8eb..df0467c 100644 --- a/user/src/lang_items.rs +++ b/user/src/lang_items.rs @@ -1,8 +1,8 @@ -use super::exit; +use super::{getpid, kill, SignalFlags}; #[panic_handler] fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! { - let err = panic_info.message(); + let err = panic_info.message().unwrap(); if let Some(location) = panic_info.location() { println!( "Panicked at {}:{}, {}", @@ -13,5 +13,6 @@ fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! { } else { println!("Panicked: {}", err); } - exit(-1); + kill(getpid() as usize, SignalFlags::SIGABRT.bits()); + unreachable!() } diff --git a/user/src/lib.rs b/user/src/lib.rs index a5584c2..8c709fc 100644 --- a/user/src/lib.rs +++ b/user/src/lib.rs @@ -1,19 +1,30 @@ #![no_std] #![feature(linkage)] +#![feature(panic_info_message)] #![feature(alloc_error_handler)] #[macro_use] pub mod console; +mod file; +mod io; mod lang_items; +mod net; +mod sync; mod syscall; +mod task; extern crate alloc; #[macro_use] extern crate bitflags; +use alloc::vec::Vec; use buddy_system_allocator::LockedHeap; -use core::ptr::addr_of_mut; +pub use file::*; +pub use io::*; +pub use net::*; +pub use sync::*; use syscall::*; +pub use task::*; const USER_HEAP_SIZE: usize = 32768; @@ -27,88 +38,55 @@ pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! { panic!("Heap allocation error, layout = {:?}", layout); } -#[unsafe(no_mangle)] -#[unsafe(link_section = ".text.entry")] -pub extern "C" fn _start() -> ! { +#[no_mangle] +#[link_section = ".text.entry"] +pub extern "C" fn _start(argc: usize, argv: usize) -> ! { unsafe { HEAP.lock() - .init(addr_of_mut!(HEAP_SPACE) as usize, USER_HEAP_SIZE); + .init(HEAP_SPACE.as_ptr() as usize, USER_HEAP_SIZE); } - exit(main()); + let mut v: Vec<&'static str> = Vec::new(); + for i in 0..argc { + let str_start = + unsafe { ((argv + i * core::mem::size_of::()) as *const usize).read_volatile() }; + let len = (0usize..) + .find(|i| unsafe { ((str_start + *i) as *const u8).read_volatile() == 0 }) + .unwrap(); + v.push( + core::str::from_utf8(unsafe { + core::slice::from_raw_parts(str_start as *const u8, len) + }) + .unwrap(), + ); + } + exit(main(argc, v.as_slice())); } #[linkage = "weak"] -#[unsafe(no_mangle)] -fn main() -> i32 { +#[no_mangle] +fn main(_argc: usize, _argv: &[&str]) -> i32 { panic!("Cannot find main!"); } -bitflags! { - pub struct OpenFlags: u32 { - const RDONLY = 0; - const WRONLY = 1 << 0; - const RDWR = 1 << 1; - const CREATE = 1 << 9; - const TRUNC = 1 << 10; - } +#[macro_export] +macro_rules! vstore { + ($var: expr, $value: expr) => { + // unsafe { core::intrinsics::volatile_store($var_ref as *const _ as _, $value) } + unsafe { core::ptr::write_volatile(core::ptr::addr_of_mut!($var), $value); } + }; } -pub fn open(path: &str, flags: OpenFlags) -> isize { - sys_open(path, flags.bits) -} -pub fn close(fd: usize) -> isize { - sys_close(fd) -} -pub fn read(fd: usize, buf: &mut [u8]) -> isize { - sys_read(fd, buf) -} -pub fn write(fd: usize, buf: &[u8]) -> isize { - sys_write(fd, buf) -} -pub fn exit(exit_code: i32) -> ! { - sys_exit(exit_code); -} -pub fn yield_() -> isize { - sys_yield() -} -pub fn get_time() -> isize { - sys_get_time() -} -pub fn getpid() -> isize { - sys_getpid() -} -pub fn fork() -> isize { - sys_fork() -} -pub fn exec(path: &str) -> isize { - sys_exec(path) -} -pub fn wait(exit_code: &mut i32) -> isize { - loop { - match sys_waitpid(-1, exit_code as *mut _) { - -2 => { - yield_(); - } - // -1 or a real pid - exit_pid => return exit_pid, - } - } +#[macro_export] +macro_rules! vload { + ($var: expr) => { + // unsafe { core::intrinsics::volatile_load($var_ref as *const _ as _) } + unsafe { core::ptr::read_volatile(core::ptr::addr_of!($var)) } + }; } -pub fn waitpid(pid: usize, exit_code: &mut i32) -> isize { - loop { - match sys_waitpid(pid as isize, exit_code as *mut _) { - -2 => { - yield_(); - } - // -1 or a real pid - exit_pid => return exit_pid, - } - } -} -pub fn sleep(period_ms: usize) { - let start = sys_get_time(); - while sys_get_time() < start + period_ms as isize { - sys_yield(); - } +#[macro_export] +macro_rules! memory_fence { + () => { + core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst) + }; } diff --git a/user/src/net.rs b/user/src/net.rs new file mode 100644 index 0000000..78529d0 --- /dev/null +++ b/user/src/net.rs @@ -0,0 +1,13 @@ +use super::*; + +pub fn connect(ip: u32, sport: u16, dport: u16) -> isize { + sys_connect(ip, sport, dport) +} + +pub fn listen(sport: u16) -> isize { + sys_listen(sport) +} + +pub fn accept(socket_fd: usize) -> isize { + sys_accept(socket_fd) +} diff --git a/user/src/sync.rs b/user/src/sync.rs new file mode 100644 index 0000000..82cd2a0 --- /dev/null +++ b/user/src/sync.rs @@ -0,0 +1,32 @@ +use super::*; + +pub fn mutex_create() -> isize { + sys_mutex_create(false) +} +pub fn mutex_blocking_create() -> isize { + sys_mutex_create(true) +} +pub fn mutex_lock(mutex_id: usize) { + sys_mutex_lock(mutex_id); +} +pub fn mutex_unlock(mutex_id: usize) { + sys_mutex_unlock(mutex_id); +} +pub fn semaphore_create(res_count: usize) -> isize { + sys_semaphore_create(res_count) +} +pub fn semaphore_up(sem_id: usize) { + sys_semaphore_up(sem_id); +} +pub fn semaphore_down(sem_id: usize) { + sys_semaphore_down(sem_id); +} +pub fn condvar_create() -> isize { + sys_condvar_create() +} +pub fn condvar_signal(condvar_id: usize) { + sys_condvar_signal(condvar_id); +} +pub fn condvar_wait(condvar_id: usize, mutex_id: usize) { + sys_condvar_wait(condvar_id, mutex_id); +} diff --git a/user/src/syscall.rs b/user/src/syscall.rs index 5423cce..48b3e99 100644 --- a/user/src/syscall.rs +++ b/user/src/syscall.rs @@ -1,21 +1,42 @@ -use core::arch::asm; - +const SYSCALL_DUP: usize = 24; +const SYSCALL_CONNECT: usize = 29; +const SYSCALL_LISTEN: usize = 30; +const SYSCALL_ACCEPT: usize = 31; const SYSCALL_OPEN: usize = 56; const SYSCALL_CLOSE: usize = 57; +const SYSCALL_PIPE: usize = 59; const SYSCALL_READ: usize = 63; const SYSCALL_WRITE: usize = 64; const SYSCALL_EXIT: usize = 93; +const SYSCALL_SLEEP: usize = 101; const SYSCALL_YIELD: usize = 124; +const SYSCALL_KILL: usize = 129; const SYSCALL_GET_TIME: usize = 169; const SYSCALL_GETPID: usize = 172; const SYSCALL_FORK: usize = 220; const SYSCALL_EXEC: usize = 221; const SYSCALL_WAITPID: usize = 260; +const SYSCALL_THREAD_CREATE: usize = 1000; +const SYSCALL_GETTID: usize = 1001; +const SYSCALL_WAITTID: usize = 1002; +const SYSCALL_MUTEX_CREATE: usize = 1010; +const SYSCALL_MUTEX_LOCK: usize = 1011; +const SYSCALL_MUTEX_UNLOCK: usize = 1012; +const SYSCALL_SEMAPHORE_CREATE: usize = 1020; +const SYSCALL_SEMAPHORE_UP: usize = 1021; +const SYSCALL_SEMAPHORE_DOWN: usize = 1022; +const SYSCALL_CONDVAR_CREATE: usize = 1030; +const SYSCALL_CONDVAR_SIGNAL: usize = 1031; +const SYSCALL_CONDVAR_WAIT: usize = 1032; +const SYSCALL_FRAMEBUFFER: usize = 2000; +const SYSCALL_FRAMEBUFFER_FLUSH: usize = 2001; +const SYSCALL_EVENT_GET: usize = 3000; +const SYSCALL_KEY_PRESSED: usize = 3001; fn syscall(id: usize, args: [usize; 3]) -> isize { let mut ret: isize; unsafe { - asm!( + core::arch::asm!( "ecall", inlateout("x10") args[0] => ret, in("x11") args[1], @@ -26,6 +47,26 @@ fn syscall(id: usize, args: [usize; 3]) -> isize { ret } +pub fn sys_dup(fd: usize) -> isize { + syscall(SYSCALL_DUP, [fd, 0, 0]) +} + +pub fn sys_connect(dest: u32, sport: u16, dport: u16) -> isize { + syscall( + SYSCALL_CONNECT, + [dest as usize, sport as usize, dport as usize], + ) +} + +// just listen for tcp connections now +pub fn sys_listen(sport: u16) -> isize { + syscall(SYSCALL_LISTEN, [sport as usize, 0, 0]) +} + +pub fn sys_accept(socket_fd: usize) -> isize { + syscall(SYSCALL_ACCEPT, [socket_fd, 0, 0]) +} + pub fn sys_open(path: &str, flags: u32) -> isize { syscall(SYSCALL_OPEN, [path.as_ptr() as usize, flags as usize, 0]) } @@ -34,6 +75,10 @@ pub fn sys_close(fd: usize) -> isize { syscall(SYSCALL_CLOSE, [fd, 0, 0]) } +pub fn sys_pipe(pipe: &mut [usize]) -> isize { + syscall(SYSCALL_PIPE, [pipe.as_mut_ptr() as usize, 0, 0]) +} + pub fn sys_read(fd: usize, buffer: &mut [u8]) -> isize { syscall( SYSCALL_READ, @@ -50,10 +95,18 @@ pub fn sys_exit(exit_code: i32) -> ! { panic!("sys_exit never returns!"); } +pub fn sys_sleep(sleep_ms: usize) -> isize { + syscall(SYSCALL_SLEEP, [sleep_ms, 0, 0]) +} + pub fn sys_yield() -> isize { syscall(SYSCALL_YIELD, [0, 0, 0]) } +pub fn sys_kill(pid: usize, signal: i32) -> isize { + syscall(SYSCALL_KILL, [pid, signal as usize, 0]) +} + pub fn sys_get_time() -> isize { syscall(SYSCALL_GET_TIME, [0, 0, 0]) } @@ -66,10 +119,77 @@ pub fn sys_fork() -> isize { syscall(SYSCALL_FORK, [0, 0, 0]) } -pub fn sys_exec(path: &str) -> isize { - syscall(SYSCALL_EXEC, [path.as_ptr() as usize, 0, 0]) +pub fn sys_exec(path: &str, args: &[*const u8]) -> isize { + syscall( + SYSCALL_EXEC, + [path.as_ptr() as usize, args.as_ptr() as usize, 0], + ) } pub fn sys_waitpid(pid: isize, exit_code: *mut i32) -> isize { syscall(SYSCALL_WAITPID, [pid as usize, exit_code as usize, 0]) } + +pub fn sys_thread_create(entry: usize, arg: usize) -> isize { + syscall(SYSCALL_THREAD_CREATE, [entry, arg, 0]) +} + +pub fn sys_gettid() -> isize { + syscall(SYSCALL_GETTID, [0; 3]) +} + +pub fn sys_waittid(tid: usize) -> isize { + syscall(SYSCALL_WAITTID, [tid, 0, 0]) +} + +pub fn sys_mutex_create(blocking: bool) -> isize { + syscall(SYSCALL_MUTEX_CREATE, [blocking as usize, 0, 0]) +} + +pub fn sys_mutex_lock(id: usize) -> isize { + syscall(SYSCALL_MUTEX_LOCK, [id, 0, 0]) +} + +pub fn sys_mutex_unlock(id: usize) -> isize { + syscall(SYSCALL_MUTEX_UNLOCK, [id, 0, 0]) +} + +pub fn sys_semaphore_create(res_count: usize) -> isize { + syscall(SYSCALL_SEMAPHORE_CREATE, [res_count, 0, 0]) +} + +pub fn sys_semaphore_up(sem_id: usize) -> isize { + syscall(SYSCALL_SEMAPHORE_UP, [sem_id, 0, 0]) +} + +pub fn sys_semaphore_down(sem_id: usize) -> isize { + syscall(SYSCALL_SEMAPHORE_DOWN, [sem_id, 0, 0]) +} + +pub fn sys_condvar_create() -> isize { + syscall(SYSCALL_CONDVAR_CREATE, [0, 0, 0]) +} + +pub fn sys_condvar_signal(condvar_id: usize) -> isize { + syscall(SYSCALL_CONDVAR_SIGNAL, [condvar_id, 0, 0]) +} + +pub fn sys_condvar_wait(condvar_id: usize, mutex_id: usize) -> isize { + syscall(SYSCALL_CONDVAR_WAIT, [condvar_id, mutex_id, 0]) +} + +pub fn sys_framebuffer() -> isize { + syscall(SYSCALL_FRAMEBUFFER, [0, 0, 0]) +} + +pub fn sys_framebuffer_flush() -> isize { + syscall(SYSCALL_FRAMEBUFFER_FLUSH, [0, 0, 0]) +} + +pub fn sys_event_get() -> isize { + syscall(SYSCALL_EVENT_GET, [0, 0, 0]) +} + +pub fn sys_key_pressed() -> isize { + syscall(SYSCALL_KEY_PRESSED, [0, 0, 0]) +} diff --git a/user/src/task.rs b/user/src/task.rs new file mode 100644 index 0000000..9e72b32 --- /dev/null +++ b/user/src/task.rs @@ -0,0 +1,83 @@ +use super::*; + +pub fn exit(exit_code: i32) -> ! { + sys_exit(exit_code); +} +pub fn yield_() -> isize { + sys_yield() +} +pub fn get_time() -> isize { + sys_get_time() +} +pub fn getpid() -> isize { + sys_getpid() +} +pub fn fork() -> isize { + sys_fork() +} +pub fn exec(path: &str, args: &[*const u8]) -> isize { + sys_exec(path, args) +} + +pub fn wait(exit_code: &mut i32) -> isize { + loop { + match sys_waitpid(-1, exit_code as *mut _) { + -2 => { + yield_(); + } + // -1 or a real pid + exit_pid => return exit_pid, + } + } +} + +pub fn waitpid(pid: usize, exit_code: &mut i32) -> isize { + loop { + match sys_waitpid(pid as isize, exit_code as *mut _) { + -2 => { + yield_(); + } + // -1 or a real pid + exit_pid => return exit_pid, + } + } +} + +pub fn waitpid_nb(pid: usize, exit_code: &mut i32) -> isize { + sys_waitpid(pid as isize, exit_code as *mut _) +} + +bitflags! { + pub struct SignalFlags: i32 { + const SIGINT = 1 << 2; + const SIGILL = 1 << 4; + const SIGABRT = 1 << 6; + const SIGFPE = 1 << 8; + const SIGSEGV = 1 << 11; + } +} + +pub fn kill(pid: usize, signal: i32) -> isize { + sys_kill(pid, signal) +} + +pub fn sleep(sleep_ms: usize) { + sys_sleep(sleep_ms); +} + +pub fn thread_create(entry: usize, arg: usize) -> isize { + sys_thread_create(entry, arg) +} +pub fn gettid() -> isize { + sys_gettid() +} +pub fn waittid(tid: usize) -> isize { + loop { + match sys_waittid(tid) { + -2 => { + yield_(); + } + exit_code => return exit_code, + } + } +}