├── .projectile ├── src ├── kernel │ ├── .projectile │ ├── .cargo │ │ └── config.toml │ ├── build.rs │ ├── condvar.rs │ ├── Cargo.toml │ ├── null.rs │ ├── kernel.ld │ ├── stat.rs │ ├── entry.rs │ ├── param.rs │ ├── swtch.rs │ ├── kalloc.rs │ ├── elf.rs │ ├── semaphore.rs │ ├── list.rs │ ├── plic.rs │ ├── main.rs │ ├── pipe.rs │ ├── printf.rs │ ├── start.rs │ ├── memlayout.rs │ ├── sleeplock.rs │ ├── fcntl.rs │ ├── lib.rs │ ├── spinlock.rs │ ├── mpmc.rs │ ├── kernelvec.rs │ ├── error.rs │ ├── defs.rs │ ├── trampoline.rs │ ├── sync.rs │ ├── console.rs │ ├── uart.rs │ ├── log.rs │ ├── trap.rs │ ├── exec.rs │ ├── bio.rs │ └── file.rs ├── mkfs │ ├── .projectile │ └── Cargo.toml └── user │ ├── .projectile │ ├── etc │ └── _paths │ ├── lib │ ├── stat.rs │ ├── pipe.rs │ ├── mutex.rs │ ├── lib.rs │ ├── env.rs │ ├── umalloc.rs │ ├── stdio.rs │ ├── io.rs │ ├── fs.rs │ └── path.rs │ ├── bin │ ├── clear.rs │ ├── echo.rs │ ├── initcode.rs │ ├── ln.rs │ ├── rm.rs │ ├── touch.rs │ ├── mkdir.rs │ ├── sleep.rs │ ├── kill.rs │ ├── cat.rs │ ├── grep.rs │ ├── init.rs │ ├── wc.rs │ ├── ls.rs │ ├── head.rs │ └── sh.rs │ ├── user.ld │ ├── Cargo.toml │ └── build.rs ├── .gitignore ├── rust-toolchain.toml ├── .cargo └── config.toml ├── Cargo.toml ├── LICENSE-MIT ├── README.org └── LICENSE-APACHE /.projectile: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/kernel/.projectile: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/mkfs/.projectile: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/user/.projectile: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/user/etc/_paths: -------------------------------------------------------------------------------- 1 | /bin 2 | -------------------------------------------------------------------------------- /src/user/lib/stat.rs: -------------------------------------------------------------------------------- 1 | include!("../kernel/stat.rs"); 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | /src/user/usys.rs 4 | /src/user/lib/_* -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | targets = ["riscv64gc-unknown-none-elf"] 4 | -------------------------------------------------------------------------------- /src/kernel/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | # Required for rust-analyzer 2 | [build] 3 | target = "riscv64gc-unknown-none-elf" -------------------------------------------------------------------------------- /src/user/bin/clear.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | use ulib::{io::Write, stdio::stdout}; 4 | 5 | fn main() { 6 | // clear the screen 7 | stdout().write(b"\x1b[2J\x1b[H").unwrap(); 8 | } 9 | -------------------------------------------------------------------------------- /src/kernel/build.rs: -------------------------------------------------------------------------------- 1 | //use std::path::Path; 2 | 3 | fn main() { 4 | //let local_path = Path::new(env!("CARGO_MANIFEST_DIR")); 5 | //println!("cargo:rustc-link-arg-bins=--script={}", local_path.join("kernel.ld").display()); 6 | } 7 | -------------------------------------------------------------------------------- /src/user/bin/echo.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | use ulib::{env, print, println}; 3 | 4 | fn main() { 5 | let args = env::args(); 6 | for arg in args.skip(1) { 7 | print!("{} ", arg); 8 | } 9 | println!(""); 10 | } 11 | -------------------------------------------------------------------------------- /src/user/bin/initcode.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | use ulib::sys; 3 | 4 | static INIT: &str = "/init"; 5 | static ARGV: [&str; 1] = ["init"]; 6 | 7 | fn main() -> sys::Result<()> { 8 | sys::exec(INIT, &ARGV, None)?; 9 | sys::exit(0) 10 | } 11 | -------------------------------------------------------------------------------- /src/user/bin/ln.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | use ulib::{env, fs}; 3 | 4 | fn main() { 5 | let mut args = env::args(); 6 | 7 | if args.len() != 3 { 8 | panic!("Usage: ln old new"); 9 | } 10 | let _ = args.next(); 11 | fs::hard_link(args.next().unwrap(), args.next().unwrap()).unwrap() 12 | } 13 | -------------------------------------------------------------------------------- /src/user/bin/rm.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | use ulib::{env, fs}; 3 | 4 | fn main() { 5 | let mut args = env::args().skip(1).peekable(); 6 | 7 | if args.peek().is_none() { 8 | panic!("Usage: rm files...") 9 | } 10 | for arg in args { 11 | fs::remove_file(arg).unwrap() 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/user/bin/touch.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | use ulib::{env, fs}; 3 | 4 | fn main() { 5 | let mut args = env::args().skip(1).peekable(); 6 | 7 | if args.peek().is_none() { 8 | panic!("Usage: touch files...") 9 | } 10 | for arg in args { 11 | fs::File::create(arg); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/user/bin/mkdir.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | use ulib::{env, fs}; 3 | 4 | fn main() { 5 | let mut args = env::args().skip(1).peekable(); 6 | 7 | if args.peek().is_none() { 8 | panic!("Usage: mkdir path...") 9 | } 10 | for arg in args { 11 | fs::create_dir(arg).unwrap(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/user/bin/sleep.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | use ulib::{env, sys}; 3 | 4 | fn main() { 5 | let mut args = env::args().skip(1).peekable(); 6 | 7 | if args.peek().is_none() { 8 | panic!("Usage: sleep TIME...") 9 | } 10 | 11 | let n = args.next().unwrap(); 12 | sys::sleep(n.parse().unwrap()).unwrap(); 13 | } 14 | -------------------------------------------------------------------------------- /src/user/bin/kill.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | use ulib::{env, sys}; 3 | 4 | fn main() { 5 | let mut args = env::args().skip(1).peekable(); 6 | 7 | if args.peek().is_none() { 8 | panic!("usage: kill pid..."); 9 | } 10 | 11 | for arg in args { 12 | sys::kill(arg.parse::().unwrap()).unwrap() 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.riscv64gc-unknown-none-elf] 2 | runner = """ 3 | qemu-system-riscv64 -machine virt -bios none -m 524M -smp 4 -nographic \ 4 | -serial mon:stdio -global virtio-mmio.force-legacy=false \ 5 | -drive file=target/fs.img,if=none,format=raw,id=x0 \ 6 | -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 \ 7 | -kernel 8 | """ 9 | -------------------------------------------------------------------------------- /src/mkfs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mkfs" 3 | description = "mkfs octox file system" 4 | version.workspace = true 5 | authors = ["Hayato Ohhashi "] 6 | edition = "2021" 7 | license.workspace = true 8 | repository.workspace = true 9 | 10 | [[bin]] 11 | name = "mkfs" 12 | path = "main.rs" 13 | 14 | [dependencies] 15 | libkernel = { workspace = true } -------------------------------------------------------------------------------- /src/user/lib/pipe.rs: -------------------------------------------------------------------------------- 1 | use crate::{fs::File, sys}; 2 | 3 | pub fn pipe() -> sys::Result<(File, File)> { 4 | let mut fds = [0; 2]; 5 | sys::pipe(&mut fds)?; 6 | let mut file0; 7 | let mut file1; 8 | unsafe { 9 | file0 = File::from_raw_fd(fds[0]); 10 | file1 = File::from_raw_fd(fds[1]); 11 | } 12 | file0.set_cloexec()?; 13 | file1.set_cloexec()?; 14 | Ok((file0, file1)) 15 | } 16 | -------------------------------------------------------------------------------- /src/kernel/condvar.rs: -------------------------------------------------------------------------------- 1 | use crate::{proc, spinlock::MutexGuard}; 2 | 3 | #[derive(Debug)] 4 | pub struct Condvar; 5 | 6 | impl Condvar { 7 | pub const fn new() -> Self { 8 | Self 9 | } 10 | pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> { 11 | proc::sleep(self as *const _ as usize, guard) 12 | } 13 | pub fn notify_all(&self) { 14 | proc::wakeup(self as *const _ as usize); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/kernel/Cargo.toml: -------------------------------------------------------------------------------- 1 | cargo-features = ["per-package-target"] 2 | 3 | [package] 4 | name = "libkernel" 5 | description = "A Rust-based library kernel" 6 | authors = ["Hayato Ohhashi "] 7 | edition = "2021" 8 | version.workspace = true 9 | license.workspace = true 10 | repository.workspace = true 11 | forced-target = "riscv64gc-unknown-none-elf" 12 | 13 | [lib] 14 | name = "kernel" 15 | path = "lib.rs" 16 | 17 | [features] 18 | default = ["kernel"] 19 | kernel = [] 20 | 21 | [dependencies] 22 | -------------------------------------------------------------------------------- /src/kernel/null.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Result; 2 | use crate::file::{Device, Major, DEVSW}; 3 | use crate::vm::VirtAddr; 4 | 5 | pub static NULL: Null = Null; 6 | pub struct Null; 7 | 8 | impl Device for Null { 9 | fn read(&self, _dst: VirtAddr, _n: usize) -> Result { 10 | Ok(0) 11 | } 12 | fn write(&self, _src: VirtAddr, n: usize) -> Result { 13 | Ok(n) 14 | } 15 | fn major(&self) -> crate::file::Major { 16 | crate::file::Major::Null 17 | } 18 | } 19 | 20 | pub fn init() { 21 | DEVSW.set(Major::Null, &NULL).unwrap(); 22 | } 23 | -------------------------------------------------------------------------------- /src/kernel/kernel.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH( "riscv" ) 2 | ENTRY( _entry ) 3 | 4 | SECTIONS 5 | { 6 | . = 0x80000000; 7 | 8 | .text : { 9 | *(.entry) 10 | *(.text .text.*) 11 | . = ALIGN(0x1000); 12 | PROVIDE(trampoline = .); 13 | *(trampsec) 14 | . = ALIGN(0x1000); 15 | PROVIDE(etext = .); 16 | } 17 | 18 | .rodata : { 19 | . = ALIGN(16); 20 | *(.srodata .srodata.*) 21 | . = ALIGN(16); 22 | *(.rodata .rodata.*) 23 | } 24 | 25 | .data : { 26 | . = ALIGN(16); 27 | *(.sbss .sbss.*) 28 | . = ALIGN(16); 29 | *(.bss .bss.*) 30 | } 31 | 32 | PROVIDE(end = .); 33 | } 34 | -------------------------------------------------------------------------------- /src/kernel/stat.rs: -------------------------------------------------------------------------------- 1 | #[repr(u16)] 2 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] 3 | pub enum FileType { 4 | #[default] 5 | Empty = 0, 6 | Dir = 1, 7 | File = 2, 8 | Device = 3, 9 | } 10 | 11 | #[derive(Default, Debug, Clone, Copy)] 12 | #[repr(C)] 13 | pub struct Stat { 14 | pub dev: u32, // File system's disk device 15 | pub ino: u32, // Inode number 16 | pub ftype: FileType, // Type of file 17 | pub nlink: u16, // Number of links to file 18 | pub size: usize, // Size of file in bytes 19 | } 20 | 21 | impl Stat { 22 | pub fn file_type(&self) -> FileType { 23 | self.ftype 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/user/user.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_ARCH( "riscv" ) 2 | ENTRY( main ) 3 | 4 | SECTIONS 5 | { 6 | . = 0x0; 7 | 8 | . = ALIGN(0x1000); 9 | .text : { 10 | *(.text .text.*) 11 | } 12 | 13 | . = ALIGN(0x1000); 14 | .rodata : { 15 | *(.srodata .srodata.*) /* do not need to distinguish this from .rodata */ 16 | . = ALIGN(16); 17 | *(.rodata .rodata.*) 18 | } 19 | 20 | . = ALIGN(0x1000); 21 | .data : { 22 | *(.sdata .sdata.*) /* do not need to distinguish this from .data */ 23 | . = ALIGN(16); 24 | *(.data .data.*) 25 | } 26 | 27 | . = ALIGN(0x1000); 28 | .bss : { 29 | . = ALIGN(16); 30 | *(.sbss .sbss.*) /* do not need to distinguish this from .bss */ 31 | . = ALIGN(16); 32 | *(.bss .bss.*) 33 | } 34 | 35 | PROVIDE(end = .); 36 | } 37 | -------------------------------------------------------------------------------- /src/kernel/entry.rs: -------------------------------------------------------------------------------- 1 | use crate::memlayout::STACK_PAGE_NUM; 2 | use crate::start::start; 3 | use core::arch::asm; 4 | 5 | #[link_section = ".entry"] 6 | #[no_mangle] 7 | pub unsafe extern "C" fn _entry() -> ! { 8 | // set up stack for Rust. 9 | // stack0 is declared in kernel/src/start.rs 10 | // with 4096 * STACK_PAGE_NUM bytes stack per CPU. 11 | // sp = stack0 + (hartid * 4096 * STACK_PAGE_NUM) 12 | asm!( 13 | "la sp, STACK0", 14 | "li a0, 4096 * {ssz}", 15 | //"mul a0, a0, {ssz}", 16 | "csrr a1, mhartid", 17 | "addi a1, a1, 1", 18 | "mul a0, a0, a1", 19 | "add sp, sp, a0", 20 | ssz = const STACK_PAGE_NUM, 21 | ); 22 | 23 | start() 24 | } 25 | -------------------------------------------------------------------------------- /src/user/bin/cat.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | use ulib::{ 4 | env, 5 | fs::File, 6 | io::{Read, Write}, 7 | stdio::{stdin, stdout}, 8 | sys, 9 | }; 10 | 11 | fn main() { 12 | let args = env::args(); 13 | 14 | if args.len() < 2 { 15 | cat(stdin()).unwrap(); 16 | return; 17 | } 18 | 19 | for arg in args.skip(1) { 20 | let file = File::open(arg).unwrap(); 21 | cat(file).unwrap(); 22 | } 23 | } 24 | 25 | fn cat(mut reader: impl Read) -> sys::Result<()> { 26 | let mut buf = [0u8; 1024]; 27 | 28 | loop { 29 | let n = match reader.read(&mut buf) { 30 | Ok(n) if n == 0 => return Ok(()), 31 | Ok(n) => n, 32 | Err(e) => return Err(e), 33 | }; 34 | stdout().write(&buf[..n])?; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | cargo-features = ["per-package-target"] 2 | 3 | [package] 4 | name = "octox" 5 | description = "Unix like Operating System" 6 | authors = ["Hayato Ohhashi "] 7 | edition = "2021" 8 | license.workspace = true 9 | version.workspace = true 10 | repository.workspace = true 11 | forced-target = "riscv64gc-unknown-none-elf" 12 | 13 | [workspace.package] 14 | version = "0.1.0" 15 | license = "MIT/Apache-2.0" 16 | repository = "https://github.com/o8vm/octox" 17 | 18 | [[bin]] 19 | name = "octox" 20 | path = "src/kernel/main.rs" 21 | 22 | [workspace] 23 | members = [ 24 | "src/kernel", 25 | "src/mkfs", 26 | "src/user", 27 | ] 28 | 29 | [workspace.dependencies] 30 | libkernel = { path = "src/kernel", default-features = false } 31 | 32 | [dependencies] 33 | libkernel = { workspace = true, features = ["kernel"] } 34 | -------------------------------------------------------------------------------- /src/kernel/param.rs: -------------------------------------------------------------------------------- 1 | pub const NCPU: usize = 8; // maximum number of CPUs 2 | pub const NPROC: usize = 16; // maximum number of processes 3 | pub const NOFILE: usize = 16; // open files per process 4 | pub const NFILE: usize = 100; // open files per system 5 | pub const NINODE: usize = 50; // maximum number of active i-nodes 6 | pub const NDEV: usize = 10; // maximum major device number 7 | pub const ROOTDEV: u32 = 1; // device number of file system root disk 8 | pub const MAXARG: usize = 32; // max exec arguments 9 | pub const MAXOPBLOCKS: usize = 10; // max # of blocks any FS op writes 10 | pub const LOGSIZE: usize = MAXOPBLOCKS * 3; // max data blocks in on-disk log 11 | pub const NBUF: usize = MAXOPBLOCKS * 3; // size of disk block cache 12 | pub const FSSIZE: usize = 200000; // size of file system in blocks 13 | pub const MAXPATH: usize = 128; // maximum file path name 14 | -------------------------------------------------------------------------------- /src/user/bin/grep.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | extern crate alloc; 3 | 4 | use alloc::string::String; 5 | use ulib::{env, fs::File, io::Read, print, println, stdio::stdin, sys}; 6 | 7 | fn main() { 8 | let args = env::args(); 9 | let len = args.len(); 10 | let mut args = args.skip(1); 11 | let Some(pat) = args.next() else { 12 | panic!("usage: grep patern [file ...]") 13 | }; 14 | 15 | if len < 3 { 16 | grep(pat, stdin()).unwrap(); 17 | return; 18 | } 19 | 20 | for arg in args { 21 | let file = File::open(arg).unwrap(); 22 | grep(pat, file).unwrap(); 23 | } 24 | } 25 | 26 | fn grep(pat: &str, mut fd: impl Read) -> sys::Result<()> { 27 | let mut contents = String::new(); 28 | fd.read_to_string(&mut contents)?; 29 | 30 | for line in contents.lines() { 31 | if line.contains(pat) { 32 | println!("{}", line); 33 | } 34 | } 35 | Ok(()) 36 | } 37 | -------------------------------------------------------------------------------- /src/kernel/swtch.rs: -------------------------------------------------------------------------------- 1 | use crate::proc::Context; 2 | use core::arch::naked_asm; 3 | 4 | #[no_mangle] 5 | #[rustc_align(16)] 6 | #[unsafe(naked)] 7 | pub unsafe extern "C" fn swtch(old: &mut Context, new: &Context) { 8 | naked_asm!( 9 | "sd ra, 0(a0)", 10 | "sd sp, 8(a0)", 11 | "sd s0, 16(a0)", 12 | "sd s1, 24(a0)", 13 | "sd s2, 32(a0)", 14 | "sd s3, 40(a0)", 15 | "sd s4, 48(a0)", 16 | "sd s5, 56(a0)", 17 | "sd s6, 64(a0)", 18 | "sd s7, 72(a0)", 19 | "sd s8, 80(a0)", 20 | "sd s9, 88(a0)", 21 | "sd s10, 96(a0)", 22 | "sd s11, 104(a0)", 23 | "ld ra, 0(a1)", 24 | "ld sp, 8(a1)", 25 | "ld s0, 16(a1)", 26 | "ld s1, 24(a1)", 27 | "ld s2, 32(a1)", 28 | "ld s3, 40(a1)", 29 | "ld s4, 48(a1)", 30 | "ld s5, 56(a1)", 31 | "ld s6, 64(a1)", 32 | "ld s7, 72(a1)", 33 | "ld s8, 80(a1)", 34 | "ld s9, 88(a1)", 35 | "ld s10, 96(a1)", 36 | "ld s11, 104(a1)", 37 | "ret", 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Hayato Ohhashi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/kernel/kalloc.rs: -------------------------------------------------------------------------------- 1 | // Physical memory allocator based on BuddyAllocator. 2 | 3 | use crate::buddy::BuddyAllocator; 4 | use crate::memlayout::PHYSTOP; 5 | use crate::spinlock::Mutex; 6 | use core::alloc::{GlobalAlloc, Layout}; 7 | use core::ptr; 8 | 9 | extern "C" { 10 | // first address after kernel. 11 | // defined by kernel.ld 12 | static mut end: [u8; 0]; 13 | } 14 | 15 | #[global_allocator] 16 | pub static KMEM: Kmem = Kmem(Mutex::new(BuddyAllocator::new(), "kmem")); 17 | 18 | #[alloc_error_handler] 19 | fn on_oom(layout: Layout) -> ! { 20 | panic!("alloc error: {:?}", layout) 21 | } 22 | 23 | pub struct Kmem(Mutex); 24 | 25 | unsafe impl GlobalAlloc for Kmem { 26 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 27 | self.0 28 | .lock() 29 | .alloc(layout) 30 | .map_or(ptr::null_mut(), |p| p.as_ptr()) 31 | } 32 | unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { 33 | self.0.lock().dealloc(ptr, layout) 34 | } 35 | } 36 | 37 | #[allow(static_mut_refs)] 38 | pub fn init() { 39 | unsafe { 40 | KMEM.0.lock().init(end.as_ptr() as usize, PHYSTOP).unwrap(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/user/bin/init.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | extern crate alloc; 3 | use ulib::{ 4 | fs::{self, File, OpenOptions}, path::Path, print, println, process::Command, stdio, sys::{self, Major} 5 | }; 6 | 7 | fn main() -> sys::Result<()> { 8 | loop { 9 | match OpenOptions::new().read(true).write(true).open("/dev/console") { 10 | Err(_) => { 11 | if !Path::new("/dev").is_dir() { 12 | fs::create_dir("/dev"); 13 | } 14 | sys::mknod("/dev/console", Major::Console as usize, 0)?;// Major をそのまま指定できるように自動生成のところの定義を変更すべき 15 | }, 16 | Ok(stdin) => { 17 | stdio::stdout().set(stdin.try_clone()?)?; 18 | stdio::stderr().set(stdin.try_clone()?)?; 19 | stdio::stdin().set(stdin)?; 20 | break; 21 | } 22 | } 23 | } 24 | if File::open("/dev/null").is_err() { 25 | sys::mknod("/dev/null", Major::Null as usize, 0).unwrap(); 26 | } 27 | 28 | loop { 29 | println!("init: starting sh"); 30 | let mut child = Command::new("/bin/sh").spawn().unwrap(); 31 | child.wait().unwrap(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/user/bin/wc.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | use ulib::{env, fs::File, io::Read, print, println, stdio::stdin, sys}; 3 | 4 | fn main() { 5 | let mut args = env::args().skip(1).peekable(); 6 | 7 | if args.peek().is_none() { 8 | wc(stdin()).unwrap(); 9 | } 10 | for arg in args { 11 | let file = File::open(arg).unwrap(); 12 | wc(file).unwrap(); 13 | } 14 | } 15 | 16 | fn wc(mut fd: impl Read) -> sys::Result<()> { 17 | let mut buf = [0u8; 512]; 18 | let mut l = 0; 19 | let mut w = 0; 20 | let mut c = 0; 21 | let mut inword = false; 22 | loop { 23 | let n = fd.read(&mut buf)?; 24 | if n == 0 { 25 | break; 26 | } 27 | for b in &buf[..n] { 28 | c += 1; 29 | match b { 30 | b'\n' => { 31 | l += 1; 32 | inword = false 33 | } 34 | b' ' | b'\r' | b'\t' => inword = false, 35 | _ if !inword => { 36 | w += 1; 37 | inword = true 38 | } 39 | _ => {} 40 | } 41 | } 42 | } 43 | println!("l={}, w={}, c={}", l, w, c); 44 | Ok(()) 45 | } 46 | -------------------------------------------------------------------------------- /src/kernel/elf.rs: -------------------------------------------------------------------------------- 1 | pub const EI_MAG0: usize = 0; 2 | pub const EI_MAG1: usize = 1; 3 | pub const EI_MAG2: usize = 2; 4 | pub const EI_MAG3: usize = 3; 5 | pub const EI_CLSS: usize = 4; 6 | pub const EI_DATA: usize = 5; 7 | 8 | pub const ELFMAG0: u8 = 127; 9 | pub const ELFMAG1: u8 = b'E'; 10 | pub const ELFMAG2: u8 = b'L'; 11 | pub const ELFMAG3: u8 = b'F'; 12 | 13 | pub const ELF64CL: u8 = 2; 14 | 15 | pub const ELFDATA2LSB: u8 = 1; 16 | 17 | pub const PT_LOAD: u32 = 1; 18 | 19 | #[derive(Copy, Clone, Default)] 20 | #[repr(C, packed)] 21 | pub struct ElfHdr { 22 | pub e_ident: [u8; 16], 23 | pub e_type: u16, 24 | pub e_cpu: u16, 25 | pub e_version: u32, 26 | pub e_entry: usize, 27 | pub e_phoff: usize, 28 | pub e_shoff: usize, 29 | pub e_flags: u32, 30 | pub e_ehsize: u16, 31 | pub e_phsize: u16, 32 | pub e_phnum: u16, 33 | pub e_shsize: u16, 34 | pub e_shnum: u16, 35 | pub e_shname: u16, 36 | } 37 | 38 | #[derive(Copy, Clone, Default)] 39 | #[repr(C, packed)] 40 | pub struct ProgHdr { 41 | pub p_type: u32, 42 | pub p_flags: u32, 43 | pub p_offset: usize, 44 | pub p_vaddr: usize, 45 | pub p_paddr: usize, 46 | pub p_fsize: usize, 47 | pub p_msize: usize, 48 | pub p_align: usize, 49 | } 50 | -------------------------------------------------------------------------------- /src/kernel/semaphore.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | condvar::Condvar, 3 | error::{Error::InvalidArgument, Result}, 4 | spinlock::Mutex, 5 | }; 6 | 7 | #[derive(Debug)] 8 | pub struct Semaphore { 9 | mutex: Mutex, 10 | cond: Condvar, 11 | max: isize, 12 | } 13 | 14 | impl Semaphore { 15 | pub fn new(max: isize, name: &'static str) -> Self { 16 | Self { 17 | mutex: Mutex::new(0, name), 18 | cond: Condvar::new(), 19 | max, 20 | } 21 | } 22 | 23 | pub fn wait(&self) -> Result<()> { 24 | let mut cnt = self.mutex.lock(); 25 | loop { 26 | if *cnt == -1 { 27 | return Err(InvalidArgument); 28 | } 29 | if *cnt >= self.max { 30 | cnt = self.cond.wait(cnt); 31 | } else { 32 | break; 33 | } 34 | } 35 | *cnt += 1; 36 | Ok(()) 37 | } 38 | 39 | pub fn post(&self) { 40 | let mut cnt = self.mutex.lock(); 41 | assert!(*cnt > 0); 42 | *cnt -= 1; 43 | if *cnt <= self.max { 44 | self.cond.notify_all(); 45 | } 46 | } 47 | 48 | pub fn close(&self) { 49 | let mut cnt = self.mutex.lock(); 50 | *cnt = -1; 51 | self.cond.notify_all(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/kernel/list.rs: -------------------------------------------------------------------------------- 1 | // double-linked, circular list. double-linked makes remove 2 | // fast. circular simplifies code, because don't have to check for 3 | // empty list in insert and remove. 4 | // ref: https://github.com/mit-pdos/xv6-riscv-fall19/blob/xv6-riscv-fall19/kernel/list.c 5 | use core::ptr; 6 | 7 | pub struct List { 8 | next: *mut List, 9 | prev: *mut List, 10 | } 11 | 12 | impl List { 13 | pub fn init(&mut self) { 14 | self.prev = self; 15 | self.next = self; 16 | } 17 | 18 | pub fn is_empty(&self) -> bool { 19 | ptr::eq(self.next, self) 20 | } 21 | 22 | pub unsafe fn remove(e: *mut List) { 23 | (*(*e).prev).next = (*e).next; 24 | (*(*e).next).prev = (*e).prev; 25 | } 26 | 27 | pub unsafe fn pop(&mut self) -> Option { 28 | if self.is_empty() { 29 | return None; 30 | } 31 | let raw_addr = self.next as usize; 32 | Self::remove(self.next); 33 | Some(raw_addr) 34 | } 35 | 36 | pub unsafe fn push(&mut self, raw_addr: usize) { 37 | let e = raw_addr as *mut List; 38 | ptr::write( 39 | e, 40 | List { 41 | prev: self, 42 | next: self.next, 43 | }, 44 | ); 45 | (*self.next).prev = e; 46 | self.next = e; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/user/bin/ls.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | use alloc::format; 3 | use ulib::{ 4 | env, 5 | fs::{self, File}, 6 | path::Path, 7 | print, println, sys, 8 | }; 9 | extern crate alloc; 10 | 11 | fn main() { 12 | let args = env::args(); 13 | if args.len() < 2 { 14 | ls(".").unwrap(); 15 | return; 16 | } 17 | for arg in args.skip(1) { 18 | ls(arg).unwrap(); 19 | } 20 | } 21 | 22 | fn ls(path: &str) -> sys::Result<()> { 23 | let path = Path::new(path); 24 | match fs::read_dir(path) { 25 | Err(_) => { 26 | let attr = File::open(path)?.metadata()?; 27 | println!( 28 | "{:14} {:6} {:3} {}", 29 | path.file_name().unwrap(), 30 | format!("{:?}", attr.file_type()), 31 | attr.inum(), 32 | attr.len() 33 | ); 34 | } 35 | Ok(entries) => { 36 | for entry in entries { 37 | let entry = entry.unwrap(); 38 | let attr = entry.metadata()?; 39 | println!( 40 | "{:14} {:6} {:3} {}", 41 | entry.file_name(), 42 | format!("{:?}", attr.file_type()), 43 | attr.inum(), 44 | attr.len() 45 | ); 46 | } 47 | } 48 | } 49 | Ok(()) 50 | } 51 | -------------------------------------------------------------------------------- /src/kernel/plic.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | memlayout::{PLIC_PRIORITY, PLIC_SCLAIM, PLIC_SENABLE, PLIC_SPRIORITY, UART0_IRQ, VIRTIO0_IRQ}, 3 | proc::Cpus, 4 | }; 5 | 6 | // the riscv Platform Level Interrupt Controller (PLIC). 7 | 8 | pub fn init() { 9 | let priority = PLIC_PRIORITY as *mut u32; 10 | unsafe { 11 | priority.add(UART0_IRQ as usize).write_volatile(1); 12 | priority.add(VIRTIO0_IRQ as usize).write_volatile(1); 13 | } 14 | } 15 | 16 | pub fn inithart() { 17 | unsafe { 18 | let hart = Cpus::cpu_id(); 19 | 20 | // set uart's enable bit for this hart's S-mode. 21 | let senable = PLIC_SENABLE(hart) as *mut u32; 22 | senable.write_volatile((1 << UART0_IRQ) | (1 << VIRTIO0_IRQ)); 23 | 24 | // set this hart's S-mode priority threshold to 0. 25 | let spriority = PLIC_SPRIORITY(hart) as *mut u32; 26 | spriority.write_volatile(0); 27 | } 28 | } 29 | 30 | // ask the PLIC what interrupt we shold serve. 31 | pub fn claim() -> Option { 32 | unsafe { 33 | let hart = Cpus::cpu_id(); 34 | let reg = PLIC_SCLAIM(hart) as *mut u32; 35 | let irq = reg.read_volatile(); 36 | if irq == 0 { 37 | None 38 | } else { 39 | Some(irq) 40 | } 41 | } 42 | } 43 | 44 | // tell the PLIC we've served this IRQ. 45 | pub fn complete(irq: u32) { 46 | unsafe { 47 | let hart = Cpus::cpu_id(); 48 | let reg = PLIC_SCLAIM(hart) as *mut u32; 49 | reg.write_volatile(irq); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/user/Cargo.toml: -------------------------------------------------------------------------------- 1 | cargo-features = ["per-package-target"] 2 | 3 | [package] 4 | name = "uprogs" 5 | description = "octox user programs" 6 | authors = ["Hayato Ohhashi "] 7 | edition = "2021" 8 | version.workspace = true 9 | license.workspace = true 10 | repository.workspace = true 11 | forced-target = "riscv64gc-unknown-none-elf" 12 | 13 | [lib] 14 | name = "ulib" 15 | path = "lib/lib.rs" 16 | 17 | [[bin]] 18 | name = "_cat" 19 | path = "bin/cat.rs" 20 | 21 | [[bin]] 22 | name = "_echo" 23 | path = "bin/echo.rs" 24 | 25 | [[bin]] 26 | name = "_grep" 27 | path = "bin/grep.rs" 28 | 29 | [[bin]] 30 | name = "_init" 31 | path = "bin/init.rs" 32 | 33 | [[bin]] 34 | name = "_initcode" 35 | path = "bin/initcode.rs" 36 | 37 | [[bin]] 38 | name = "_kill" 39 | path = "bin/kill.rs" 40 | 41 | [[bin]] 42 | name = "_ln" 43 | path = "bin/ln.rs" 44 | 45 | [[bin]] 46 | name = "_ls" 47 | path = "bin/ls.rs" 48 | 49 | [[bin]] 50 | name = "_mkdir" 51 | path = "bin/mkdir.rs" 52 | 53 | [[bin]] 54 | name = "_rm" 55 | path = "bin/rm.rs" 56 | 57 | [[bin]] 58 | name = "_sh" 59 | path = "bin/sh.rs" 60 | 61 | [[bin]] 62 | name = "_wc" 63 | path = "bin/wc.rs" 64 | 65 | [[bin]] 66 | name = "_head" 67 | path = "bin/head.rs" 68 | 69 | [[bin]] 70 | name = "_clear" 71 | path = "bin/clear.rs" 72 | 73 | [[bin]] 74 | name = "_sleep" 75 | path = "bin/sleep.rs" 76 | 77 | [[bin]] 78 | name = "_touch" 79 | path = "bin/touch.rs" 80 | 81 | [[bin]] 82 | name = "_jell" 83 | path = "bin/jell.rs" 84 | 85 | [dependencies] 86 | libkernel = { workspace = true } 87 | 88 | [build-dependencies] 89 | libkernel = { workspace = true } 90 | -------------------------------------------------------------------------------- /src/user/lib/mutex.rs: -------------------------------------------------------------------------------- 1 | // implement simple mutex as spinlock 2 | // later: we need to implement futex_mutex with implementing kernel futex. 3 | 4 | use core::{ 5 | cell::UnsafeCell, 6 | ops::{Deref, DerefMut}, 7 | sync::atomic::{AtomicBool, Ordering}, 8 | }; 9 | 10 | #[derive(Debug)] 11 | pub struct Mutex { 12 | locked: AtomicBool, 13 | value: UnsafeCell, 14 | } 15 | 16 | unsafe impl Sync for Mutex where T: Send {} 17 | 18 | impl Mutex { 19 | pub const fn new(value: T) -> Self { 20 | Self { 21 | locked: AtomicBool::new(false), 22 | value: UnsafeCell::new(value), 23 | } 24 | } 25 | pub fn lock(&self) -> MutexGuard { 26 | while self.locked.swap(true, Ordering::Acquire) { 27 | core::hint::spin_loop(); 28 | } 29 | MutexGuard { lock: self } 30 | } 31 | pub fn into_inner(self) -> T { 32 | self.value.into_inner() 33 | } 34 | 35 | #[allow(clippy::mut_from_ref)] 36 | pub unsafe fn get_mut(&self) -> &mut T { 37 | &mut *self.value.get() 38 | } 39 | } 40 | 41 | pub struct MutexGuard<'a, T> { 42 | lock: &'a Mutex, 43 | } 44 | 45 | impl Deref for MutexGuard<'_, T> { 46 | type Target = T; 47 | fn deref(&self) -> &Self::Target { 48 | unsafe { &*self.lock.value.get() } 49 | } 50 | } 51 | 52 | impl DerefMut for MutexGuard<'_, T> { 53 | fn deref_mut(&mut self) -> &mut Self::Target { 54 | unsafe { &mut *self.lock.value.get() } 55 | } 56 | } 57 | 58 | impl Drop for MutexGuard<'_, T> { 59 | fn drop(&mut self) { 60 | self.lock.locked.store(false, Ordering::Release) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/user/bin/head.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | extern crate alloc; 3 | 4 | use alloc::{string::String, vec}; 5 | use ulib::{ 6 | env, 7 | fs::File, 8 | io::{BufReader, Read, Write}, 9 | print, 10 | stdio::{stdin, stdout}, 11 | sys, 12 | }; 13 | 14 | fn main() { 15 | let mut args = env::args().skip(1).peekable(); 16 | let mut config = (Some(10), None); 17 | 18 | while let Some(&arg) = args.peek() { 19 | match arg { 20 | "-n" => { 21 | let _ = args.next(); 22 | config = (args.next().map(|s| s.parse().unwrap()), None); 23 | } 24 | "-c" => { 25 | let _ = args.next(); 26 | config = (None, args.next().map(|s| s.parse().unwrap())); 27 | } 28 | s if s.contains('-') => panic!("invalid argument"), 29 | _ => break, 30 | } 31 | } 32 | 33 | if let Some(file) = args.next() { 34 | let buf = BufReader::new(File::open(file).unwrap()); 35 | head(buf, config).unwrap(); 36 | } else { 37 | let buf = BufReader::new(stdin()); 38 | head(buf, config).unwrap(); 39 | } 40 | } 41 | 42 | fn head( 43 | mut reader: BufReader, 44 | config: (Option, Option), 45 | ) -> sys::Result<()> { 46 | match config { 47 | (Some(n), None) => { 48 | let mut buf = String::new(); 49 | for _ in 0..n { 50 | reader.read_line(&mut buf)?; 51 | } 52 | print!("{}", buf); 53 | Ok(()) 54 | } 55 | (None, Some(c)) => { 56 | let mut buf = vec![0u8; c]; 57 | reader.read(&mut buf)?; 58 | stdout().write(&buf)?; 59 | Ok(()) 60 | } 61 | _ => unreachable!(), 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/kernel/main.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![no_main] 3 | 4 | extern crate alloc; 5 | 6 | use core::sync::atomic::{AtomicBool, Ordering}; 7 | use kernel::{ 8 | bio, console, kalloc, kmain, null, plic, println, 9 | proc::{self, scheduler, user_init, Cpus}, 10 | trap, virtio_disk, vm, 11 | }; 12 | 13 | static STARTED: AtomicBool = AtomicBool::new(false); 14 | 15 | kmain!(main); 16 | 17 | extern "C" fn main() -> ! { 18 | let cpuid = unsafe { Cpus::cpu_id() }; 19 | if cpuid == 0 { 20 | let initcode = include_bytes!(concat!(env!("OUT_DIR"), "/bin/_initcode")); 21 | console::init(); // console init 22 | println!(""); 23 | println!("octox kernel is booting"); 24 | println!(""); 25 | null::init(); // null device init 26 | kalloc::init(); // physical memory allocator 27 | vm::kinit(); // create kernel page table 28 | vm::kinithart(); // turn on paging 29 | proc::init(); // process table 30 | trap::inithart(); // install kernel trap vector 31 | plic::init(); // set up interrupt controller 32 | plic::inithart(); // ask PLIC for device interrupts 33 | bio::init(); // buffer cache 34 | virtio_disk::init(); // emulated hard disk 35 | user_init(initcode); 36 | STARTED.store(true, Ordering::SeqCst); 37 | } else { 38 | while !STARTED.load(Ordering::SeqCst) { 39 | core::hint::spin_loop() 40 | } 41 | println!("hart {} starting", unsafe { Cpus::cpu_id() }); 42 | vm::kinithart(); // turn on paging 43 | trap::inithart(); // install kernel trap vector 44 | plic::inithart(); // ask PLIC for device interrupts 45 | } 46 | scheduler() 47 | } 48 | 49 | #[panic_handler] 50 | fn panic(info: &core::panic::PanicInfo<'_>) -> ! { 51 | use kernel::printf::panic_inner; 52 | panic_inner(info) 53 | } 54 | -------------------------------------------------------------------------------- /src/kernel/pipe.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | error::{Error::*, Result}, 3 | fcntl::OMode, 4 | file::{FType, File, FTABLE}, 5 | mpmc::*, 6 | proc::{either_copyin, either_copyout}, 7 | vm::VirtAddr, 8 | }; 9 | 10 | #[derive(Debug)] 11 | pub struct Pipe { 12 | rx: Option>, 13 | tx: Option>, 14 | } 15 | 16 | impl Pipe { 17 | const PIPESIZE: isize = 512; 18 | 19 | pub fn new(rx: Option>, tx: Option>) -> Self { 20 | Self { rx, tx } 21 | } 22 | 23 | pub fn get_mode(&self) -> OMode { 24 | let mut omode = OMode::new(); 25 | omode.read(self.rx.is_some()).write(self.tx.is_some()); 26 | omode 27 | } 28 | 29 | pub fn alloc() -> Result<(File, File)> { 30 | let (tx, rx) = sync_channel::(Self::PIPESIZE, "pipe"); 31 | 32 | let p0 = Self::new(Some(rx), None); 33 | let p1 = Self::new(None, Some(tx)); 34 | let f0 = FTABLE.alloc(p0.get_mode(), FType::Pipe(p0))?; 35 | let f1 = FTABLE.alloc(p1.get_mode(), FType::Pipe(p1))?; 36 | 37 | Ok((f0, f1)) 38 | } 39 | 40 | pub fn write(&self, mut src: VirtAddr, n: usize) -> Result { 41 | let tx = self.tx.as_ref().ok_or(BrokenPipe)?; 42 | 43 | let mut i = 0; 44 | while i < n { 45 | let mut ch: u8 = 0; 46 | either_copyin(&mut ch, src)?; 47 | let Ok(_) = tx.send(ch) else { 48 | break; 49 | }; 50 | src += 1; 51 | i += 1; 52 | } 53 | Ok(i) 54 | } 55 | 56 | pub fn read(&self, mut dst: VirtAddr, n: usize) -> Result { 57 | let rx = self.rx.as_ref().ok_or(BrokenPipe)?; 58 | 59 | let mut i = 0; 60 | while i < n { 61 | let Ok(ch) = rx.recv() else { 62 | break; 63 | }; 64 | either_copyout(dst, &ch)?; 65 | dst += 1; 66 | i += 1; 67 | } 68 | Ok(i) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/user/build.rs: -------------------------------------------------------------------------------- 1 | use kernel::syscall::*; 2 | use std::{ 3 | fs::{self, File}, 4 | io::{self, Write}, 5 | path::{Path, PathBuf}, 6 | }; 7 | 8 | fn main() { 9 | let root_out_dir = PathBuf::from(std::env::var("ROOT_OUT_DIR").unwrap()); 10 | 11 | // copy etc/_* to root_out_dir/etc 12 | let src_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("etc"); 13 | let dst_dir = root_out_dir.join("etc"); 14 | copy_files(&src_dir, &dst_dir, Some("_")).expect("failed to copy user etc"); 15 | 16 | // copy lib/_* to root_out_dir/lib 17 | let src_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("lib"); 18 | let dst_dir = root_out_dir.join("lib"); 19 | copy_files(&src_dir, &dst_dir, Some("_")).expect("failed to copy user etc"); 20 | 21 | // build syscall interface file usys.rs 22 | let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); 23 | let mut usys_rs = 24 | File::create(out_dir.join("usys.rs")).expect("cloudn't create OUT_DIR/usys.rs"); 25 | usys_rs 26 | .write_all("// Created by build.rs\n\n".as_bytes()) 27 | .expect("OUT_DIR/usys.rs: write error"); 28 | for syscall_id in SysCalls::into_enum_iter().skip(1) { 29 | usys_rs 30 | .write_all(syscall_id.gen_usys().as_bytes()) 31 | .expect("usys write error"); 32 | } 33 | 34 | // set linker script 35 | let local_path = Path::new(env!("CARGO_MANIFEST_DIR")); 36 | println!( 37 | "cargo:rustc-link-arg=-T{}", 38 | local_path.join("user.ld").display() 39 | ); 40 | } 41 | 42 | fn copy_files(src_dir: &Path, dst_dir: &Path, prefix: Option<&str>) -> io::Result<()> { 43 | if !src_dir.exists() { 44 | // nothing to do 45 | return Ok(()); 46 | } 47 | if !dst_dir.exists() { 48 | fs::create_dir_all(dst_dir)?; 49 | } 50 | for entry in fs::read_dir(src_dir)? { 51 | let entry = entry?; 52 | let entry_path = entry.path(); 53 | let dst_path = dst_dir.join(entry.file_name()); 54 | if entry_path.is_dir() { 55 | todo!() 56 | } else { 57 | let should_copy = match (prefix, entry_path.file_name().and_then(|s| s.to_str())) { 58 | (Some(prefix), Some(name)) if name.starts_with(prefix) => true, 59 | (None, Some(_)) => true, 60 | _ => false, 61 | }; 62 | if should_copy { 63 | fs::copy(&entry_path, &dst_path)?; 64 | } 65 | } 66 | } 67 | Ok(()) 68 | } 69 | -------------------------------------------------------------------------------- /src/kernel/printf.rs: -------------------------------------------------------------------------------- 1 | use crate::console; 2 | use crate::spinlock::Mutex; 3 | use core::fmt; 4 | use core::panic; 5 | use core::sync::atomic::{AtomicBool, Ordering}; 6 | 7 | pub static PR: Pr = Pr { 8 | writer: Mutex::new(Writer, "pr"), 9 | panicked: AtomicBool::new(false), 10 | locking: AtomicBool::new(true), 11 | }; 12 | 13 | // lock to avoid interleaving concurrent println!'s. 14 | // Pr struct is slightly different, i.e., 15 | // it is not wrapped in a Mutex 16 | // Because we need another field(locking), 17 | // This trick can make `panic` print something to the console quicker. 18 | pub struct Pr { 19 | writer: Mutex, 20 | panicked: AtomicBool, 21 | locking: AtomicBool, 22 | } 23 | 24 | impl Pr { 25 | pub fn panicked(&self) -> &AtomicBool { 26 | &self.panicked 27 | } 28 | } 29 | 30 | pub struct Writer; 31 | 32 | impl Writer { 33 | fn print(&self, c: u8) { 34 | console::putc(c) 35 | } 36 | } 37 | 38 | impl fmt::Write for Writer { 39 | fn write_str(&mut self, s: &str) -> fmt::Result { 40 | for byte in s.bytes() { 41 | self.print(byte); 42 | } 43 | Ok(()) 44 | } 45 | } 46 | 47 | pub fn _print(args: fmt::Arguments<'_>) { 48 | use fmt::Write; 49 | 50 | if PR.locking.load(Ordering::Relaxed) { 51 | PR.writer.lock().write_fmt(args).expect("_print: error"); 52 | } else { 53 | // for panic! 54 | unsafe { 55 | PR.writer.get_mut().write_fmt(args).expect("_print: error"); 56 | } 57 | } 58 | } 59 | 60 | #[macro_export] 61 | macro_rules! print { 62 | ($($arg:tt)*) => { 63 | $crate::printf::_print(format_args!($($arg)*)) 64 | }; 65 | } 66 | #[macro_export] 67 | macro_rules! println { 68 | ($fmt:literal$(, $($arg: tt)+)?) => { 69 | $crate::printf::_print(format_args!(concat!($fmt, "\n") $(,$($arg)+)?)) 70 | }; 71 | } 72 | 73 | pub static mut EWRITER: Writer = Writer; 74 | 75 | #[allow(static_mut_refs)] 76 | pub fn _eprint(args: fmt::Arguments<'_>) { 77 | use fmt::Write; 78 | unsafe { 79 | EWRITER.write_fmt(args).expect("_print: error"); 80 | } 81 | } 82 | 83 | #[macro_export] 84 | macro_rules! eprint { 85 | ($($arg:tt)*) => { 86 | $crate::printf::_eprint(format_args!($($arg)*)) 87 | }; 88 | } 89 | 90 | #[allow(clippy::empty_loop)] 91 | pub fn panic_inner(info: &panic::PanicInfo<'_>) -> ! { 92 | PR.locking.store(false, Ordering::Relaxed); 93 | crate::println!("core {}: {}", unsafe { crate::proc::Cpus::cpu_id() }, info); 94 | PR.panicked.store(true, Ordering::Relaxed); 95 | loop {} 96 | } 97 | -------------------------------------------------------------------------------- /src/user/lib/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![feature(lang_items, never_type, allocator_api, alloc_error_handler)] 3 | #![allow(clippy::missing_safety_doc)] 4 | #![allow(internal_features)] 5 | 6 | pub mod sys { 7 | use core::arch::asm; 8 | use fcntl::FcntlCmd; 9 | pub use kernel::defs; 10 | pub use kernel::error::Error; 11 | pub use kernel::error::Result; 12 | pub use kernel::fcntl; 13 | pub use kernel::file::Major; 14 | pub use kernel::fs; 15 | pub use kernel::stat; 16 | pub use kernel::sync; 17 | use stat::Stat; 18 | include!(concat!(env!("OUT_DIR"), "/usys.rs")); 19 | } 20 | pub extern crate alloc; 21 | #[macro_use] 22 | pub mod stdio; 23 | pub mod env; 24 | pub mod fs; 25 | pub mod io; 26 | pub mod mutex; 27 | pub mod path; 28 | pub mod pipe; 29 | pub mod process; 30 | pub mod umalloc; 31 | //pub mod regex; 32 | 33 | use crate::env::ARGS; 34 | use core::panic; 35 | use env::ENVIRON; 36 | use io::Write; 37 | 38 | // wrapper so that it's ok if main() does not call exit(). 39 | #[lang = "start"] 40 | fn lang_start( 41 | main: fn() -> T, 42 | argc: isize, 43 | argv: *const *const u8, 44 | _: u8, 45 | ) -> isize { 46 | unsafe { 47 | if let Some(argv) = (argv as *mut &mut [Option<&str>]).as_mut() { 48 | let (args, environ) = argv.split_at_mut(argc as usize); 49 | ARGS = Some(args); 50 | ENVIRON = Some(environ); 51 | } 52 | } 53 | let xstatus = main().report() as i32; 54 | sys::exit(xstatus) 55 | } 56 | 57 | pub enum ExitCode { 58 | SUCCESS = 0x0isize, 59 | FAILURE = 0x1isize, 60 | } 61 | 62 | #[lang = "termination"] 63 | pub trait Termination { 64 | fn report(self) -> ExitCode; 65 | } 66 | 67 | impl Termination for () { 68 | fn report(self) -> ExitCode { 69 | ExitCode::SUCCESS 70 | } 71 | } 72 | 73 | impl Termination for ! { 74 | fn report(self) -> ExitCode { 75 | self 76 | } 77 | } 78 | 79 | impl Termination for ExitCode { 80 | #[inline] 81 | fn report(self) -> ExitCode { 82 | self 83 | } 84 | } 85 | 86 | impl Termination for core::result::Result { 87 | fn report(self) -> ExitCode { 88 | match self { 89 | Ok(val) => val.report(), 90 | Err(_) => ExitCode::FAILURE, 91 | } 92 | } 93 | } 94 | 95 | #[panic_handler] 96 | fn panic(_info: &panic::PanicInfo<'_>) -> ! { 97 | if let Some(out) = crate::stdio::panic_output() { 98 | let _ = out.write_fmt(format_args!("{}\n", _info)); 99 | } 100 | sys::exit(-1) 101 | } 102 | -------------------------------------------------------------------------------- /src/kernel/start.rs: -------------------------------------------------------------------------------- 1 | use crate::kernelvec::*; 2 | use crate::memlayout::*; 3 | use crate::param::NCPU; 4 | use crate::riscv::registers::{pmpcfg0::*, *}; 5 | use core::arch::asm; 6 | use core::hint::unreachable_unchecked; 7 | 8 | #[repr(C, align(16))] 9 | struct Stack([u8; 4096 * STACK_PAGE_NUM * NCPU]); 10 | 11 | #[no_mangle] 12 | static mut STACK0: Stack = Stack([0; 4096 * STACK_PAGE_NUM * NCPU]); 13 | 14 | pub unsafe fn start() -> ! { 15 | // set MPP mode to Supervisor, for mret 16 | mstatus::set_mpp(mstatus::MPP::Supervisor); 17 | 18 | // set MEPC to main, for mret 19 | mepc::write(main as usize); 20 | 21 | // disable paging for now. 22 | satp::write(0); 23 | 24 | // delegate all interrupts and exceptions to supervisor mode. 25 | medeleg::set_all(); 26 | mideleg::set_all(); 27 | sie::set_sext(); 28 | sie::set_ssoft(); 29 | sie::set_stimer(); 30 | 31 | // configure Physical Memory Protection to give supervisor mode 32 | // access to all of physical memory. 33 | pmpaddr0::write(0x3fffffffffffff); 34 | pmpcfg0::set_pmp(0, Range::TOR, Permission::RWX, false); // 0 < addr < pmpaddr0 35 | 36 | // ask for clock interrupts. 37 | timerinit(); 38 | 39 | // keep each CPU's hartid in its tp register, for cpuid(). 40 | let id = mhartid::read(); 41 | asm!("mv tp, {0}", in(reg) id); 42 | 43 | // switch to supervisor mode and jump to main(). 44 | asm!("mret"); 45 | 46 | extern "C" { 47 | fn main() -> !; 48 | } 49 | unreachable_unchecked(); 50 | } 51 | 52 | // a scratch area per CPU for machine-mode timer interrupts. 53 | static mut TIMER_SCRATCH: [[u64; 5]; NCPU] = [[0; 5]; NCPU]; 54 | 55 | unsafe fn timerinit() { 56 | // each CPU has a separate source of timer interrupts 57 | let id = mhartid::read(); 58 | 59 | // ask the CLINT for a timer interrupts 60 | let interval = 1000000u64; // cycles; about 1/10th second in qemu. 61 | let mtimecmp = clint_mtimecmp(id) as *mut u64; 62 | let mtime = CLINT_MTIME as *const u64; 63 | mtimecmp.write_volatile(mtime.read_volatile() + interval); 64 | 65 | // prepare information in scratch[] for timervec. 66 | // scratch[0..2] : space for timervec to save registers. 67 | // scratch[3] : address of CLINT MTIMECMP register. 68 | // scratch[4] : desired interval (in cycles) between timer interrupts. 69 | let scratch = &mut TIMER_SCRATCH[id]; 70 | scratch[3] = mtimecmp as u64; 71 | scratch[4] = interval; 72 | mscratch::write(scratch.as_mut_ptr() as usize); 73 | 74 | // set the machine-mode trap handler 75 | mtvec::write(timervec as usize, mtvec::TrapMode::Direct); 76 | 77 | // enable machine-mode interrupts. 78 | mstatus::set_mie(); 79 | 80 | // enable machime-mode timer interrupts. 81 | mie::set_mtimer(); 82 | } 83 | -------------------------------------------------------------------------------- /src/kernel/memlayout.rs: -------------------------------------------------------------------------------- 1 | // Physical memory layout 2 | use crate::riscv::PGSIZE; 3 | use crate::vm::{KVAddr, VAddr}; 4 | 5 | // qemu -machine virt is set up like this, 6 | // based on qemu's hw/riscv/virt.c: 7 | // 8 | // 00001000 -- boot ROM, provided by qemu 9 | // 02000000 -- CLINT 10 | // 0C000000 -- PLIC 11 | // 10000000 -- uart0 12 | // 10001000 -- virtio disk 13 | // 80000000 -- boot ROM jumps here in machine mode 14 | // -kernel loads the kernel here 15 | // unused RAM after 80000000. 16 | 17 | // the kernel uses physical memory thus: 18 | // 80000000 -- entry.S, then kernel text and data 19 | // end -- start of kernel page allocation area 20 | // PHYSTOP -- end RAM used by the kernel 21 | 22 | // qemu puts UART registers here in physical memory. 23 | pub const UART0: usize = 0x1000_0000; 24 | pub const UART0_IRQ: u32 = 10; 25 | 26 | // virtio mmio interface 27 | pub const VIRTIO0: usize = 0x1000_1000; 28 | pub const VIRTIO0_IRQ: u32 = 1; 29 | 30 | // core local interrupter (CLINT), which contains the timer 31 | pub const CLINT: usize = 0x2000000; 32 | pub const fn clint_mtimecmp(hartid: usize) -> usize { 33 | CLINT + 0x4000 + 8 * hartid 34 | } 35 | pub const CLINT_MTIME: usize = CLINT + 0xBFF8; // Cycles since boot. 36 | 37 | // qemu puts platform-level interrupt controller (PLIC) here. 38 | pub const PLIC: usize = 0x0C00_0000; 39 | pub const PLIC_PRIORITY: usize = PLIC; 40 | pub const PLIC_PENDING: usize = PLIC + 0x1000; 41 | #[allow(non_snake_case)] 42 | pub const fn PLIC_SENABLE(hart: usize) -> usize { 43 | PLIC + 0x2080 + hart * 0x100 44 | } 45 | #[allow(non_snake_case)] 46 | pub const fn PLIC_SPRIORITY(hart: usize) -> usize { 47 | PLIC + 0x201000 + hart * 0x2000 48 | } 49 | #[allow(non_snake_case)] 50 | pub const fn PLIC_SCLAIM(hart: usize) -> usize { 51 | PLIC + 0x201004 + hart * 0x2000 52 | } 53 | 54 | // the kernel expects there to be RAM 55 | // for use by the kernel and user psges 56 | // from physical address 0x80000000 to PHYSTOP. 57 | pub const KERNBASE: usize = 0x8000_0000; 58 | pub const PHYSTOP: usize = KERNBASE + 512 * 1024 * 1024; 59 | 60 | // map the trampoline page to the highest address, 61 | // in both user and kernel space. 62 | pub const TRAMPOLINE: usize = KVAddr::MAXVA - PGSIZE; 63 | 64 | // num of stack pages. 65 | pub const STACK_PAGE_NUM: usize = 25; 66 | // map kernel stacks beneath the trampoline, 67 | // each surrounded by invalid guard pages. 68 | pub const fn kstack(p: usize) -> KVAddr { 69 | KVAddr::new(TRAMPOLINE - ((p + 1) * (STACK_PAGE_NUM + 1) * PGSIZE)) 70 | } 71 | 72 | // User memory layout. 73 | // Address zero first: 74 | // text 75 | // original data and bss 76 | // fixed-size stack 77 | // expandable heap 78 | // ... 79 | // TRAPFRAME (p->trampframe, used by trampoline) 80 | // TRAMPOLINE (the same page as in the kernel) 81 | pub const TRAPFRAME: usize = TRAMPOLINE - PGSIZE; 82 | -------------------------------------------------------------------------------- /src/kernel/sleeplock.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | proc::{sleep, wakeup, Cpus}, 3 | spinlock::Mutex, 4 | }; 5 | use core::{ 6 | cell::UnsafeCell, 7 | ops::{Deref, DerefMut}, 8 | }; 9 | 10 | // Sleeping locks 11 | 12 | // Long-term locks for processes 13 | #[derive(Debug)] 14 | pub struct SleepLock { 15 | lk: Mutex, // spinlock protecting this sleep lock 16 | data: UnsafeCell, 17 | name: &'static str, // Name of lock 18 | } 19 | unsafe impl Sync for SleepLock {} 20 | unsafe impl Send for SleepLock {} 21 | 22 | #[derive(Debug)] 23 | struct SleepLockInfo { 24 | locked: bool, 25 | pid: usize, 26 | } 27 | 28 | #[derive(Debug)] 29 | pub struct SleepLockGuard<'a, T> { 30 | sleep_lock: &'a SleepLock, 31 | } 32 | 33 | impl SleepLockInfo { 34 | pub const fn new(locked: bool, pid: usize) -> Self { 35 | SleepLockInfo { locked, pid } 36 | } 37 | } 38 | 39 | impl SleepLock { 40 | pub const fn new(data: T, name: &'static str) -> Self { 41 | Self { 42 | lk: Mutex::new(SleepLockInfo::new(false, 0), name), 43 | data: UnsafeCell::new(data), 44 | name, 45 | } 46 | } 47 | 48 | pub fn lock(&self) -> SleepLockGuard<'_, T> { 49 | let mut lk = self.lk.lock(); 50 | let p = Cpus::myproc().unwrap(); 51 | while lk.locked { 52 | lk = sleep(self as *const _ as usize, lk); 53 | } 54 | lk.locked = true; 55 | lk.pid = p.pid(); 56 | SleepLockGuard { sleep_lock: self } 57 | } 58 | 59 | pub fn holding(&self) -> bool { 60 | let lk = self.lk.lock(); 61 | lk.locked && lk.pid == Cpus::myproc().unwrap().pid() 62 | } 63 | 64 | pub fn unlock(guard: SleepLockGuard<'_, T>) -> &'_ SleepLock { 65 | guard.sleep_lock() 66 | } 67 | } 68 | 69 | impl<'a, T: 'a> SleepLockGuard<'a, T> { 70 | // Returns a reference to the original 'Mutex' object. 71 | pub fn sleep_lock(&self) -> &'a SleepLock { 72 | self.sleep_lock 73 | } 74 | 75 | pub fn holding(&self) -> bool { 76 | self.sleep_lock.holding() 77 | } 78 | } 79 | impl<'a, T: 'a> Deref for SleepLockGuard<'a, T> { 80 | type Target = T; 81 | fn deref(&self) -> &Self::Target { 82 | unsafe { &*self.sleep_lock.data.get() } 83 | } 84 | } 85 | 86 | impl<'a, T: 'a> DerefMut for SleepLockGuard<'a, T> { 87 | fn deref_mut(&mut self) -> &mut T { 88 | unsafe { &mut *self.sleep_lock.data.get() } 89 | } 90 | } 91 | 92 | impl<'a, T: 'a> Drop for SleepLockGuard<'a, T> { 93 | fn drop(&mut self) { 94 | assert!( 95 | self.sleep_lock.holding(), 96 | "release {}", 97 | self.sleep_lock.name 98 | ); 99 | let mut lk = self.sleep_lock.lk.lock(); 100 | lk.locked = false; 101 | lk.pid = 0; 102 | wakeup(self.sleep_lock as *const _ as usize); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/kernel/fcntl.rs: -------------------------------------------------------------------------------- 1 | pub mod omode { 2 | pub const RDONLY: usize = 0x000; 3 | pub const WRONLY: usize = 0x001; 4 | pub const RDWR: usize = 0x002; 5 | pub const CREATE: usize = 0x200; 6 | pub const TRUNC: usize = 0x400; 7 | pub const APPEND: usize = 0x800; 8 | pub const CLOEXEC: usize = 0x1000; 9 | } 10 | 11 | pub struct OMode { 12 | read: bool, 13 | write: bool, 14 | truncate: bool, 15 | create: bool, 16 | append: bool, 17 | cloexec: bool, 18 | } 19 | 20 | impl Default for OMode { 21 | fn default() -> Self { 22 | Self::new() 23 | } 24 | } 25 | 26 | impl OMode { 27 | pub fn new() -> Self { 28 | Self { 29 | read: false, 30 | write: false, 31 | truncate: false, 32 | create: false, 33 | append: false, 34 | cloexec: false, 35 | } 36 | } 37 | 38 | pub fn read(&mut self, read: bool) -> &mut Self { 39 | self.read = read; 40 | self 41 | } 42 | pub fn write(&mut self, write: bool) -> &mut Self { 43 | self.write = write; 44 | self 45 | } 46 | pub fn append(&mut self, append: bool) -> &mut Self { 47 | self.append = append; 48 | self 49 | } 50 | pub fn cloexec(&mut self, cloexec: bool) -> &mut Self { 51 | self.cloexec = cloexec; 52 | self 53 | } 54 | fn truncate(&mut self, truncate: bool) -> &mut Self { 55 | self.truncate = truncate; 56 | self 57 | } 58 | fn create(&mut self, create: bool) -> &mut Self { 59 | self.create = create; 60 | self 61 | } 62 | 63 | pub fn from_usize(bits: usize) -> Self { 64 | let mut mode = Self::new(); 65 | mode.read(bits & omode::WRONLY == 0) 66 | .write(bits & omode::WRONLY != 0 || bits & omode::RDWR != 0) 67 | .create(bits & omode::CREATE != 0) 68 | .truncate(bits & omode::TRUNC != 0) 69 | .append(bits & omode::APPEND != 0) 70 | .cloexec(bits & omode::CLOEXEC != 0); 71 | mode 72 | } 73 | 74 | pub fn is_read(&self) -> bool { 75 | self.read 76 | } 77 | 78 | pub fn is_write(&self) -> bool { 79 | self.write 80 | } 81 | 82 | pub fn is_create(&self) -> bool { 83 | self.create 84 | } 85 | 86 | pub fn is_trunc(&self) -> bool { 87 | self.truncate 88 | } 89 | 90 | pub fn is_rdonly(&self) -> bool { 91 | self.read && !self.write 92 | } 93 | 94 | pub fn is_cloexec(&self) -> bool { 95 | self.cloexec 96 | } 97 | 98 | pub fn is_append(&self) -> bool { 99 | self.append 100 | } 101 | } 102 | 103 | #[repr(usize)] 104 | pub enum FcntlCmd { 105 | SetCloexec = 1, 106 | Invalid, 107 | } 108 | 109 | impl FcntlCmd { 110 | pub fn from_usize(bits: usize) -> Self { 111 | match bits { 112 | 1 => Self::SetCloexec, 113 | _ => Self::Invalid, 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/kernel/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(target_os = "none", no_std)] 2 | #![cfg_attr( 3 | all(target_os = "none", feature = "kernel"), 4 | feature(alloc_error_handler) 5 | )] 6 | #![cfg_attr(all(target_os = "none", feature = "kernel"), feature(allocator_api))] 7 | #![feature(negative_impls)] 8 | #![feature(fn_align)] 9 | #![feature(variant_count)] 10 | #![allow(clippy::missing_safety_doc)] 11 | 12 | #[cfg(all(target_os = "none", feature = "kernel"))] 13 | extern crate alloc; 14 | 15 | #[cfg(all(target_os = "none", feature = "kernel"))] 16 | pub mod condvar; 17 | #[cfg(all(target_os = "none", feature = "kernel"))] 18 | pub mod console; 19 | #[cfg(all(target_os = "none", feature = "kernel"))] 20 | pub mod entry; 21 | #[cfg(all(target_os = "none", feature = "kernel"))] 22 | pub mod kernelvec; 23 | #[cfg(all(target_os = "none", feature = "kernel"))] 24 | pub mod memlayout; 25 | #[cfg(all(target_os = "none", feature = "kernel"))] 26 | pub mod mpmc; 27 | #[cfg(all(target_os = "none", feature = "kernel"))] 28 | pub mod null; 29 | pub mod param; 30 | #[cfg(all(target_os = "none", feature = "kernel"))] 31 | pub mod proc; 32 | #[cfg(all(target_os = "none", feature = "kernel"))] 33 | pub mod semaphore; 34 | #[cfg(all(target_os = "none", feature = "kernel"))] 35 | pub mod sleeplock; 36 | #[cfg(all(target_os = "none", feature = "kernel"))] 37 | pub mod spinlock; 38 | #[cfg(all(target_os = "none", feature = "kernel"))] 39 | pub mod start; 40 | #[cfg(all(target_os = "none", feature = "kernel"))] 41 | pub mod uart; 42 | #[cfg(all(target_os = "none", feature = "kernel"))] 43 | #[macro_use] 44 | pub mod printf; 45 | #[cfg(all(target_os = "none", feature = "kernel"))] 46 | pub mod bio; 47 | #[cfg(all(target_os = "none", feature = "kernel"))] 48 | pub mod buddy; 49 | pub mod defs; 50 | #[cfg(all(target_os = "none", feature = "kernel"))] 51 | pub mod elf; 52 | pub mod error; 53 | #[cfg(all(target_os = "none", feature = "kernel"))] 54 | pub mod exec; 55 | #[cfg(target_os = "none")] 56 | pub mod fcntl; 57 | pub mod file; 58 | pub mod fs; 59 | #[cfg(all(target_os = "none", feature = "kernel"))] 60 | pub mod kalloc; 61 | #[cfg(all(target_os = "none", feature = "kernel"))] 62 | pub mod list; 63 | #[cfg(all(target_os = "none", feature = "kernel"))] 64 | pub mod log; 65 | #[cfg(all(target_os = "none", feature = "kernel"))] 66 | pub mod pipe; 67 | #[cfg(all(target_os = "none", feature = "kernel"))] 68 | pub mod plic; 69 | #[cfg(all(target_os = "none", feature = "kernel"))] 70 | pub mod riscv; 71 | pub mod stat; 72 | #[cfg(all(target_os = "none", feature = "kernel"))] 73 | pub mod swtch; 74 | #[cfg(target_os = "none")] 75 | pub mod sync; 76 | pub mod syscall; 77 | #[cfg(all(target_os = "none", feature = "kernel"))] 78 | pub mod trampoline; 79 | #[cfg(all(target_os = "none", feature = "kernel"))] 80 | pub mod trap; 81 | #[cfg(all(target_os = "none", feature = "kernel"))] 82 | pub mod virtio_disk; 83 | #[cfg(all(target_os = "none", feature = "kernel"))] 84 | pub mod vm; 85 | 86 | #[macro_export] 87 | macro_rules! kmain { 88 | ($path:path) => { 89 | #[export_name = "main"] 90 | pub extern "C" fn __main() -> ! { 91 | // type check the given path 92 | let f: extern "C" fn() -> ! = $path; 93 | 94 | f() 95 | } 96 | }; 97 | } 98 | -------------------------------------------------------------------------------- /src/kernel/spinlock.rs: -------------------------------------------------------------------------------- 1 | use crate::riscv::intr_get; 2 | 3 | use super::proc::{Cpu, Cpus, IntrLock, CPUS}; 4 | use core::cell::UnsafeCell; 5 | use core::ops::{Deref, DerefMut, Drop}; 6 | use core::{ 7 | ptr, 8 | sync::atomic::{AtomicPtr, Ordering}, 9 | }; 10 | 11 | #[derive(Debug)] 12 | pub struct Mutex { 13 | name: &'static str, // Name of lock 14 | locked: AtomicPtr, // Is the lock held? 15 | data: UnsafeCell, // actual data 16 | } 17 | 18 | #[derive(Debug)] 19 | pub struct MutexGuard<'a, T: 'a> { 20 | mutex: &'a Mutex, 21 | _intr_lock: IntrLock, 22 | } 23 | 24 | impl Mutex { 25 | pub const fn new(value: T, name: &'static str) -> Mutex { 26 | Mutex { 27 | locked: AtomicPtr::new(ptr::null_mut()), 28 | data: UnsafeCell::new(value), 29 | name, 30 | } 31 | } 32 | 33 | pub fn lock(&self) -> MutexGuard<'_, T> { 34 | let _intr_lock = Cpus::lock_mycpu(self.name); // disable interrupts to avoid deadlock. 35 | 36 | unsafe { 37 | assert!(!self.holding(), "acquire {}", self.name); 38 | 39 | loop { 40 | if self 41 | .locked 42 | .compare_exchange( 43 | ptr::null_mut(), 44 | CPUS.mycpu(), 45 | Ordering::Acquire, 46 | Ordering::Relaxed, 47 | ) 48 | .is_ok() 49 | { 50 | break MutexGuard { 51 | mutex: self, 52 | _intr_lock, 53 | }; 54 | } 55 | core::hint::spin_loop() 56 | } 57 | } 58 | } 59 | 60 | // Check whether this cpu is holding the lock. 61 | // Interrupts must be off. 62 | unsafe fn holding(&self) -> bool { 63 | self.locked.load(Ordering::Relaxed) == CPUS.mycpu() 64 | } 65 | 66 | pub fn unlock(guard: MutexGuard<'_, T>) -> &'_ Mutex { 67 | guard.mutex() 68 | } 69 | 70 | #[allow(clippy::mut_from_ref)] 71 | pub unsafe fn get_mut(&self) -> &mut T { 72 | &mut *self.data.get() 73 | } 74 | 75 | // It is only safe when used in functions such as fork_ret(), 76 | // where passing guards is difficult. 77 | pub unsafe fn force_unlock(&self) { 78 | assert!(self.holding(), "force unlock {}", self.name); 79 | self.locked.store(ptr::null_mut(), Ordering::Release); 80 | (&mut *CPUS.mycpu()).unlock() 81 | } 82 | } 83 | 84 | unsafe impl Sync for Mutex {} 85 | 86 | impl<'a, T: 'a> MutexGuard<'a, T> { 87 | // Returns a reference to the original 'Mutex' object. 88 | pub fn mutex(&self) -> &'a Mutex { 89 | self.mutex 90 | } 91 | 92 | pub fn holding(&self) -> bool { 93 | assert!(!intr_get(), "interrupts enabled"); 94 | unsafe { self.mutex.holding() } 95 | } 96 | } 97 | 98 | impl<'a, T: 'a> Drop for MutexGuard<'a, T> { 99 | fn drop(&mut self) { 100 | assert!(self.holding(), "release {}", self.mutex.name); 101 | self.mutex.locked.store(ptr::null_mut(), Ordering::Release); 102 | } 103 | } 104 | 105 | impl<'a, T: 'a> Deref for MutexGuard<'a, T> { 106 | type Target = T; 107 | fn deref(&self) -> &Self::Target { 108 | unsafe { &*self.mutex.data.get() } 109 | } 110 | } 111 | 112 | impl<'a, T: 'a> DerefMut for MutexGuard<'a, T> { 113 | fn deref_mut(&mut self) -> &mut Self::Target { 114 | unsafe { &mut *self.mutex.data.get() } 115 | } 116 | } 117 | 118 | unsafe impl Sync for MutexGuard<'_, T> {} 119 | -------------------------------------------------------------------------------- /src/user/lib/env.rs: -------------------------------------------------------------------------------- 1 | use crate::{path::Path, sys}; 2 | use alloc::{ 3 | boxed::Box, 4 | string::ToString, 5 | vec::{self, Vec}, 6 | }; 7 | 8 | pub static mut ARGS: Option<&[Option<&str>]> = None; 9 | pub static mut ENVIRON: Option<&mut [Option<&str>]> = None; 10 | 11 | pub fn args() -> Args { 12 | Args { 13 | iter: if let Some(args) = unsafe { ARGS } { 14 | args.iter() 15 | } else { 16 | [].iter() 17 | }, 18 | } 19 | } 20 | 21 | pub struct Args { 22 | iter: core::slice::Iter<'static, Option<&'static str>>, 23 | } 24 | 25 | impl Iterator for Args { 26 | type Item = &'static str; 27 | fn next(&mut self) -> Option { 28 | self.iter.next().copied().flatten() 29 | } 30 | } 31 | 32 | impl ExactSizeIterator for Args { 33 | fn len(&self) -> usize { 34 | self.iter.len() 35 | } 36 | } 37 | 38 | pub fn set_current_dir>(path: P) -> sys::Result<()> { 39 | sys::chdir(path.as_ref().as_ref()) 40 | } 41 | 42 | pub struct Vars { 43 | iter: vec::IntoIter<(&'static str, &'static str)>, 44 | } 45 | 46 | pub fn vars() -> Vars { 47 | unsafe { 48 | let mut result = Vec::new(); 49 | if let Some(ref environ) = ENVIRON { 50 | for key_value in environ.iter().filter_map(|&e| e) { 51 | if let Some(key_value) = key_value.split_once('=') { 52 | result.push(key_value); 53 | } 54 | } 55 | } 56 | Vars { 57 | iter: result.into_iter(), 58 | } 59 | } 60 | } 61 | 62 | impl Iterator for Vars { 63 | type Item = (&'static str, &'static str); 64 | fn next(&mut self) -> Option { 65 | self.iter.next() 66 | } 67 | fn size_hint(&self) -> (usize, Option) { 68 | self.iter.size_hint() 69 | } 70 | } 71 | pub enum VarError { 72 | NotPresent, 73 | } 74 | 75 | pub fn var(key: &str) -> Result<&'static str, VarError> { 76 | vars() 77 | .find_map(|(k, v)| if k == key { Some(v) } else { None }) 78 | .ok_or(VarError::NotPresent) 79 | } 80 | 81 | pub fn set_var(key: &str, value: &str) -> sys::Result<()> { 82 | let mut new_key_value = key.to_string(); 83 | new_key_value.push('='); 84 | new_key_value.push_str(value); 85 | let new_key_value: &'static str = Box::leak(Box::new(new_key_value)); 86 | 87 | unsafe { 88 | if let Some(ref mut environ) = ENVIRON { 89 | for key_val in environ.iter_mut() { 90 | match key_val { 91 | Some(existing_key_val) if existing_key_val.starts_with(key) => { 92 | key_val.replace(new_key_value); 93 | return Ok(()); 94 | } 95 | Some(_) => {}, 96 | None => { 97 | key_val.replace(new_key_value); 98 | return Ok(()); 99 | } 100 | } 101 | } 102 | } 103 | } 104 | Err(sys::Error::ArgumentListTooLong) 105 | } 106 | 107 | pub fn remove_var(key: &str) -> sys::Result<()> { 108 | unsafe { 109 | if let Some(ref mut environ) = ENVIRON { 110 | for key_val in environ.iter_mut() { 111 | match key_val { 112 | Some(existing_key_val) if existing_key_val.starts_with(key) => { 113 | key_val.take(); 114 | return Ok(()); 115 | } 116 | Some(_) | None => {}, 117 | } 118 | } 119 | } 120 | } 121 | Err(sys::Error::InvalidArgument) 122 | } 123 | -------------------------------------------------------------------------------- /src/kernel/mpmc.rs: -------------------------------------------------------------------------------- 1 | use crate::condvar::Condvar; 2 | use crate::error::{Error::NotConnected, Result}; 3 | use crate::semaphore::Semaphore; 4 | use crate::spinlock::Mutex; 5 | use alloc::{collections::LinkedList, sync::Arc}; 6 | use core::fmt::Debug; 7 | use core::sync::atomic::{AtomicUsize, Ordering}; 8 | 9 | #[derive(Debug)] 10 | pub struct SyncSender { 11 | sem: Arc, // count receiver 12 | buf: Arc>>, 13 | cond: Arc, // cont sender 14 | scnt: Arc, 15 | rcnt: Arc, 16 | } 17 | unsafe impl Send for SyncSender {} 18 | 19 | impl Clone for SyncSender { 20 | fn clone(&self) -> Self { 21 | self.scnt.fetch_add(1, Ordering::Relaxed); 22 | Self { 23 | sem: Arc::clone(&self.sem), 24 | buf: Arc::clone(&self.buf), 25 | cond: Arc::clone(&self.cond), 26 | scnt: Arc::clone(&self.scnt), 27 | rcnt: Arc::clone(&self.rcnt), 28 | } 29 | } 30 | } 31 | 32 | impl SyncSender { 33 | pub fn send(&self, data: T) -> Result<()> { 34 | self.sem.wait()?; 35 | let mut buf = self.buf.lock(); 36 | buf.push_back(data); 37 | self.cond.notify_all(); 38 | Ok(()) 39 | } 40 | } 41 | 42 | #[derive(Debug)] 43 | pub struct Receiver { 44 | sem: Arc, 45 | buf: Arc>>, 46 | cond: Arc, 47 | scnt: Arc, 48 | rcnt: Arc, 49 | } 50 | unsafe impl Send for Receiver {} 51 | 52 | impl Clone for Receiver { 53 | fn clone(&self) -> Self { 54 | self.rcnt.fetch_add(1, Ordering::Relaxed); 55 | Self { 56 | sem: Arc::clone(&self.sem), 57 | buf: Arc::clone(&self.buf), 58 | cond: Arc::clone(&self.cond), 59 | scnt: Arc::clone(&self.scnt), 60 | rcnt: Arc::clone(&self.rcnt), 61 | } 62 | } 63 | } 64 | 65 | impl Receiver { 66 | pub fn recv(&self) -> Result { 67 | let mut buf = self.buf.lock(); 68 | loop { 69 | if let Some(data) = buf.pop_front() { 70 | self.sem.post(); 71 | break Ok(data); 72 | } 73 | if self.scnt.load(Ordering::Relaxed) > 0 { 74 | buf = self.cond.wait(buf); 75 | } else { 76 | break Err(NotConnected); 77 | } 78 | } 79 | } 80 | } 81 | 82 | pub fn sync_channel(max: isize, name: &'static str) -> (SyncSender, Receiver) { 83 | let sem = Arc::new(Semaphore::new(max, name)); 84 | let buf = Arc::new(Mutex::new(LinkedList::new(), name)); 85 | let cond = Arc::new(Condvar::new()); 86 | let scnt = Arc::new(AtomicUsize::new(1)); 87 | let rcnt = Arc::new(AtomicUsize::new(1)); 88 | let tx = SyncSender { 89 | sem: Arc::clone(&sem), 90 | buf: Arc::clone(&buf), 91 | cond: Arc::clone(&cond), 92 | scnt: Arc::clone(&scnt), 93 | rcnt: Arc::clone(&rcnt), 94 | }; 95 | let rx = Receiver { 96 | sem, 97 | buf, 98 | cond, 99 | scnt, 100 | rcnt, 101 | }; 102 | (tx, rx) 103 | } 104 | 105 | impl Drop for SyncSender { 106 | fn drop(&mut self) { 107 | self.scnt.fetch_sub(1, Ordering::Relaxed); 108 | self.cond.notify_all(); 109 | } 110 | } 111 | 112 | impl Drop for Receiver { 113 | fn drop(&mut self) { 114 | let cnt = self.rcnt.fetch_sub(1, Ordering::Relaxed); 115 | if cnt == 1 { 116 | self.sem.close(); 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/kernel/kernelvec.rs: -------------------------------------------------------------------------------- 1 | use core::arch::naked_asm; 2 | 3 | // interrupts and exceptions while in supervisor mode come here. 4 | // push all registers, call kerneltrap(), restorem return. 5 | #[unsafe(naked)] 6 | #[rustc_align(16)] 7 | #[no_mangle] 8 | pub unsafe extern "C" fn kernelvec() -> ! { 9 | naked_asm!( 10 | // make room to save registers. 11 | "addi sp, sp, -256", 12 | // save the registers. 13 | "sd ra, 0(sp)", 14 | "sd sp, 8(sp)", 15 | "sd gp, 16(sp)", 16 | "sd tp, 24(sp)", 17 | "sd t0, 32(sp)", 18 | "sd t1, 40(sp)", 19 | "sd t2, 48(sp)", 20 | "sd s0, 56(sp)", 21 | "sd s1, 64(sp)", 22 | "sd a0, 72(sp)", 23 | "sd a1, 80(sp)", 24 | "sd a2, 88(sp)", 25 | "sd a3, 96(sp)", 26 | "sd a4, 104(sp)", 27 | "sd a5, 112(sp)", 28 | "sd a6, 120(sp)", 29 | "sd a7, 128(sp)", 30 | "sd s2, 136(sp)", 31 | "sd s3, 144(sp)", 32 | "sd s4, 152(sp)", 33 | "sd s5, 160(sp)", 34 | "sd s6, 168(sp)", 35 | "sd s7, 176(sp)", 36 | "sd s8, 184(sp)", 37 | "sd s9, 192(sp)", 38 | "sd s10, 200(sp)", 39 | "sd s11, 208(sp)", 40 | "sd t3, 216(sp)", 41 | "sd t4, 224(sp)", 42 | "sd t5, 232(sp)", 43 | "sd t6, 240(sp)", 44 | // call the Rust trap handler in trap.rs 45 | "call kerneltrap", 46 | // restore registers. 47 | "ld ra, 0(sp)", 48 | "ld sp, 8(sp)", 49 | "ld gp, 16(sp)", 50 | // not this, in case we moved CPUs: ld tp, 24(sp) 51 | "ld t0, 32(sp)", 52 | "ld t1, 40(sp)", 53 | "ld t2, 48(sp)", 54 | "ld s0, 56(sp)", 55 | "ld s1, 64(sp)", 56 | "ld a0, 72(sp)", 57 | "ld a1, 80(sp)", 58 | "ld a2, 88(sp)", 59 | "ld a3, 96(sp)", 60 | "ld a4, 104(sp)", 61 | "ld a5, 112(sp)", 62 | "ld a6, 120(sp)", 63 | "ld a7, 128(sp)", 64 | "ld s2, 136(sp)", 65 | "ld s3, 144(sp)", 66 | "ld s4, 152(sp)", 67 | "ld s5, 160(sp)", 68 | "ld s6, 168(sp)", 69 | "ld s7, 176(sp)", 70 | "ld s8, 184(sp)", 71 | "ld s9, 192(sp)", 72 | "ld s10, 200(sp)", 73 | "ld s11, 208(sp)", 74 | "ld t3, 216(sp)", 75 | "ld t4, 224(sp)", 76 | "ld t5, 232(sp)", 77 | "ld t6, 240(sp)", 78 | "addi sp, sp, 256", 79 | // return to whatever we were doing in the kernel. 80 | "sret", 81 | ); 82 | } 83 | 84 | #[unsafe(naked)] 85 | #[rustc_align(16)] // if miss this alignment, a load access fault will occur. 86 | #[no_mangle] 87 | pub unsafe extern "C" fn timervec() -> ! { 88 | // start.rs has set up the memory that mscratch points to: 89 | // scratch[0,8,16] : register save area. 90 | // scratch[24] : address of CLINT's MTIMECMP register. 91 | // scratch[32] : desired interval between interrupts. 92 | 93 | // Now, mscrach has a pointer to an additional scratch space. 94 | // to aboid overwriting the contents of the integer registers, 95 | // the prologue of an interrupts handler usually begins by swapping 96 | // an integer register(say a0) with mscratch CSR. 97 | // The interrupt handler stores the integer registers 98 | // used for processing in this scratch space. 99 | // a0 saved in mscrach, a1 ~ a3 saved in scratch space. 100 | //loop {} 101 | naked_asm!( 102 | "csrrw a0, mscratch, a0", 103 | "sd a1, 0(a0)", 104 | "sd a2, 8(a0)", 105 | "sd a3, 16(a0)", 106 | // schedule the next timer interrupt 107 | // by adding interval to mtimecmp. 108 | "ld a1, 24(a0)", // CLINT_MTIMECMP(hartid) contents 109 | "ld a2, 32(a0)", // interval 110 | "ld a3, 0(a1)", 111 | "add a3, a3, a2", 112 | "sd a3, 0(a1)", 113 | // raise a supervisor software interrupt. 114 | "li a1, 2", 115 | "csrw sip, a1", 116 | // restore and return 117 | "ld a3, 16(a0)", 118 | "ld a2, 8(a0)", 119 | "ld a1, 0(a0)", 120 | "csrrw a0, mscratch, a0", 121 | "mret", 122 | ); 123 | } 124 | -------------------------------------------------------------------------------- /src/kernel/error.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | 3 | pub type Result = core::result::Result; 4 | 5 | #[repr(isize)] 6 | #[derive(PartialEq, Debug)] 7 | pub enum Error { 8 | Uncategorized, 9 | ResourceBusy = -2, 10 | NotFound = -3, 11 | OutOfMemory = -4, 12 | BadVirtAddr = -5, 13 | StorageFull = -6, 14 | TooManyLinks = -7, 15 | NoSuchProcess = -8, 16 | WouldBlock = -9, 17 | NoBufferSpace = -10, 18 | NoChildProcesses = -11, 19 | Interrupted = -12, 20 | BadFileDescriptor = -13, 21 | FileDescriptorTooLarge = -14, 22 | FileTooLarge = -15, 23 | AlreadyExists = -16, 24 | IsADirectory = -17, 25 | NotADirectory = -18, 26 | CrossesDevices = -19, 27 | PermissionDenied = -20, 28 | DirectoryNotEmpty = -21, 29 | FileTableOverflow = -22, 30 | InvalidArgument = -23, 31 | NoSuchNode = -24, 32 | BrokenPipe = -25, 33 | ExecFileFormatError = -26, 34 | ArgumentListTooLong = -27, 35 | Utf8Error = -28, 36 | WriteZero = -29, 37 | NotConnected = -30, 38 | } 39 | 40 | impl Error { 41 | pub fn as_str(&self) -> &'static str { 42 | use Error::*; 43 | match *self { 44 | ResourceBusy => "resource busy", 45 | NotFound => "entry not found", 46 | OutOfMemory => "out of memory", 47 | StorageFull => "no storage space", 48 | TooManyLinks => "too many links", 49 | BadVirtAddr => "bad virtual address", 50 | NoSuchProcess => "no such process", 51 | WouldBlock => "operation would block", 52 | NoBufferSpace => "no buffer space available", 53 | NoChildProcesses => "no child processes", 54 | Interrupted => "operation interrupted", 55 | BadFileDescriptor => "bad file descriptor", 56 | FileDescriptorTooLarge => "file descriptor value too large", 57 | FileTooLarge => "file too large", 58 | AlreadyExists => "entity already exists", 59 | IsADirectory => "is a directory", 60 | NotADirectory => "not a directory", 61 | CrossesDevices => "cross-device link or rename", 62 | PermissionDenied => "permission denied", 63 | DirectoryNotEmpty => "directory not empty", 64 | FileTableOverflow => "inode or file table overflow in system", 65 | InvalidArgument => "invalid argument", 66 | NoSuchNode => "no such node or address", 67 | BrokenPipe => "broken pipe", 68 | ExecFileFormatError => "executable file format error", 69 | ArgumentListTooLong => "argument list too long", 70 | Utf8Error => "slice is not utf8", 71 | WriteZero => "write zero", 72 | NotConnected => "not connected", 73 | Uncategorized => "uncategorized error", 74 | } 75 | } 76 | pub fn from_isize(code: isize) -> Self { 77 | use Error::*; 78 | match code { 79 | -2 => ResourceBusy, 80 | -3 => NotFound, 81 | -4 => OutOfMemory, 82 | -5 => BadVirtAddr, 83 | -6 => StorageFull, 84 | -7 => TooManyLinks, 85 | -8 => NoSuchProcess, 86 | -9 => WouldBlock, 87 | -10 => NoBufferSpace, 88 | -11 => NoChildProcesses, 89 | -12 => Interrupted, 90 | -13 => BadFileDescriptor, 91 | -14 => FileDescriptorTooLarge, 92 | -15 => FileTooLarge, 93 | -16 => AlreadyExists, 94 | -17 => IsADirectory, 95 | -18 => NotADirectory, 96 | -19 => CrossesDevices, 97 | -20 => PermissionDenied, 98 | -21 => DirectoryNotEmpty, 99 | -22 => FileTableOverflow, 100 | -23 => InvalidArgument, 101 | -24 => NoSuchNode, 102 | -25 => BrokenPipe, 103 | -26 => ExecFileFormatError, 104 | -27 => ArgumentListTooLong, 105 | -28 => Utf8Error, 106 | -29 => WriteZero, 107 | -30 => NotConnected, 108 | _ => Uncategorized, 109 | } 110 | } 111 | } 112 | 113 | impl fmt::Display for Error { 114 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 115 | f.write_str(self.as_str()) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/user/lib/umalloc.rs: -------------------------------------------------------------------------------- 1 | use crate::{mutex::Mutex, sys}; 2 | use core::alloc::{GlobalAlloc, Layout}; 3 | use core::{mem::size_of, ptr::NonNull}; 4 | 5 | #[global_allocator] 6 | static UMEM: UMem = UMem(Mutex::new(Allocator::new())); 7 | 8 | struct UMem(Mutex); 9 | unsafe impl GlobalAlloc for UMem { 10 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 11 | self.0.lock().alloc(layout) 12 | } 13 | unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { 14 | self.0.lock().free(ptr) 15 | } 16 | } 17 | 18 | #[alloc_error_handler] 19 | fn on_oom(layout: Layout) -> ! { 20 | panic!("alloc error: {:?}", layout) 21 | } 22 | 23 | struct Allocator { 24 | base: Header, 25 | freep: Option>, 26 | } 27 | unsafe impl Send for Allocator {} 28 | 29 | #[repr(C, align(16))] 30 | struct Header { 31 | ptr: Option>, 32 | size: usize, 33 | } 34 | 35 | impl Header { 36 | const fn new() -> Self { 37 | Self { ptr: None, size: 0 } 38 | } 39 | } 40 | 41 | impl Allocator { 42 | const fn new() -> Self { 43 | Self { 44 | base: Header::new(), 45 | freep: None, 46 | } 47 | } 48 | 49 | unsafe fn alloc(&mut self, layout: Layout) -> *mut u8 { 50 | let nbytes = layout.size(); 51 | let mut p: Option>; 52 | let mut prevp: Option>; 53 | 54 | let nunits = (nbytes + size_of::
() - 1) / size_of::
() + 1; 55 | prevp = self.freep; 56 | 57 | if prevp.is_none() { 58 | prevp = NonNull::new(&mut self.base as *mut _); 59 | self.freep = prevp; 60 | self.base.ptr = prevp; 61 | self.base.size = 0; 62 | } 63 | 64 | p = prevp.unwrap().as_ref().ptr; 65 | loop { 66 | if p.unwrap().as_ref().size >= nunits { 67 | if p.unwrap().as_ref().size == nunits { 68 | prevp.unwrap().as_mut().ptr = p.unwrap().as_ref().ptr; 69 | } else { 70 | p.unwrap().as_mut().size -= nunits; 71 | p = NonNull::new(p.unwrap().as_ptr().add(p.unwrap().as_ref().size)); 72 | p.unwrap().as_mut().size = nunits; 73 | } 74 | self.freep = prevp; 75 | break p.unwrap().as_ptr().add(1) as *mut u8; 76 | } 77 | if p == self.freep { 78 | match self.morecore(nunits) { 79 | None => break core::ptr::null_mut(), 80 | np => p = np, 81 | } 82 | } 83 | prevp = p; 84 | p = p.unwrap().as_ref().ptr; 85 | } 86 | } 87 | 88 | unsafe fn morecore(&mut self, mut nu: usize) -> Option> { 89 | if nu < 4096 { 90 | nu = 4096 91 | } 92 | let p = NonNull::new(sys::sbrk(nu * size_of::
()).ok()? as *mut Header); 93 | p.unwrap().as_mut().size = nu; 94 | self.free(p.unwrap().as_ptr().add(1) as *mut u8); 95 | self.freep 96 | } 97 | 98 | unsafe fn free(&mut self, ap: *mut u8) { 99 | let bp: Option> = NonNull::new((ap as *mut Header).sub(1)); 100 | let mut p = self.freep; 101 | 102 | while !(bp > p && bp < p.unwrap().as_ref().ptr) { 103 | if p >= p.unwrap().as_ref().ptr && (bp > p || bp < p.unwrap().as_ref().ptr) { 104 | break; 105 | } 106 | p = p.unwrap().as_ref().ptr; 107 | } 108 | 109 | if NonNull::new(bp.unwrap().as_ptr().add(bp.unwrap().as_ref().size)) 110 | == p.unwrap().as_ref().ptr 111 | { 112 | let p_ptr = p.unwrap().as_ref().ptr; 113 | bp.unwrap().as_mut().size += p_ptr.unwrap().as_ref().size; 114 | bp.unwrap().as_mut().ptr = p_ptr.unwrap().as_ref().ptr; 115 | } else { 116 | bp.unwrap().as_mut().ptr = p.unwrap().as_ref().ptr; 117 | } 118 | 119 | if NonNull::new(p.unwrap().as_ptr().add(p.unwrap().as_ref().size)) == bp { 120 | p.unwrap().as_mut().size += bp.unwrap().as_ref().size; 121 | p.unwrap().as_mut().ptr = bp.unwrap().as_ref().ptr; 122 | } else { 123 | p.unwrap().as_mut().ptr = bp; 124 | } 125 | self.freep = p; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/kernel/defs.rs: -------------------------------------------------------------------------------- 1 | // Gets the bytes of the value. 2 | // 3 | // as_bytes() provides access to the bytes of the value as an immutable 4 | // byte slice. 5 | // 6 | // # Safety: 7 | // If the memory layout of T is fixed 8 | pub unsafe fn as_bytes(refs: &T) -> &[u8] { 9 | let len = core::mem::size_of_val(refs); 10 | core::slice::from_raw_parts(refs as *const T as *const u8, len) 11 | } 12 | 13 | // Gets the bytes of the value mutably. 14 | // 15 | // as_bytes_mut() provides access to the bytes of the value as a mutable 16 | // byte slice. 17 | // 18 | // # Safety: 19 | // If the memory layout of T is fixed 20 | pub unsafe fn as_bytes_mut(refs: &mut T) -> &mut [u8] { 21 | let len = core::mem::size_of_val(refs); 22 | core::slice::from_raw_parts_mut(refs as *mut T as *mut u8, len) 23 | } 24 | 25 | // Array Macro for const variables 26 | pub use core::mem::{ManuallyDrop, MaybeUninit}; 27 | use core::net::Ipv4Addr; 28 | 29 | use crate::{fs::DirEnt, stat::Stat}; 30 | 31 | #[repr(C)] 32 | pub union _transmuter { 33 | pub arr_in: ManuallyDrop<[MaybeUninit; N]>, 34 | pub arr_out: ManuallyDrop<[T; N]>, 35 | } 36 | 37 | #[macro_export] 38 | macro_rules! array { 39 | [$e:expr; $count:expr] => { 40 | unsafe { 41 | use $crate::defs::{ManuallyDrop, MaybeUninit, _transmuter}; 42 | 43 | let mut arr_in: [MaybeUninit<_>; $count] = MaybeUninit::uninit().assume_init(); 44 | let mut idx = 0; 45 | while idx < $count { 46 | arr_in[idx] = MaybeUninit::new($e); 47 | idx += 1; 48 | } 49 | ManuallyDrop::into_inner(_transmuter { arr_in: ManuallyDrop::new(arr_in) }.arr_out) 50 | } 51 | }; 52 | } 53 | 54 | // # Safety 55 | // - The memory layout of T must be fixed and all bytes of T must be valid. 56 | // - It must be safe to interpret an instance of T as a slice of bytes. 57 | // - A reference to T must be able to coexist with a byte slice reference to the same memory. 58 | pub unsafe trait AsBytes { 59 | fn as_bytes(&self) -> &[u8] { 60 | unsafe { 61 | let len = core::mem::size_of_val(self); 62 | let slf: *const Self = self; 63 | core::slice::from_raw_parts(slf.cast::(), len) 64 | } 65 | } 66 | fn as_bytes_mut(&mut self) -> &mut [u8] { 67 | unsafe { 68 | let len = core::mem::size_of_val(self); 69 | let slf: *mut Self = self; 70 | core::slice::from_raw_parts_mut(slf.cast::(), len) 71 | } 72 | } 73 | } 74 | 75 | // # Safety 76 | // - The memory layout of T must be fixed and all bytes of T must be valid. 77 | // - It must be safe to interpret a properly aligned byte slice of the correct length as an instance of T. 78 | // - A reference to T must be able to coexist with a byte slice reference to the same memory. 79 | pub unsafe trait FromBytes: Sized { 80 | fn ref_from(bytes: &[u8]) -> Option<&Self> { 81 | if bytes.len() < core::mem::size_of::() { 82 | None 83 | } else { 84 | unsafe { Some(&*(bytes.as_ptr() as *const Self)) } 85 | } 86 | } 87 | fn mut_from(bytes: &mut [u8]) -> Option<&mut Self> { 88 | if bytes.len() < core::mem::size_of::() { 89 | None 90 | } else { 91 | unsafe { Some(&mut *(bytes.as_mut_ptr() as *mut Self)) } 92 | } 93 | } 94 | fn read_from(bytes: &[u8]) -> Option { 95 | if bytes.len() < core::mem::size_of::() { 96 | None 97 | } else { 98 | let mut value = core::mem::MaybeUninit::::uninit(); 99 | unsafe { 100 | core::ptr::copy_nonoverlapping( 101 | bytes.as_ptr(), 102 | value.as_mut_ptr() as *mut u8, 103 | core::mem::size_of::(), 104 | ); 105 | Some(value.assume_init()) 106 | } 107 | } 108 | } 109 | } 110 | 111 | // u8, [u8; N], [u8], stats 112 | unsafe impl AsBytes for Stat {} 113 | unsafe impl AsBytes for str {} 114 | unsafe impl AsBytes for u8 {} 115 | unsafe impl AsBytes for usize {} 116 | unsafe impl AsBytes for i32 {} 117 | unsafe impl AsBytes for [T] {} 118 | unsafe impl AsBytes for [T; N] {} 119 | unsafe impl AsBytes for DirEnt {} 120 | unsafe impl AsBytes for Ipv4Addr {} 121 | // null pointer optimization 122 | unsafe impl AsBytes for Option<&str> {} 123 | unsafe impl AsBytes for Option<&[u8]> {} 124 | 125 | unsafe impl FromBytes for u8 {} 126 | unsafe impl FromBytes for u16 {} 127 | unsafe impl FromBytes for u32 {} 128 | unsafe impl FromBytes for u64 {} 129 | unsafe impl FromBytes for usize {} 130 | unsafe impl FromBytes for [T; N] {} 131 | unsafe impl FromBytes for Ipv4Addr {} 132 | -------------------------------------------------------------------------------- /src/user/lib/stdio.rs: -------------------------------------------------------------------------------- 1 | use alloc::string::String; 2 | use alloc::vec::Vec; 3 | 4 | use crate::fs::File; 5 | use crate::io::{self, Read, Write}; 6 | use crate::mutex::Mutex; 7 | use crate::sys::{self, sync::OnceLock, Error::AlreadyExists, Error::Utf8Error}; 8 | use core::fmt; 9 | 10 | pub const STDIN_FILENO: usize = 0; 11 | pub const STDOUT_FILENO: usize = 1; 12 | pub const STDERR_FILENO: usize = 2; 13 | 14 | static mut STDIN: OnceLock> = OnceLock::new(); 15 | static mut STDOUT: OnceLock> = OnceLock::new(); 16 | static mut STDERR: OnceLock> = OnceLock::new(); 17 | 18 | pub struct Stdin { 19 | inner: &'static mut OnceLock>, 20 | } 21 | 22 | pub fn stdin() -> Stdin { 23 | unsafe { Stdin { inner: &mut STDIN } } 24 | } 25 | 26 | impl Stdin { 27 | pub fn set(&self, file: File) -> sys::Result<()> { 28 | self.inner.set(Mutex::new(file)).or(Err(AlreadyExists)) 29 | } 30 | pub fn replace(&mut self, src: &File) -> sys::Result<()> { 31 | File::dup2(src, &mut self.inner.get().unwrap().lock()) 32 | } 33 | 34 | pub fn read_line(&mut self, buf: &mut String) -> sys::Result { 35 | let mut char: [u8; 1] = [0]; 36 | let mut bytes: Vec = Vec::new(); 37 | loop { 38 | let cc = self.read(&mut char)?; 39 | if cc < 1 { 40 | break; 41 | } 42 | bytes.extend_from_slice(&char); 43 | if char[0] == b'\n' || char[0] == b'\r' { 44 | break; 45 | } 46 | } 47 | buf.push_str(core::str::from_utf8(&bytes).or(Err(Utf8Error))?); 48 | Ok(buf.len()) 49 | } 50 | } 51 | 52 | impl Read for Stdin { 53 | fn read(&mut self, buf: &mut [u8]) -> sys::Result { 54 | self.inner 55 | .get_or_init(|| Mutex::new(unsafe { File::from_raw_fd(STDIN_FILENO) })) 56 | .lock() 57 | .read(buf) 58 | } 59 | } 60 | 61 | pub struct Stdout { 62 | inner: &'static mut OnceLock>, 63 | } 64 | 65 | pub fn stdout() -> Stdout { 66 | unsafe { Stdout { inner: &mut STDOUT } } 67 | } 68 | 69 | impl Stdout { 70 | pub fn set(&self, file: File) -> sys::Result<()> { 71 | self.inner.set(Mutex::new(file)).or(Err(AlreadyExists)) 72 | } 73 | pub fn replace(&mut self, src: &File) -> sys::Result<()> { 74 | File::dup2(src, &mut self.inner.get().unwrap().lock()) 75 | } 76 | } 77 | 78 | impl Write for Stdout { 79 | fn write(&mut self, buf: &[u8]) -> sys::Result { 80 | self.inner 81 | .get_or_init(|| Mutex::new(unsafe { File::from_raw_fd(STDOUT_FILENO) })) 82 | .lock() 83 | .write(buf) 84 | } 85 | } 86 | 87 | pub struct Stderr { 88 | inner: &'static mut OnceLock>, 89 | } 90 | 91 | pub fn stderr() -> Stderr { 92 | unsafe { Stderr { inner: &mut STDERR } } 93 | } 94 | 95 | impl Stderr { 96 | pub fn set(&self, file: File) -> sys::Result<()> { 97 | self.inner.set(Mutex::new(file)).or(Err(AlreadyExists)) 98 | } 99 | pub fn replace(&mut self, src: &File) -> sys::Result<()> { 100 | File::dup2(src, &mut self.inner.get().unwrap().lock()) 101 | } 102 | } 103 | 104 | impl Write for Stderr { 105 | fn write(&mut self, buf: &[u8]) -> sys::Result { 106 | self.inner 107 | .get_or_init(|| Mutex::new(unsafe { File::from_raw_fd(STDERR_FILENO) })) 108 | .lock() 109 | .write(buf) 110 | } 111 | } 112 | 113 | fn print_to(args: fmt::Arguments<'_>, global_s: fn() -> T) 114 | where 115 | T: Write, 116 | { 117 | if let Err(e) = global_s().write_fmt(args) { 118 | panic!("failed printing {e}"); 119 | } 120 | } 121 | 122 | pub fn _print(args: fmt::Arguments<'_>) { 123 | print_to(args, stdout); 124 | } 125 | 126 | pub fn _eprint(args: fmt::Arguments<'_>) { 127 | print_to(args, stderr); 128 | } 129 | 130 | #[macro_export] 131 | macro_rules! print { 132 | ($($arg:tt)*) => { 133 | $crate::stdio::_print(format_args!($($arg)*)) 134 | }; 135 | } 136 | 137 | #[macro_export] 138 | macro_rules! println { 139 | ($fmt:expr) => { 140 | print!(concat!($fmt, "\n")) 141 | }; 142 | ($fmt:expr, $($arg:tt)*) => { 143 | print!(concat!($fmt, "\n"), $($arg)*) 144 | }; 145 | } 146 | 147 | #[macro_export] 148 | macro_rules! eprint { 149 | ($($arg:tt)*) => { 150 | $crate::stdio::_eprint(format_args!($($arg)*)) 151 | }; 152 | } 153 | 154 | #[macro_export] 155 | macro_rules! eprintln { 156 | ($fmt:expr) => { 157 | eprint!(concat!($fmt, "\n")) 158 | }; 159 | ($fmt:expr, $($arg:tt)*) => { 160 | eprint!(concat!($fmt, "\n"), $($arg)*) 161 | }; 162 | } 163 | 164 | pub fn panic_output() -> Option<&'static mut impl io::Write> { 165 | unsafe { 166 | Some( 167 | stderr() 168 | .inner 169 | .get_or_init(|| Mutex::new(File::from_raw_fd(STDERR_FILENO))) 170 | .get_mut(), 171 | ) 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/kernel/trampoline.rs: -------------------------------------------------------------------------------- 1 | // low-level code to handle traps from user space into 2 | // the kernel, and returns from kernel to user. 3 | // 4 | // the kernel maps the page holding this code 5 | // at the same virtual address (TRAMPOLINE) 6 | // in user and kernel space so that is continues 7 | // to work when it switches page tables. 8 | // kernel.ld causes this code to start at a 9 | // page boundary. 10 | 11 | use crate::memlayout::TRAPFRAME; 12 | use core::arch::naked_asm; 13 | 14 | extern "C" { 15 | pub fn trampoline(); 16 | } 17 | 18 | #[link_section = "trampsec"] 19 | #[unsafe(naked)] 20 | #[no_mangle] 21 | #[rustc_align(16)] 22 | pub unsafe extern "C" fn uservec() -> ! { 23 | // trap.rs sets stvec to point here, so 24 | // traps from user space start here, 25 | // in supervisor mode, but with a 26 | // user page table. 27 | 28 | naked_asm!( 29 | // save user a0 in sscratch so 30 | // a0 can be used to get at TRAPFRAME 31 | "csrw sscratch, a0", 32 | // each process has a separate p.trapframe memory area, 33 | // but it's mapped to the same virtual address 34 | // (TRAPFRAME) in every process's user page table. 35 | "li a0, {tf}", 36 | // save the user registers in TRAPFRAME 37 | "sd ra, 40(a0)", 38 | "sd sp, 48(a0)", 39 | "sd gp, 56(a0)", 40 | "sd tp, 64(a0)", 41 | "sd t0, 72(a0)", 42 | "sd t1, 80(a0)", 43 | "sd t2, 88(a0)", 44 | "sd s0, 96(a0)", 45 | "sd s1, 104(a0)", 46 | "sd a1, 120(a0)", 47 | "sd a2, 128(a0)", 48 | "sd a3, 136(a0)", 49 | "sd a4, 144(a0)", 50 | "sd a5, 152(a0)", 51 | "sd a6, 160(a0)", 52 | "sd a7, 168(a0)", 53 | "sd s2, 176(a0)", 54 | "sd s3, 184(a0)", 55 | "sd s4, 192(a0)", 56 | "sd s5, 200(a0)", 57 | "sd s6, 208(a0)", 58 | "sd s7, 216(a0)", 59 | "sd s8, 224(a0)", 60 | "sd s9, 232(a0)", 61 | "sd s10, 240(a0)", 62 | "sd s11, 248(a0)", 63 | "sd t3, 256(a0)", 64 | "sd t4, 264(a0)", 65 | "sd t5, 272(a0)", 66 | "sd t6, 280(a0)", 67 | // save the user a0 in p->trapframe->a0 68 | "csrr t0, sscratch", 69 | "sd t0, 112(a0)", 70 | // restore the kernel stack pointer from p->trapframe->kernel_sp 71 | "ld sp, 8(a0)", 72 | // make tp hold the current hartid, from p->trapframe->kernel_hartid 73 | "ld tp, 32(a0)", 74 | // load the address of usertrap(), p->trapframe->kernel_trap 75 | "ld t0, 16(a0)", 76 | // fetch the kernel page table from p->trapframe->kernel_satp 77 | "ld t1, 0(a0)", 78 | // wait for any previous memory operations to complete, so that 79 | // they use the user page table 80 | "sfence.vma zero, zero", 81 | // install the kernel page table 82 | "csrw satp, t1", 83 | // flush now-stable user entries from the TLB 84 | "sfence.vma zero, zero", 85 | // jump to usertrap(), which does not return 86 | "jr t0", 87 | tf = const TRAPFRAME, 88 | ); 89 | } 90 | 91 | #[link_section = "trampsec"] 92 | #[unsafe(naked)] 93 | #[no_mangle] 94 | #[rustc_align(16)] 95 | pub unsafe extern "C" fn userret(pagetable: usize) -> ! { 96 | // userret(TRAPFLAME, pagetable) 97 | // called by usertrap_ret() in trap.rs to 98 | // switch from kernel to user. 99 | // a0: TRAPFRAME, in user page table. 100 | // a1: user page table, for satp. 101 | 102 | naked_asm!( 103 | // switch to the user page table. 104 | "sfence.vma zero, zero", 105 | "csrw satp, a0", 106 | "sfence.vma zero, zero", 107 | // put the saved user a0 in sscratch, so we 108 | // can swap it with our a0 (TRAPRAME) 109 | // set TRAPFRAME to a0 110 | "li a0, {tf}", 111 | // restore all but a0 from TRAPFRAME 112 | "ld ra, 40(a0)", 113 | "ld sp, 48(a0)", 114 | "ld gp, 56(a0)", 115 | "ld tp, 64(a0)", 116 | "ld t0, 72(a0)", 117 | "ld t1, 80(a0)", 118 | "ld t2, 88(a0)", 119 | "ld s0, 96(a0)", 120 | "ld s1, 104(a0)", 121 | "ld a1, 120(a0)", 122 | "ld a2, 128(a0)", 123 | "ld a3, 136(a0)", 124 | "ld a4, 144(a0)", 125 | "ld a5, 152(a0)", 126 | "ld a6, 160(a0)", 127 | "ld a7, 168(a0)", 128 | "ld s2, 176(a0)", 129 | "ld s3, 184(a0)", 130 | "ld s4, 192(a0)", 131 | "ld s5, 200(a0)", 132 | "ld s6, 208(a0)", 133 | "ld s7, 216(a0)", 134 | "ld s8, 224(a0)", 135 | "ld s9, 232(a0)", 136 | "ld s10, 240(a0)", 137 | "ld s11, 248(a0)", 138 | "ld t3, 256(a0)", 139 | "ld t4, 264(a0)", 140 | "ld t5, 272(a0)", 141 | "ld t6, 280(a0)", 142 | // restore user a0 143 | "ld a0, 112(a0)", 144 | // return to user mode and user pc, 145 | // usertrap_ret() set up sstatus and sepc. 146 | "sret", 147 | tf = const TRAPFRAME, 148 | ); 149 | } 150 | -------------------------------------------------------------------------------- /src/user/lib/io.rs: -------------------------------------------------------------------------------- 1 | use crate::sys::{self, Error::*}; 2 | use core::fmt; 3 | 4 | pub trait Write { 5 | fn write(&mut self, buf: &[u8]) -> sys::Result; 6 | fn write_all(&mut self, mut buf: &[u8]) -> sys::Result<()> { 7 | while !buf.is_empty() { 8 | match self.write(buf) { 9 | Ok(0) => return Err(WriteZero), 10 | Ok(n) => buf = &buf[n..], 11 | Err(Interrupted) => {} 12 | Err(e) => return Err(e), 13 | } 14 | } 15 | Ok(()) 16 | } 17 | fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> sys::Result<()> { 18 | struct Adapter<'a, T: ?Sized + 'a> { 19 | inner: &'a mut T, 20 | error: sys::Result<()>, 21 | } 22 | 23 | impl fmt::Write for Adapter<'_, T> { 24 | fn write_str(&mut self, s: &str) -> fmt::Result { 25 | match self.inner.write_all(s.as_bytes()) { 26 | Ok(()) => Ok(()), 27 | Err(e) => { 28 | self.error = Err(e); 29 | Err(fmt::Error) 30 | } 31 | } 32 | } 33 | } 34 | 35 | let mut output = Adapter { 36 | inner: self, 37 | error: Ok(()), 38 | }; 39 | match fmt::write(&mut output, fmt) { 40 | Ok(()) => Ok(()), 41 | Err(_) => { 42 | if output.error.is_err() { 43 | output.error 44 | } else { 45 | Err(Uncategorized) 46 | } 47 | } 48 | } 49 | } 50 | } 51 | 52 | use alloc::{string::String, vec::Vec}; 53 | pub trait Read { 54 | fn read(&mut self, buf: &mut [u8]) -> sys::Result; 55 | fn read_to_end(&mut self, buf: &mut Vec) -> sys::Result { 56 | let start_len = buf.len(); 57 | loop { 58 | let mut space = [0u8; 32]; 59 | match self.read(&mut space) { 60 | Ok(0) => return Ok(buf.len() - start_len), 61 | Ok(n) => { 62 | buf.extend_from_slice(&space[..n]); 63 | continue; 64 | } 65 | Err(e) if e == sys::Error::Interrupted => continue, 66 | Err(e) => return Err(e), 67 | } 68 | } 69 | } 70 | fn read_to_string(&mut self, buf: &mut String) -> sys::Result { 71 | let len = buf.len(); 72 | let buf_as_vec = unsafe { buf.as_mut_vec() }; 73 | let ret = self.read_to_end(buf_as_vec); 74 | if core::str::from_utf8(&buf_as_vec[len..]).is_err() { 75 | Err(Utf8Error) 76 | } else { 77 | ret 78 | } 79 | } 80 | } 81 | 82 | pub struct BufReader { 83 | inner: R, 84 | } 85 | 86 | impl BufReader { 87 | pub fn new(inner: R) -> BufReader { 88 | Self { inner } 89 | } 90 | pub fn read_line(&mut self, buf: &mut String) -> sys::Result { 91 | let mut char: [u8; 1] = [0]; 92 | let mut bytes: Vec = Vec::new(); 93 | 94 | loop { 95 | let cc = self.read(&mut char)?; 96 | if cc < 1 { 97 | break; 98 | } 99 | bytes.extend_from_slice(&char); 100 | if char[0] == b'\n' || char[0] == b'\r' { 101 | break; 102 | } 103 | } 104 | buf.push_str(core::str::from_utf8(&bytes).or(Err(Utf8Error))?); 105 | Ok(buf.len()) 106 | } 107 | } 108 | 109 | impl Read for BufReader { 110 | fn read(&mut self, buf: &mut [u8]) -> sys::Result { 111 | self.inner.read(buf) 112 | } 113 | } 114 | 115 | pub struct Lines { 116 | buf: B 117 | } 118 | 119 | impl Iterator for Lines { 120 | type Item = sys::Result; 121 | fn next(&mut self) -> Option { 122 | let mut buf = String::new(); 123 | match self.buf.read_line(&mut buf) { 124 | Ok(0) => None, 125 | Ok(_n) => { 126 | if buf.ends_with('\n') { 127 | buf.pop(); 128 | if buf.ends_with('\r') { 129 | buf.pop(); 130 | } 131 | } 132 | Some(Ok(buf)) 133 | }, 134 | Err(e) => Some(Err(e)), 135 | } 136 | } 137 | } 138 | 139 | pub trait BufRead: Read { 140 | fn lines(self) -> Lines 141 | where 142 | Self: Sized, 143 | { 144 | Lines { buf: self } 145 | } 146 | fn read_line(&mut self, buf: &mut String) -> sys::Result { 147 | let mut char: [u8; 1] = [0]; 148 | let mut bytes: Vec = Vec::new(); 149 | 150 | loop { 151 | let cc = self.read(&mut char)?; 152 | if cc < 1 { 153 | break; 154 | } 155 | bytes.extend_from_slice(&char); 156 | if char[0] == b'\n' || char[0] == b'\r' { 157 | break; 158 | } 159 | } 160 | buf.push_str(core::str::from_utf8(&bytes).or(Err(Utf8Error))?); 161 | Ok(buf.len()) 162 | } 163 | } 164 | 165 | impl BufRead for BufReader {} 166 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | #+title: octox 2 | #+author: Hayato Ohhashi 3 | #+email: o8@vmm.dev 4 | 5 | octox is a Unix-like operating system inspired by xv6-riscv. octox loosely 6 | follows the structure and style of xv6, but is implemented in pure Rust. 7 | 8 | [[https://vhs.charm.sh/vhs-6MQBIyAo3DpBrARBxHxL35.gif]] 9 | 10 | - Everything from kernel, userland, mkfs, to build system is written in safe 11 | Rust as much as possible. 12 | - There are no dependencies on external crates. 13 | - The userland has a library similar to Rust’s std with K&R malloc. 14 | - Multi-core support, buddy allocator as kernel-side memory allocator, file 15 | system with logging support, etc. 16 | 17 | * Getting Started 18 | 19 | ** Requirements 20 | 21 | - Install the rust toolchain to have cargo installed by following 22 | [[https://www.rust-lang.org/tools/install][this]] guide. 23 | - Install ~qemu-system-riscv~ 24 | - (option) Install ~gdb-multiarch~ 25 | 26 | ** Build and Run 27 | 28 | - Clone this project & enter: ~git clone ... && cd octox~ 29 | - Build: ~cargo build --target riscv64gc-unknown-none-elf~. 30 | - Run: ~cargo run --target riscv64gc-unknown-none-elf~, then qemu will boot 31 | octox. To exit, press ~Ctrl+a~ and ~x~. 32 | 33 | ** Play with the Shell 34 | 35 | A very simple shell is implemented. 36 | In addition to executing commands, you can only do the following things. 37 | 38 | - Pipe: ~cat file | head | grep test~ 39 | - Dump processes: ~Ctrl + P~ 40 | - End of line: ~Ctrl + D~ 41 | - Redirect output: ~>~, ~>>~ 42 | 43 | * Development 44 | 45 | ** Userland Application 46 | 47 | The userland comes with a user library called ulib (located at src/user/lib) 48 | that is similar to Rust’s std, so you can use it to develop your favorite 49 | commands. If you create a bin crate named ~_command~ in src/user/bin, the 50 | build.rs and mkfs.rs will place a file named ~command~ in the file system 51 | and make it available for use. 52 | 53 | - In src/user/Cargo.toml, define a bin crate with the name of the command you 54 | want to create with a ~_~ prefix 55 | #+begin_src toml 56 | [[bin]] 57 | name = "_rm" 58 | path = "bin/rm.rs" 59 | #+end_src 60 | - userland is also no_std, so don’t forget to add ~#[no_std]~. Use ulib to 61 | develop any command you like. Here is an example of the rm command. 62 | #+begin_src rust 63 | #![no_std] 64 | use ulib::{env, fs}; 65 | 66 | fn main() { 67 | let mut args = env::args().skip(1).peekable(); 68 | 69 | if args.peek().is_none() { 70 | panic!("Usage: rm files...") 71 | } 72 | for arg in args { 73 | fs::remove_file(arg).unwrap() 74 | } 75 | } 76 | #+end_src 77 | - Then, ~cargo run --target riscv64gc-unknown-none-elf~ in the root of octox. 78 | - To use ~Vec~ and ~String~, etc, do the following: 79 | #+begin_src rust 80 | extern crate alloc; 81 | use alloc::{string::String, vec::Vec}; 82 | #+end_src 83 | 84 | ** Kernel 85 | 86 | Developing in src/kernel. Here is an example of adding a system call. If you 87 | want to add a new system call, you only need to add a definition to the system 88 | call table in libkernel, and the userland library will be automatically 89 | generated by build.rs. 90 | 91 | - Add a variant and Syscall Number to ~enum SysCalls~ in src/kernel/syscall.rs. 92 | Here is ~Dup2~ as an example: 93 | #+begin_src rust 94 | pub enum SysCalls { 95 | Fork = 1, 96 | ..., 97 | Dup2 = 23, 98 | Invalid = 0, 99 | } 100 | #+end_src 101 | - Define the function signature of the system call in the ~TABLE~ of 102 | ~SysCalls~. Use the enum type ~Fn~ to describe the return type(~U~ (Unit), 103 | ~I~ (Integer), ~N~ (never)) and use ~&str~ to represent arguments. then, 104 | define kernel-side implementation as a method on ~SysCalls~. ~cfg~ flag is 105 | used to control the compilation target for kernel and the rest. Here is an 106 | example of ~dup2~: 107 | #+begin_src rust 108 | impl SysCalls { 109 | pub const TABLE: [(fn, &'static str); variant_count::()] = [ 110 | (Fn::N(Self::Invalid), ""), 111 | (Fn::I(Self::fork), "()"), 112 | (Fn::N(Self::exit), "(xstatus: i32)"), 113 | ..., 114 | (Fn::I(Self::dup2), "(src: usize, dst: usize)"), 115 | ]; 116 | pub fn dup2() -> Result { 117 | #[cfg(not(all(target_os = "none", feature = "kernel")))] 118 | return Ok(0); 119 | #[cfg(all(target_os = "none", feature = "kernel"))] 120 | { 121 | let p = Cpus::myproc().unwrap().data_mut(); 122 | let src_fd = argraw(0); let dst_fd = argraw(1); 123 | if src_fd != dst_fd { 124 | let mut src = p.ofile.get_mut(src_fd).unwrap() 125 | .take().unwrap(); 126 | src.clear_cloexec(); 127 | p.ofile.get_mut(dst_fd) 128 | .ok_or(FileDescriptorTooLarge)?.replace(src); 129 | } 130 | Ok(dst_fd) 131 | } 132 | } 133 | #+end_src 134 | - With just these steps, the dup2 system call is implemented in both kernel and 135 | userland. 136 | 137 | * License 138 | 139 | Licensed under either of 140 | 141 | - [[http://www.apache.org/licenses/LICENSE-2.0][Apache License, Version 2.0]] 142 | - [[http://opensource.org/licenses/MIT][MIT license]] 143 | 144 | at your option. 145 | 146 | * Acknowledgments 147 | 148 | octox is inspired by [[https://github.com/mit-pdos/xv6-riscv][xv6-riscv]]. 149 | 150 | I'm also grateful for the bug reports and discussion about the implementation 151 | contributed by Takahiro Itazuri and Kuniyuki Iwashima. 152 | 153 | * Contribution 154 | 155 | This is a hobby learning project, but contributions are welcome! However, please note that reviews may take considerable time. Discussions and advice are always appreciated. 156 | -------------------------------------------------------------------------------- /src/kernel/sync.rs: -------------------------------------------------------------------------------- 1 | use core::cell::{Cell, UnsafeCell}; 2 | use core::mem::MaybeUninit; 3 | use core::ops::Deref; 4 | use core::sync::atomic::{AtomicUsize, Ordering}; 5 | 6 | const IMCOMPLETE: usize = 0x0; 7 | const POISONED: usize = 0x1; 8 | const RUNNING: usize = 0x2; 9 | const COMPLETE: usize = 0x3; 10 | 11 | pub struct OnceLock { 12 | state: AtomicUsize, 13 | inner: UnsafeCell>, 14 | } 15 | 16 | unsafe impl Sync for OnceLock {} 17 | unsafe impl Send for OnceLock {} 18 | 19 | struct OnceLockGuard<'a, T> { 20 | oncecell: &'a OnceLock, 21 | poison: bool, 22 | } 23 | 24 | impl OnceLock { 25 | pub const fn new() -> Self { 26 | Self { 27 | state: AtomicUsize::new(IMCOMPLETE), 28 | inner: UnsafeCell::new(MaybeUninit::uninit()), 29 | } 30 | } 31 | fn try_get(&self) -> Result<&T, usize> { 32 | match self.state.load(Ordering::Acquire) { 33 | POISONED => panic!("poisoned"), 34 | COMPLETE => Ok(unsafe { self.get_unchecked() }), 35 | IMCOMPLETE => Err(IMCOMPLETE), 36 | RUNNING => Err(RUNNING), 37 | _ => unreachable!(), 38 | } 39 | } 40 | 41 | pub fn get(&self) -> Option<&T> { 42 | if self.is_initialized() { 43 | Some(unsafe { self.get_unchecked() }) 44 | } else { 45 | None 46 | } 47 | } 48 | 49 | pub fn get_mut(&mut self) -> Option<&mut T> { 50 | if self.is_initialized() { 51 | Some(unsafe { self.get_unchecked_mut() }) 52 | } else { 53 | None 54 | } 55 | } 56 | 57 | #[allow(clippy::result_unit_err)] 58 | pub fn get_or_try_init(&self, func: impl FnOnce() -> T) -> Result<&T, ()> { 59 | match self.try_get() { 60 | Ok(res) => Ok(res), 61 | Err(RUNNING) => Err(()), 62 | Err(IMCOMPLETE) => { 63 | let mut func = Some(func); 64 | let res = self.try_init_inner(&mut || func.take().unwrap()())?; 65 | Ok(res) 66 | } 67 | Err(_) => unreachable!(), 68 | } 69 | } 70 | pub fn get_or_init(&self, func: impl FnOnce() -> T) -> &T { 71 | match self.get_or_try_init(func) { 72 | Ok(res) => res, 73 | Err(_) => loop { 74 | if self.state.load(Ordering::Acquire) == COMPLETE { 75 | break unsafe { self.get_unchecked() }; 76 | } 77 | core::hint::spin_loop() 78 | }, 79 | } 80 | } 81 | pub fn set(&self, value: T) -> Result<(), T> { 82 | let mut value = Some(value); 83 | self.get_or_init(|| value.take().unwrap()); 84 | match value { 85 | None => Ok(()), 86 | Some(value) => Err(value), 87 | } 88 | } 89 | 90 | fn try_block(&self, order: Ordering) -> Result, ()> { 91 | match self 92 | .state 93 | .compare_exchange(IMCOMPLETE, RUNNING, order, Ordering::Acquire) 94 | { 95 | Ok(prev) if prev == IMCOMPLETE => Ok(OnceLockGuard { 96 | oncecell: self, 97 | poison: true, 98 | }), 99 | _ => Err(()), 100 | } 101 | } 102 | 103 | fn try_init_inner(&self, func: &mut dyn FnMut() -> T) -> Result<&T, ()> { 104 | unsafe { 105 | let mut guard = self.try_block(Ordering::Acquire)?; 106 | let inner = &mut *self.inner.get(); 107 | inner.as_mut_ptr().write(func()); 108 | guard.poison = false; 109 | } 110 | Ok(unsafe { self.get_unchecked() }) 111 | } 112 | 113 | unsafe fn get_unchecked(&self) -> &T { 114 | (*self.inner.get()).assume_init_ref() 115 | } 116 | 117 | unsafe fn get_unchecked_mut(&mut self) -> &mut T { 118 | (*self.inner.get()).assume_init_mut() 119 | } 120 | 121 | fn unblock(&self, state: usize, order: Ordering) { 122 | self.state.swap(state, order); 123 | } 124 | 125 | pub fn into_inner(mut self) -> Option { 126 | self.take() 127 | } 128 | 129 | fn is_initialized(&self) -> bool { 130 | self.state.load(Ordering::Acquire) == COMPLETE 131 | } 132 | 133 | pub fn take(&mut self) -> Option { 134 | if self.is_initialized() { 135 | self.state = AtomicUsize::new(IMCOMPLETE); 136 | unsafe { Some((*self.inner.get()).assume_init_read()) } 137 | } else { 138 | None 139 | } 140 | } 141 | } 142 | 143 | impl Drop for OnceLock { 144 | fn drop(&mut self) { 145 | if self.is_initialized() { 146 | unsafe { (*self.inner.get()).assume_init_drop() } 147 | } 148 | } 149 | } 150 | 151 | impl<'a, T: 'a> Drop for OnceLockGuard<'a, T> { 152 | fn drop(&mut self) { 153 | let state = if self.poison { POISONED } else { COMPLETE }; 154 | self.oncecell.unblock(state, Ordering::AcqRel); 155 | } 156 | } 157 | 158 | pub struct LazyLock T> { 159 | cell: OnceLock, 160 | init: Cell>, 161 | } 162 | 163 | unsafe impl Sync for LazyLock where OnceLock: Sync {} 164 | // auto-derived Send impl is OK. 165 | 166 | impl LazyLock { 167 | pub const fn new(init: F) -> Self { 168 | Self { 169 | cell: OnceLock::new(), 170 | init: Cell::new(Some(init)), 171 | } 172 | } 173 | } 174 | 175 | impl T> LazyLock { 176 | pub fn force(this: &LazyLock) -> &T { 177 | this.cell.get_or_init(|| match this.init.take() { 178 | Some(f) => f(), 179 | None => panic!("Lazy instance has previously been poisoned"), 180 | }) 181 | } 182 | } 183 | 184 | impl T> Deref for LazyLock { 185 | type Target = T; 186 | fn deref(&self) -> &Self::Target { 187 | LazyLock::force(self) 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /src/kernel/console.rs: -------------------------------------------------------------------------------- 1 | // Console input and output, to the uart. 2 | // Reads are line at a time. 3 | // Implements special input characters: 4 | // newline -- end of line 5 | // control-h -- backspace 6 | // control-u -- kill line 7 | // control-d -- end of line 8 | // control-p -- print process list 9 | 10 | use crate::error::{Error::*, Result}; 11 | use crate::file::{Device, Major, DEVSW}; 12 | use crate::proc::{dump, either_copyin, either_copyout, sleep, wakeup, Cpus}; 13 | use crate::spinlock::Mutex; 14 | use crate::uart; 15 | use crate::vm::VirtAddr; 16 | use core::num::Wrapping; 17 | 18 | pub static CONS: Mutex = Mutex::new(Cons::new(), "cons"); 19 | 20 | const BS: u8 = 0x08; 21 | 22 | // Control-x 23 | const fn ctrl(x: u8) -> u8 { 24 | x - b'@' 25 | } 26 | 27 | const INPUT_BUF_SIZE: usize = 128; 28 | pub struct Cons { 29 | buf: [u8; INPUT_BUF_SIZE], 30 | r: Wrapping, // Read index 31 | w: Wrapping, // Write index 32 | e: Wrapping, // Edit index 33 | } 34 | 35 | impl Cons { 36 | const fn new() -> Cons { 37 | Cons { 38 | buf: [0; INPUT_BUF_SIZE], 39 | r: Wrapping(0), 40 | w: Wrapping(0), 41 | e: Wrapping(0), 42 | } 43 | } 44 | } 45 | 46 | impl Device for Mutex { 47 | // 48 | // user read()s from the console go here. 49 | // copy (up to) a whole input line to dst. 50 | // 51 | fn read(&self, mut dst: VirtAddr, mut n: usize) -> Result { 52 | let mut cons_guard = self.lock(); 53 | let p = Cpus::myproc().unwrap(); 54 | 55 | let target = n; 56 | while n > 0 { 57 | // wait until interrupt handler has put some 58 | // input into CONS.buf 59 | while cons_guard.r == cons_guard.w { 60 | if p.inner.lock().killed { 61 | return Err(Interrupted); 62 | } 63 | cons_guard = sleep(&cons_guard.r as *const _ as usize, cons_guard); 64 | } 65 | let c = cons_guard.buf[cons_guard.r.0 % INPUT_BUF_SIZE]; 66 | cons_guard.r += Wrapping(1); 67 | 68 | if c == ctrl(b'D') { 69 | // end of line 70 | if n < target { 71 | // Save ^D for nexst time, to make sure 72 | // caller gets a 0-bytes result. 73 | cons_guard.r -= Wrapping(1); 74 | } 75 | break; 76 | } 77 | 78 | // copy the input byte to the user-space buffer. 79 | either_copyout(dst, &c)?; 80 | 81 | dst += 1; 82 | n -= 1; 83 | 84 | if c == b'\n' { 85 | // a whole line has arrived, return to 86 | // the user-level read(). 87 | break; 88 | } 89 | } 90 | 91 | Ok(target - n) 92 | } 93 | 94 | // 95 | // user write()s to the console go here. 96 | // 97 | fn write(&self, src: VirtAddr, n: usize) -> Result { 98 | for i in 0..n { 99 | let mut c = 0; 100 | either_copyin(&mut c, src + i)?; 101 | putc(c) 102 | } 103 | Ok(n) 104 | } 105 | 106 | fn major(&self) -> Major { 107 | Major::Console 108 | } 109 | } 110 | 111 | impl Mutex { 112 | // 113 | // the console input interrupt handler. 114 | // CONS.intr() calls this for input character. 115 | // do erase/kill processing, append to cons.buf, 116 | // wake up CONS.read() if a whole line has arrived. 117 | // 118 | pub fn intr(&self, c: u8) { 119 | let mut cons_guard = self.lock(); 120 | match c { 121 | // Print process list 122 | m if m == ctrl(b'P') => dump(), 123 | // Kill line 124 | m if m == ctrl(b'U') => { 125 | while cons_guard.e != cons_guard.w 126 | && cons_guard.buf[(cons_guard.e - Wrapping(1)).0 % INPUT_BUF_SIZE] != b'\n' 127 | { 128 | cons_guard.e -= Wrapping(1); 129 | putc(ctrl(b'H')); 130 | } 131 | } 132 | // Backspace 133 | m if m == ctrl(b'H') | b'\x7f' => { 134 | if cons_guard.e != cons_guard.w { 135 | cons_guard.e -= Wrapping(1); 136 | putc(ctrl(b'H')); 137 | } 138 | } 139 | _ => { 140 | if c != 0 && (cons_guard.e - cons_guard.r).0 < INPUT_BUF_SIZE { 141 | let c = if c == b'\r' { b'\n' } else { c }; 142 | 143 | // echo back to the user 144 | putc(c); 145 | 146 | // store for consumption by CONS.read(). 147 | let e_idx = cons_guard.e.0 % INPUT_BUF_SIZE; 148 | cons_guard.buf[e_idx] = c; 149 | cons_guard.e += Wrapping(1); 150 | 151 | if c == b'\n' 152 | || c == ctrl(b'D') 153 | || (cons_guard.e - cons_guard.r).0 == INPUT_BUF_SIZE 154 | { 155 | // wake up CONS.read() if a whole line (or end of line) 156 | // has arrived 157 | cons_guard.w = cons_guard.e; 158 | wakeup(&cons_guard.r as *const _ as usize); 159 | } 160 | } 161 | } 162 | } 163 | } 164 | } 165 | 166 | pub fn init() { 167 | unsafe { uart::init() } 168 | DEVSW.set(Major::Console, &CONS).unwrap(); 169 | } 170 | 171 | // 172 | // send one character to the uart. 173 | // called by printf, and to echo input characters, 174 | // but not from write(). 175 | // 176 | pub fn putc(c: u8) { 177 | if c == ctrl(b'H') { 178 | uart::putc_sync(BS); 179 | uart::putc_sync(b' '); 180 | uart::putc_sync(BS); 181 | } else { 182 | uart::putc_sync(c); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/user/bin/sh.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | extern crate alloc; 3 | use alloc::{ 4 | string::{String, ToString}, 5 | vec::Vec, 6 | }; 7 | use ulib::{ 8 | env, eprint, eprintln, 9 | fs::{File, OpenOptions}, 10 | io::{BufRead, BufReader}, 11 | path::Path, 12 | print, println, 13 | process::{Child, Command, Stdio}, 14 | stdio::stdin, 15 | sys, 16 | }; 17 | 18 | fn main() { 19 | // Ensure that three file descriptors are open 20 | while let Ok(fd) = OpenOptions::new() 21 | .read(true) 22 | .write(true) 23 | .open("/dev/console") 24 | { 25 | if fd.get_fd() > 2 { 26 | drop(fd); 27 | break; 28 | } 29 | } 30 | set_path_fron_etc_paths().unwrap(); 31 | 32 | // read and run input commands. 33 | 'main: loop { 34 | print!("$ "); 35 | 36 | let mut input = String::new(); 37 | stdin().read_line(&mut input).unwrap(); 38 | 39 | let mut commands = input.trim().split('|').enumerate().peekable(); 40 | let mut previous_command: Option = None; 41 | 42 | while let Some((num, command)) = commands.next() { 43 | let mut parts = command.split_whitespace(); 44 | let Some(command) = parts.next() else { 45 | continue 'main; 46 | }; 47 | let mut args = parts.peekable(); 48 | 49 | match command { 50 | "cd" => { 51 | if num == 0 { 52 | // chdir must be called by the parent. if in child do nothing any more. 53 | let new_dir = args.peek().map_or("/", |x| *x); 54 | if let Err(e) = env::set_current_dir(new_dir) { 55 | eprintln!("{}", e); 56 | } 57 | } 58 | continue 'main; 59 | } 60 | "export" => { 61 | if args.peek().map_or(true, |x| *x == "-p") { 62 | for (key, value) in env::vars() { 63 | println!("{}: {}", key, value); 64 | } 65 | } else { 66 | for arg in args { 67 | if let Some((key, value)) = arg.split_once('=') { 68 | if let Err(e) = env::set_var(key, value) { 69 | eprintln!("{}", e); 70 | } 71 | } else { 72 | eprintln!("export: invalid argument: {}", arg); 73 | } 74 | } 75 | } 76 | continue 'main; 77 | } 78 | "exit" => return, 79 | command => { 80 | let stdin = previous_command 81 | .map_or(Stdio::Inherit, |pc| Stdio::from(pc.stdout.unwrap())); 82 | let mut stdout = if commands.peek().is_some() { 83 | Stdio::MakePipe 84 | } else { 85 | Stdio::Inherit 86 | }; 87 | 88 | let rawstring; 89 | let mut file_name = ""; 90 | let mut overwrite = true; 91 | let mut append = false; 92 | let mut arg_vec = Vec::new(); 93 | while let Some(arg) = args.next_if(|s| !s.contains('>')) { 94 | arg_vec.push(arg); 95 | } 96 | if let Some(redir) = args.peek() { 97 | if redir.contains(">>") { 98 | overwrite = false; 99 | append = true; 100 | } 101 | rawstring = args.collect::>().concat(); 102 | let splited = rawstring.split('>'); 103 | for (i, e) in splited.enumerate() { 104 | if e.is_empty() { 105 | continue; 106 | } 107 | if i == 0 { 108 | arg_vec.push(e); 109 | } else { 110 | file_name = e; 111 | } 112 | } 113 | assert!(!file_name.is_empty(), "redirect"); 114 | stdout = Stdio::Fd( 115 | OpenOptions::new() 116 | .create(true) 117 | .write(true) 118 | .truncate(overwrite) 119 | .append(append) 120 | .open(file_name) 121 | .unwrap(), 122 | ); 123 | } 124 | 125 | match Command::new(command) 126 | .args(arg_vec) 127 | .stdin(stdin) 128 | .stdout(stdout) 129 | .spawn() 130 | { 131 | Ok(child) => previous_command = Some(child), 132 | Err(e) => { 133 | previous_command = None; 134 | eprintln!("{}", e); 135 | } 136 | } 137 | } 138 | } 139 | } 140 | 141 | if let Some(mut final_command) = previous_command { 142 | final_command.wait().unwrap(); 143 | } 144 | } 145 | } 146 | 147 | fn set_path_fron_etc_paths() -> sys::Result<()> { 148 | let path_file = "/etc/paths"; 149 | if Path::new(path_file).exists() { 150 | let file = BufReader::new(File::open(path_file)?); 151 | let mut paths: Vec = env::var("PATH") 152 | .unwrap_or_default() 153 | .split(':') 154 | .filter(|s| !s.is_empty()) 155 | .map(String::from) 156 | .collect(); 157 | for line in file.lines() { 158 | if let Ok(path) = line { 159 | paths.push(path); 160 | } 161 | } 162 | let new_path = paths.join(":"); 163 | env::set_var("PATH", &new_path); 164 | } 165 | Ok(()) 166 | } 167 | -------------------------------------------------------------------------------- /src/kernel/uart.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | console::CONS, 3 | memlayout::UART0, 4 | printf::PR, 5 | proc::{self, Cpus}, 6 | spinlock::Mutex, 7 | }; 8 | use core::{num::Wrapping, ptr, sync::atomic::Ordering}; 9 | use Register::*; 10 | 11 | #[allow(clippy::identity_op)] 12 | const LCR_EIGHT_BITS: u8 = 3 << 0; 13 | const LCR_BAUD_LATCH: u8 = 1 << 7; 14 | const IER_RX_ENABLE: u8 = 1 << 0; 15 | const IER_TX_ENABLE: u8 = 1 << 1; 16 | const FCR_FIFO_ENABLE: u8 = 1 << 0; 17 | const FCR_FIFO_CLEAR: u8 = 3 << 1; 18 | const _LSR_RX_READY: u8 = 1 << 0; 19 | const LSR_TX_IDLE: u8 = 1 << 5; 20 | 21 | // UART driver object 22 | pub static UART: Mutex = Mutex::new(Uart::new(UART0), "uart"); 23 | 24 | const UART_TX_BUF_SIZE: usize = 32; 25 | pub struct Uart { 26 | base_address: usize, 27 | tx_buf: [u8; UART_TX_BUF_SIZE], // the transmit output buffer 28 | tx_w: Wrapping, // write next to tx_buf[tx_w % UART_TX_BUF_SIZE] 29 | tx_r: Wrapping, // read next from tx_buf[tx_r % UART_TX_BUF_SIZE] 30 | } 31 | 32 | enum Register { 33 | Rbr, 34 | Thr, 35 | Ier, 36 | _IIR, 37 | Fcr, 38 | Lcr, 39 | Lsr, 40 | } 41 | 42 | impl Register { 43 | fn addr(self, base_addr: usize) -> *mut u8 { 44 | match self { 45 | Self::Rbr | Self::Thr => base_addr as *mut u8, 46 | Self::Ier => (base_addr + 1) as *mut u8, 47 | Self::_IIR | Self::Fcr => (base_addr + 2) as *mut u8, 48 | Self::Lcr => (base_addr + 3) as *mut u8, 49 | Self::Lsr => (base_addr + 5) as *mut u8, 50 | } 51 | } 52 | } 53 | 54 | impl Uart { 55 | pub const fn new(base_address: usize) -> Self { 56 | Self { 57 | base_address, 58 | tx_buf: [0; UART_TX_BUF_SIZE], 59 | tx_w: Wrapping(0), 60 | tx_r: Wrapping(0), 61 | } 62 | } 63 | 64 | pub fn init(&mut self) { 65 | // diable interrupts. 66 | self.write(Ier, 0x00); 67 | 68 | // special mode to set baud rate. 69 | self.write(Lcr, LCR_BAUD_LATCH); 70 | 71 | // LSB for baud rate of 38.4K. 72 | self.write(Rbr, 0x03); 73 | 74 | // MSB for baud rate of 38.4K 75 | self.write(Ier, 0x00); 76 | 77 | // leave set-baud mode, 78 | // and set worf length to 8 bits, no parity. 79 | self.write(Lcr, LCR_EIGHT_BITS); 80 | 81 | // reset and enable FIFOs 82 | self.write(Fcr, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR); 83 | 84 | // enable transmit and receive interrupts 85 | self.write(Ier, IER_TX_ENABLE | IER_RX_ENABLE); 86 | } 87 | 88 | fn read(&self, reg: Register) -> u8 { 89 | unsafe { ptr::read_volatile(reg.addr(self.base_address)) } 90 | } 91 | 92 | fn write(&mut self, reg: Register, val: u8) { 93 | unsafe { ptr::write_volatile(reg.addr(self.base_address), val) } 94 | } 95 | 96 | // if the UART is idle, and a character is waiting 97 | // in the transmit buffer, send it. 98 | // caller must hold "uart" lock. 99 | // called from both top- and bottom-half. 100 | fn start(&mut self) { 101 | loop { 102 | if self.tx_w == self.tx_r { 103 | // trasnmit buffer is empty. 104 | break; 105 | } 106 | 107 | if self.read(Lsr) & LSR_TX_IDLE == 0 { 108 | // the UART transmit holding register is full, 109 | // so we cannot give it another byte. 110 | // it will interrupt when it's ready for a new byte. 111 | break; 112 | } 113 | 114 | let c = *self.tx_buf.get(self.tx_r.0 % UART_TX_BUF_SIZE).unwrap(); 115 | self.tx_r += Wrapping(1); 116 | 117 | // maybe putc() is waiting for space in the buffer 118 | proc::wakeup(&self.tx_r as *const _ as usize); 119 | self.write(Thr, c); 120 | } 121 | } 122 | } 123 | 124 | impl Mutex { 125 | // add a character to the output buffer and tell the 126 | // UART to start sending if it isn't already. 127 | // blocks if the output buffer is full. 128 | // because it may block, it can't be called 129 | // from interrupts; it's only suitable for use 130 | // by write() 131 | pub fn putc(&self, c: u8) { 132 | let mut uart_guard = self.lock(); 133 | 134 | #[allow(clippy::empty_loop)] 135 | if PR.panicked().load(Ordering::Relaxed) { 136 | loop {} 137 | } 138 | 139 | loop { 140 | if uart_guard.tx_w == uart_guard.tx_r + Wrapping(UART_TX_BUF_SIZE) { 141 | // buffer is full. 142 | // wait for self.start() to open up space in the buffer 143 | uart_guard = proc::sleep(&uart_guard.tx_r as *const _ as usize, uart_guard); 144 | } else { 145 | let write_idx = uart_guard.tx_w.0 % UART_TX_BUF_SIZE; 146 | *uart_guard.tx_buf.get_mut(write_idx).unwrap() = c; 147 | uart_guard.tx_w += Wrapping(1); 148 | uart_guard.start(); 149 | break; 150 | } 151 | } 152 | } 153 | 154 | // read one input character from the UART. 155 | // return Option 156 | fn getc(&self) -> Option { 157 | let uart = unsafe { self.get_mut() }; 158 | if uart.read(Lsr) & 0x01 != 0 { 159 | Some(uart.read(Rbr)) 160 | } else { 161 | None 162 | } 163 | } 164 | 165 | // handle a uart interrupt, raised because input has 166 | // arraived, or the uart is ready more output, or 167 | // both. called from trap.c 168 | pub fn intr(&self) { 169 | // read and process incoming characters 170 | while let Some(c) = self.getc() { 171 | CONS.intr(c); 172 | } 173 | 174 | // send buffered characters. 175 | self.lock().start(); 176 | } 177 | } 178 | 179 | pub(crate) unsafe fn init() { 180 | UART.get_mut().init(); 181 | } 182 | 183 | // alternate version of putc that doesn't 184 | // use interrupts, for use by kernel printf() and 185 | // to echo characters. it spins wating for the uart's 186 | // output register to be empty. 187 | pub fn putc_sync(c: u8) { 188 | let _intr_lock = Cpus::lock_mycpu("putcwithoutspin"); 189 | let uart = unsafe { UART.get_mut() }; 190 | 191 | #[allow(clippy::empty_loop)] 192 | if PR.panicked().load(Ordering::Relaxed) { 193 | loop {} 194 | } 195 | 196 | // wait for Transmit Holding Empty to be set in LSR. 197 | loop { 198 | if uart.read(Lsr) & LSR_TX_IDLE != 0 { 199 | break; 200 | } 201 | } 202 | uart.write(Thr, c); 203 | } 204 | -------------------------------------------------------------------------------- /src/kernel/log.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | bio::{BufGuard, BCACHE}, 3 | fs::{BSIZE, SB}, 4 | param::{LOGSIZE, MAXOPBLOCKS, ROOTDEV}, 5 | proc, 6 | spinlock::Mutex, 7 | sync::LazyLock, 8 | }; 9 | use core::ops::{Deref, DerefMut}; 10 | 11 | // Simple logging that allows concurrent FS system calls. 12 | // 13 | // A log transaction contains the updates of multiple FS system 14 | // calls. The logging system only commits when there are 15 | // no FS system calls active. Thus there is never 16 | // any reasoning required about whether a commit might 17 | // write an uncommitted system call's updates to disk. 18 | // 19 | // A system call should call begin_op()/end_op() to mark 20 | // its start and end. Usually begin_op() just increments 21 | // the count of in-progress FS system calls and returns. 22 | // But if it thinks the log is close to running out, it 23 | // sleeps until the last outstanding end_op() commits. 24 | // 25 | // The log is a physical re-do log containing disk blocks. 26 | // The on-disk log format: 27 | // header block, containing block #s for block A, B, C, ... 28 | // block A 29 | // block B 30 | // block C 31 | // ... 32 | // Log appends are synchronous. 33 | 34 | pub static LOG: LazyLock> = LazyLock::new(|| Mutex::new(Log::new(ROOTDEV), "log")); 35 | 36 | // Contents of the header block, used for both the on-disk header block 37 | // and to keep track in memory of logged block# before commit. 38 | #[repr(C)] 39 | #[derive(Default, Debug, Clone, Copy)] 40 | struct LogHeader { 41 | n: u32, 42 | block: [u32; LOGSIZE], 43 | } 44 | 45 | pub struct Log { 46 | start: u32, 47 | size: u32, 48 | dev: u32, 49 | outstanding: u32, 50 | committing: bool, 51 | lh: LogHeader, 52 | } 53 | 54 | impl Log { 55 | fn new(dev: u32) -> Self { 56 | let sb = SB.get().unwrap(); 57 | let mut log = Self { 58 | start: sb.logstart, 59 | size: sb.nlog, 60 | dev, 61 | outstanding: 0, 62 | committing: false, 63 | lh: LogHeader { 64 | n: 0, 65 | block: [0; LOGSIZE], 66 | }, 67 | }; 68 | log.recover(); 69 | log 70 | } 71 | 72 | // Read the log header from disk into in-memory log header 73 | fn read_head(&mut self) { 74 | let buf = BCACHE.read(self.dev, self.start); 75 | let lh = buf.align_to::().get(0).unwrap(); 76 | self.lh = *lh; 77 | } 78 | 79 | // Write in-memory log header to disk. 80 | // This is the true point at which the 81 | // current transaction commits. 82 | fn write_head(&self) { 83 | let mut buf = BCACHE.read(self.dev, self.start); 84 | let hb = buf.align_to_mut::().get_mut(0).unwrap(); 85 | *hb = self.lh; 86 | buf.write(); 87 | } 88 | 89 | fn recover(&mut self) { 90 | self.read_head(); 91 | self.install_trans(true); // if committed, copy from log to disk 92 | self.lh.n = 0; 93 | self.write_head(); // clear the log 94 | } 95 | 96 | // Copy comitted blocks from log to their home location 97 | fn install_trans(&self, recovering: bool) { 98 | for tail in 0..self.lh.n { 99 | let lbuf = BCACHE.read(self.dev, self.start + tail + 1); // read log block 100 | let mut dbuf = BCACHE.read(self.dev, self.lh.block[tail as usize]); // read dst 101 | dbuf.copy_from_slice(&lbuf); // copy block to dst 102 | dbuf.write(); // write dst to disk 103 | if !recovering { 104 | dbuf.unpin(); 105 | } 106 | } 107 | } 108 | 109 | // Copy modified blocks from cache to log. 110 | fn write_log(&mut self) { 111 | for tail in 0..self.lh.n { 112 | let mut to = BCACHE.read(self.dev, self.start + tail + 1); // log block 113 | let from = BCACHE.read(self.dev, self.lh.block[tail as usize]); // cache block 114 | to.copy_from_slice(from.deref().deref()); 115 | to.write(); // write the log 116 | } 117 | } 118 | 119 | fn commit(&mut self) { 120 | if self.lh.n > 0 { 121 | self.write_log(); // Write modified blocks from cache to log 122 | self.write_head(); // Wrtie header to disk -- the real commit 123 | self.install_trans(false); // Now install writes to home locations 124 | self.lh.n = 0; 125 | self.write_head(); 126 | } 127 | } 128 | } 129 | 130 | impl Mutex { 131 | pub fn init(&self) { 132 | // SyncLazy initialization 133 | assert!( 134 | core::mem::size_of::() < BSIZE, 135 | "initlog: too big log header" 136 | ); 137 | } 138 | // called at the start of each FS system call. 139 | pub fn begin_op(&self) { 140 | let mut guard = self.lock(); 141 | loop { 142 | if guard.committing 143 | || (guard.lh.n as usize + (guard.outstanding + 1) as usize * MAXOPBLOCKS) > LOGSIZE 144 | // this op might exhaust log space; wait for commit. 145 | { 146 | guard = proc::sleep(guard.deref() as *const _ as usize, guard); 147 | } else { 148 | guard.outstanding += 1; 149 | break; 150 | } 151 | } 152 | } 153 | 154 | // called at the end of each FS system call. 155 | // commits if this was the last outstanding operation. 156 | pub fn end_op(&self) { 157 | let mut log: Option<*mut Log> = None; 158 | 159 | { 160 | let mut guard = self.lock(); 161 | guard.outstanding -= 1; 162 | if guard.committing { 163 | panic!("log.commiting"); 164 | } 165 | if guard.outstanding == 0 { 166 | log.replace(guard.deref_mut() as *mut _); 167 | guard.committing = true; 168 | } else { 169 | // begin_op() may be waiting for log space, 170 | // and decrementing log.outstanding has decreased 171 | // the amount of reserved space. 172 | proc::wakeup(guard.deref() as *const _ as usize); 173 | } 174 | } 175 | 176 | if let Some(log) = log { 177 | // call commit w/o holding locks, since not allowed 178 | // to sleep with locks. 179 | unsafe { 180 | log.as_mut().unwrap().commit(); 181 | } 182 | let mut guard = self.lock(); 183 | guard.committing = false; 184 | proc::wakeup(guard.deref() as *const _ as usize); 185 | } 186 | } 187 | 188 | // Caller has modified b->data and is done with the buffer. 189 | // Record the block number and pin in the cache by increasing refcnt. 190 | // commit()/write() will do the disk write. 191 | // 192 | // LOG.write() replaces BudGuard.write(); a typical use is: 193 | // bp = BCACHE.read(); 194 | // modify bp.data[] 195 | // LOG.write(bp) 196 | pub fn write(&self, b: BufGuard) { 197 | let mut guard = self.lock(); 198 | if guard.lh.n as usize >= LOGSIZE || guard.lh.n >= guard.size - 1 { 199 | panic!("too big a transaction"); 200 | } 201 | if guard.outstanding < 1 { 202 | panic!("LOG.write outside of trans"); 203 | } 204 | 205 | for i in 0..guard.lh.n { 206 | if guard.lh.block[i as usize] == b.blockno() { 207 | // log absorption 208 | return; 209 | } 210 | } 211 | let n = guard.lh.n as usize; 212 | guard.lh.block[n] = b.blockno(); 213 | b.pin(); 214 | guard.lh.n += 1; 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /src/kernel/trap.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | kernelvec::kernelvec, 3 | memlayout::{STACK_PAGE_NUM, TRAMPOLINE, UART0_IRQ, VIRTIO0_IRQ}, 4 | plic, 5 | proc::{self, Cpus, ProcState}, 6 | riscv::{ 7 | registers::{scause::*, *}, 8 | *, 9 | }, 10 | spinlock::Mutex, 11 | syscall::syscall, 12 | trampoline::trampoline, 13 | uart::UART, 14 | virtio_disk::DISK, 15 | vm::Addr, 16 | }; 17 | 18 | extern "C" { 19 | fn uservec(); 20 | fn userret(); 21 | } 22 | 23 | #[derive(PartialEq)] 24 | pub enum Intr { 25 | Timer, 26 | Device, 27 | } 28 | 29 | pub static TICKS: Mutex = Mutex::new(0, "time"); 30 | 31 | // set up to take exceptions and traps while in the kernel. 32 | #[no_mangle] 33 | pub fn inithart() { 34 | unsafe { 35 | stvec::write(kernelvec as usize, stvec::TrapMode::Direct); 36 | } 37 | } 38 | 39 | // 40 | // handle an interrupt, exception, or system call from user space. 41 | // called from trampoline.rs 42 | // 43 | #[no_mangle] 44 | pub extern "C" fn usertrap() -> ! { 45 | assert!( 46 | sstatus::read().spp() == sstatus::SPP::User, 47 | "usertrap: not from user mode" 48 | ); 49 | assert!(!intr_get(), "kerneltrap: interrupts enabled"); 50 | 51 | // send interrupts and exceptions to kerneltrap(). 52 | // since we're now in the kernel. 53 | unsafe { 54 | stvec::write(kernelvec as usize, stvec::TrapMode::Direct); 55 | } 56 | 57 | let p = Cpus::myproc().unwrap(); 58 | let data = unsafe { &mut (*p.data.get()) }; 59 | let tf = data.trapframe.as_mut().unwrap(); 60 | 61 | // save user program counter 62 | tf.epc = sepc::read(); 63 | 64 | let mut which_dev = None; 65 | match scause::read().cause() { 66 | Trap::Exception(Exception::UserEnvCall) => { 67 | // system call 68 | 69 | if p.inner.lock().killed { 70 | proc::exit(-1) 71 | } 72 | 73 | // sepc points to the ecall instruction, 74 | // but we want to return to the next instruction. 75 | tf.epc += 4; 76 | 77 | // an interrupt will change sstatus &c registers, 78 | // so don't enable until done with those registers. 79 | intr_on(); 80 | 81 | syscall(); 82 | } 83 | Trap::Interrupt(intr) 84 | if { 85 | which_dev = devintr(intr); 86 | which_dev.is_some() 87 | } => {} 88 | _ => { 89 | let mut inner = p.inner.lock(); 90 | println!( 91 | "usertrap(): unexcepted scause {:?}, pid={:?}", 92 | scause::read().cause(), 93 | inner.pid 94 | ); 95 | println!( 96 | " sepc={:X}, stval={:X}", 97 | sepc::read(), 98 | stval::read() 99 | ); 100 | inner.killed = true; 101 | } 102 | } 103 | 104 | if p.inner.lock().killed { 105 | proc::exit(-1) 106 | } 107 | 108 | // give up the CPU if this is a timer interrupt. 109 | if Some(Intr::Timer) == which_dev { 110 | proc::yielding() 111 | } 112 | 113 | unsafe { usertrap_ret() } 114 | } 115 | 116 | // 117 | // return to user space 118 | // 119 | #[no_mangle] 120 | pub unsafe extern "C" fn usertrap_ret() -> ! { 121 | let p = Cpus::myproc().unwrap(); 122 | 123 | // we're about to switch the destination of traps from 124 | // kerneltrap() to usertrap(), so turn off interrupts until 125 | // we're back in user space, where usertrap() is correct. 126 | intr_off(); 127 | 128 | // send syscalls, interrupts, and exceptions to trampoline.rs 129 | stvec::write( 130 | TRAMPOLINE + (uservec as usize - trampoline as usize), 131 | stvec::TrapMode::Direct, 132 | ); 133 | 134 | let data = p.data_mut(); //&mut *p.data.get(); 135 | 136 | // set up trapframe values that uservec will need when 137 | // the process next re-enters the kernel. 138 | let tf = data.trapframe.as_mut().unwrap(); 139 | tf.kernel_satp = satp::read().bits(); 140 | tf.kernel_sp = data.kstack.into_usize() + PGSIZE * STACK_PAGE_NUM; 141 | tf.kernel_trap = usertrap as usize; 142 | tf.kernel_hartid = Cpus::cpu_id(); 143 | 144 | // set up the registers that trampoline.rs's sret will use 145 | // to get to user space. 146 | 147 | // set S Previous Priviledge mode to User. 148 | sstatus::set_spp(sstatus::SPP::User); // clear SPP to 0 for user mode. 149 | sstatus::set_spie(); // enable interrupts in user mode. 150 | 151 | // set S Exception Program Counter Counter to the saved user pc. 152 | sepc::write(tf.epc); 153 | 154 | // tell trampoline.rs the user page table to switch to. 155 | let satp = data.uvm.as_ref().unwrap().as_satp(); 156 | 157 | // jump to trampoline.rs at the top of memory, witch 158 | // switches to the user page table, restores user registers, 159 | // and switches to user mode with sret. 160 | 161 | let fn_0: usize = TRAMPOLINE + (userret as usize - trampoline as usize); 162 | let fn_0: extern "C" fn(usize) -> ! = core::mem::transmute(fn_0); 163 | fn_0(satp) 164 | } 165 | 166 | // interrupts and exceptions from kernel code go here via kernelvec, 167 | // on whatever the current kernel stack is. 168 | #[no_mangle] 169 | pub extern "C" fn kerneltrap() { 170 | let which_dev; 171 | let sepc = sepc::read(); 172 | let sstatus = sstatus::read(); 173 | let scause = scause::read(); 174 | 175 | assert!( 176 | sstatus.spp() == sstatus::SPP::Supervisor, 177 | "not from supervisor mode" 178 | ); 179 | assert!(!intr_get(), "kerneltrap: interrupts enabled"); 180 | 181 | match scause.cause() { 182 | Trap::Interrupt(intr) 183 | if { 184 | which_dev = devintr(intr); 185 | which_dev.is_some() 186 | } => {} 187 | _ => { 188 | panic!( 189 | "kerneltrap: scause = {:?}, sepc = {:x}, stval = {:x}", 190 | scause.cause(), 191 | sepc::read(), 192 | stval::read() 193 | ); 194 | } 195 | } 196 | 197 | // give up the CPU if this is a timer interrupt. 198 | if Some(Intr::Timer) == which_dev { 199 | if let Some(p) = Cpus::myproc() { 200 | if p.inner.lock().state == ProcState::RUNNING { 201 | proc::yielding() 202 | } 203 | } 204 | } 205 | 206 | // the yielding() may have caused some traps to occur. 207 | // so restore trap registers for use by kernelvec.rs's sepc instruction. 208 | sepc::write(sepc); 209 | sstatus.restore(); 210 | } 211 | 212 | fn clockintr() { 213 | let mut ticks = TICKS.lock(); 214 | *ticks += 1; 215 | proc::wakeup(&(*ticks) as *const _ as usize) 216 | } 217 | 218 | // check if it's an external interrupt or software interrupt, 219 | // and handle it. 220 | // returns Option 221 | // devintr() is safe because it is only called in the non-interruptable 222 | // part of trap.rs. 223 | fn devintr(intr: Interrupt) -> Option { 224 | match intr { 225 | Interrupt::SupervisorExternal => { 226 | // this is a supervisor external interrupt, via PLIC. 227 | 228 | // irq indicates which device interrupted. 229 | let irq = plic::claim(); 230 | 231 | if let Some(irq) = irq { 232 | match irq { 233 | UART0_IRQ => UART.intr(), 234 | VIRTIO0_IRQ => DISK.intr(), 235 | _ => println!("unexpected interrupt irq={}", irq), 236 | } 237 | // the PLIC allows each device to raise at most one 238 | // interrupt at a time; tell the PLIC the device is 239 | // now allowed to interrupt again. 240 | plic::complete(irq); 241 | } 242 | 243 | Some(Intr::Device) 244 | } 245 | Interrupt::SupervisorSoft => { 246 | // software interrupt from a machine-mode timer interrupt, 247 | // forwarded by timervec in kernelvec.rs. 248 | if unsafe { Cpus::cpu_id() == 0 } { 249 | clockintr(); 250 | } 251 | 252 | // acknowledge the software interrupt by clearing 253 | // the SSIP bit in sip. 254 | unsafe { 255 | sip::clear_ssoft(); 256 | } 257 | 258 | Some(Intr::Timer) 259 | } 260 | _ => None, 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/kernel/exec.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | elf::{self, ElfHdr, ProgHdr}, 3 | error::{Error::*, Result}, 4 | fs::{IData, Path}, 5 | log::LOG, 6 | memlayout::STACK_PAGE_NUM, 7 | param::MAXARG, 8 | proc::Cpus, 9 | riscv::{pgroundup, pteflags, PGSIZE}, 10 | sleeplock::SleepLockGuard, 11 | vm::{Addr, UVAddr, Uvm, VirtAddr}, 12 | }; 13 | use alloc::string::{String, ToString}; 14 | use core::mem::size_of; 15 | 16 | pub const fn flags2perm(flags: u32) -> usize { 17 | let mut perm = 0; 18 | if flags & 0x1 != 0 { 19 | perm = pteflags::PTE_X; 20 | } 21 | if flags & 0x2 != 0 { 22 | perm |= pteflags::PTE_W; 23 | } 24 | perm 25 | } 26 | 27 | // Load a program segment into pagetable at virtual address va. 28 | // va must be page-aligned 29 | // and the pages from va to va+sz must already be mapped. 30 | // Returns Ok(()) on success, Err(_) on failure. 31 | impl Uvm { 32 | pub fn loadseg( 33 | &mut self, 34 | va: UVAddr, 35 | ip_guard: &mut SleepLockGuard, 36 | offset: usize, 37 | sz: usize, 38 | ) -> Result<()> { 39 | if !va.is_aligned() { 40 | panic!("loadseg(): va must be aligned."); 41 | } 42 | 43 | let mut i: usize = 0; 44 | 45 | while i < sz { 46 | let pa = self.walkaddr(va + i)?; 47 | let n = if sz - i < PGSIZE { sz - i } else { PGSIZE }; 48 | ip_guard.read(From::from(pa), (offset + i) as u32, n)?; 49 | i += PGSIZE; 50 | } 51 | Ok(()) 52 | } 53 | } 54 | 55 | #[allow(clippy::redundant_closure_call)] 56 | #[no_mangle] 57 | pub fn exec( 58 | path: &Path, 59 | argv: [Option; MAXARG], 60 | envp: [Option; MAXARG], 61 | ) -> Result { 62 | let p = Cpus::myproc().unwrap(); 63 | 64 | let mut uvm: Option = None; 65 | let mut ustack = [0usize; MAXARG * 2]; // &str = [usize, usize] 66 | let mut elf: ElfHdr = Default::default(); 67 | let mut res; 68 | let mut sz = 0; 69 | 70 | { 71 | LOG.begin_op(); 72 | let mut load = || -> Result { 73 | let (_, ip) = path.namei()?; 74 | let mut ip_guard = ip.lock(); 75 | 76 | // Load & Check ELF header 77 | ip_guard.read( 78 | VirtAddr::Kernel(&mut elf as *mut _ as usize), 79 | 0, 80 | size_of::(), 81 | )?; 82 | if elf.e_ident[elf::EI_MAG0] != elf::ELFMAG0 83 | || elf.e_ident[elf::EI_MAG1] != elf::ELFMAG1 84 | || elf.e_ident[elf::EI_MAG2] != elf::ELFMAG2 85 | || elf.e_ident[elf::EI_MAG3] != elf::ELFMAG3 86 | { 87 | return Err(ExecFileFormatError); 88 | } 89 | 90 | uvm = Some(p.uvmcreate()?); 91 | 92 | // Load program into memory. 93 | let mut phdr: ProgHdr = Default::default(); 94 | let mut off = elf.e_phoff; 95 | for _ in 0..elf.e_phnum { 96 | ip_guard.read( 97 | VirtAddr::Kernel(&mut phdr as *mut _ as usize), 98 | off as u32, 99 | size_of::(), 100 | )?; 101 | if phdr.p_type != elf::PT_LOAD { 102 | continue; 103 | } 104 | if phdr.p_msize < phdr.p_fsize { 105 | return Err(ExecFileFormatError); 106 | } 107 | if phdr.p_vaddr + phdr.p_msize < phdr.p_msize { 108 | return Err(ExecFileFormatError); 109 | } 110 | if phdr.p_vaddr % PGSIZE != 0 { 111 | return Err(ExecFileFormatError); 112 | } 113 | sz = uvm.as_mut().unwrap().alloc( 114 | sz, 115 | phdr.p_vaddr + phdr.p_msize, 116 | flags2perm(phdr.p_flags), 117 | )?; 118 | uvm.as_mut().unwrap().loadseg( 119 | From::from(phdr.p_vaddr), 120 | &mut ip_guard, 121 | phdr.p_offset, 122 | phdr.p_fsize, 123 | )?; 124 | off += size_of::(); 125 | } 126 | Ok(0) 127 | }; 128 | res = load(); 129 | LOG.end_op(); 130 | } 131 | 132 | let exec = || -> Result { 133 | res?; 134 | let p = Cpus::myproc().unwrap(); 135 | let proc_data = p.data_mut(); 136 | 137 | let tf = proc_data.trapframe.as_mut().unwrap(); 138 | 139 | let oldsz = proc_data.sz; 140 | 141 | // Allocate some pages at the next page boundary. 142 | // Make the first inaccessible as a stack guard. 143 | // Use the next STACK_PAGE_NUM pages as the user stack. 144 | let pgnum = 1 + STACK_PAGE_NUM; 145 | sz = pgroundup(sz); 146 | sz = uvm 147 | .as_mut() 148 | .unwrap() 149 | .alloc(sz, sz + pgnum * PGSIZE, pteflags::PTE_W)?; 150 | uvm.as_mut().unwrap().clear(From::from(sz - pgnum * PGSIZE)); 151 | let mut sp: UVAddr = UVAddr::from(sz); 152 | let stackbase: UVAddr = sp - PGSIZE * STACK_PAGE_NUM; 153 | 154 | // Push argument strings, prepare rest of stack in ustack. 155 | let mut argc = 0; 156 | for arg in argv.into_iter().take_while(|e| e.is_some()).flatten() { 157 | sp -= arg.len(); 158 | sp -= sp.into_usize() % 16; // riscv sp must be 16-byte aligned 159 | if sp < stackbase { 160 | return Err(NoBufferSpace); 161 | } 162 | // copyout str to sp 163 | uvm.as_mut().unwrap().copyout(sp, arg.as_str())?; 164 | // now sp = *const str 165 | // make &str from sp and len, and store it in ustack[]. 166 | *ustack.get_mut(argc * 2).ok_or(ArgumentListTooLong)? = sp.into_usize(); 167 | *ustack.get_mut(argc * 2 + 1).ok_or(ArgumentListTooLong)? = arg.len(); 168 | argc += 1; 169 | } 170 | let mut envc = 0; 171 | for env in envp.into_iter() { 172 | match env { 173 | Some(env_str) => { 174 | sp -= env_str.len(); 175 | sp -= sp.into_usize() % 16; // riscv sp must be 16-byte aligned 176 | if sp < stackbase { 177 | return Err(NoBufferSpace); 178 | } 179 | // copyout str to sp 180 | uvm.as_mut().unwrap().copyout(sp, env_str.as_str())?; 181 | // now sp = *const str 182 | // make &str from sp and len, and store it in ustack[]. 183 | *ustack 184 | .get_mut((argc + envc) * 2) 185 | .ok_or(ArgumentListTooLong)? = sp.into_usize(); 186 | *ustack 187 | .get_mut((argc + envc) * 2 + 1) 188 | .ok_or(ArgumentListTooLong)? = env_str.len(); 189 | envc += 1; 190 | } 191 | None => {} 192 | } 193 | } 194 | // now &ustack = &[Option<&str>] 195 | 196 | // Push [&str] to stack. 197 | sp -= MAXARG * 2 * size_of::(); 198 | sp -= sp.into_usize() % 16; 199 | if sp < stackbase { 200 | return Err(NoBufferSpace); 201 | } 202 | uvm.as_mut().unwrap().copyout(sp, &ustack)?; 203 | // now sp = *const [Option<&str>] 204 | 205 | // make &[Option<&str>] from sp(=*const [Option<&str>]) and argc(= slice len) at stack 206 | let slice: [usize; 2] = [sp.into_usize(), MAXARG]; 207 | sp -= size_of::<[usize; 2]>(); 208 | sp -= sp.into_usize() % 16; 209 | if sp < stackbase { 210 | return Err(NoBufferSpace); 211 | } 212 | uvm.as_mut().unwrap().copyout(sp, &slice)?; 213 | // now sp = *const &[Option<&str>] 214 | 215 | // arguments to user main(argc: i32, args: usize) generated by rustc 216 | // argc is returned via the system call return value, which goes in a0. 217 | // argc does not need to be i32 at all, so here we return usize. 218 | // Note that argc is treated as i32 in the main generated by rustc. 219 | // 220 | // The second argument is treated as *const *const u8 in rustc, 221 | // but it is still a pointer which size is usize, so here we set 222 | // *const &[&str] to the second argument. In rust_main, It should be 223 | // able to be treated as an Option<&[&str]> by NullPo Optimization in 224 | // main(.., args). 225 | tf.a1 = if argc > 0 { sp.into_usize() } else { 0 }; 226 | 227 | // Save program name for debugging. 228 | if let Some(name) = path.file_name() { 229 | proc_data.name = name.to_string(); 230 | } 231 | 232 | // Commit to the user image. 233 | let olduvm = proc_data.uvm.replace(uvm.take().unwrap()); 234 | proc_data.sz = sz; 235 | tf.epc = elf.e_entry; // initial program counter = main 236 | tf.sp = sp.into_usize(); // initial stack pointer 237 | olduvm.unwrap().proc_uvmfree(oldsz); 238 | 239 | Ok(argc) // this ends up in a0, the first argument to main(argc, args: &[&str]) 240 | }; 241 | res = exec(); 242 | 243 | match res { 244 | Err(_) => { 245 | if let Some(uvm) = uvm { 246 | uvm.proc_uvmfree(sz) 247 | } 248 | } 249 | _ => { 250 | // close on exec 251 | for file in p.data_mut().ofile.iter_mut() { 252 | match file { 253 | Some(f) if f.is_cloexec() => { 254 | file.take(); 255 | } 256 | _ => continue, 257 | } 258 | } 259 | } 260 | } 261 | 262 | res 263 | } 264 | -------------------------------------------------------------------------------- /src/user/lib/fs.rs: -------------------------------------------------------------------------------- 1 | use alloc::string::{String, ToString}; 2 | use alloc::sync::Arc; 3 | 4 | use crate::io::{Read, Write}; 5 | use crate::path::{Path, PathBuf}; 6 | use crate::sys::{ 7 | self, 8 | defs::AsBytes, 9 | fcntl::{omode, FcntlCmd}, 10 | fs::DirEnt, 11 | stat::FileType, 12 | stat::Stat, 13 | Error::*, 14 | }; 15 | pub type Fd = usize; 16 | 17 | #[derive(Debug, PartialEq, Eq)] 18 | pub struct File(Fd); 19 | 20 | impl File { 21 | pub fn open>(path: P) -> sys::Result { 22 | OpenOptions::new().read(true).open(path) 23 | } 24 | 25 | pub fn create>(path: P) -> sys::Result { 26 | OpenOptions::new() 27 | .write(true) 28 | .create(true) 29 | .truncate(true) 30 | .open(path) 31 | } 32 | 33 | pub fn options() -> OpenOptions { 34 | OpenOptions::new() 35 | } 36 | 37 | pub fn stat(&self) -> sys::Result { 38 | let mut stat: Stat = Default::default(); 39 | sys::fstat(self.0, &mut stat)?; 40 | Ok(stat) 41 | } 42 | 43 | pub fn try_clone(&self) -> sys::Result { 44 | sys::dup(self.0).map(File) 45 | } 46 | 47 | pub fn dup2(src: &File, dst: &mut File) -> sys::Result<()> { 48 | sys::dup2(src.0, dst.0)?; 49 | Ok(()) 50 | } 51 | 52 | pub unsafe fn from_raw_fd(raw_fd: Fd) -> Self { 53 | Self(raw_fd) 54 | } 55 | 56 | pub fn get_fd(&self) -> Fd { 57 | self.0 58 | } 59 | 60 | pub fn metadata(&self) -> sys::Result { 61 | let mut stat: Stat = Default::default(); 62 | sys::fstat(self.0, &mut stat)?; 63 | Ok(Metadata(stat)) 64 | } 65 | 66 | pub fn set_cloexec(&mut self) -> sys::Result { 67 | sys::fcntl(self.0, FcntlCmd::SetCloexec) 68 | } 69 | } 70 | 71 | impl Drop for File { 72 | fn drop(&mut self) { 73 | let _ = sys::close(self.0); 74 | } 75 | } 76 | 77 | impl Read for File { 78 | fn read(&mut self, buf: &mut [u8]) -> sys::Result { 79 | sys::read(self.0, buf) 80 | } 81 | } 82 | 83 | impl Write for File { 84 | fn write(&mut self, buf: &[u8]) -> sys::Result { 85 | sys::write(self.0, buf) 86 | } 87 | } 88 | 89 | #[derive(Clone, Debug)] 90 | pub struct OpenOptions { 91 | read: bool, 92 | write: bool, 93 | truncate: bool, 94 | append: bool, 95 | create: bool, 96 | } 97 | 98 | impl Default for OpenOptions { 99 | fn default() -> Self { 100 | Self::new() 101 | } 102 | } 103 | 104 | impl OpenOptions { 105 | pub fn new() -> Self { 106 | Self { 107 | read: false, 108 | write: false, 109 | truncate: false, 110 | append: false, 111 | create: false, 112 | } 113 | } 114 | pub fn read(&mut self, read: bool) -> &mut Self { 115 | self.read = read; 116 | self 117 | } 118 | pub fn write(&mut self, write: bool) -> &mut Self { 119 | self.write = write; 120 | self 121 | } 122 | pub fn truncate(&mut self, truncate: bool) -> &mut Self { 123 | self.truncate = truncate; 124 | self 125 | } 126 | pub fn append(&mut self, append: bool) -> &mut Self { 127 | self.append = append; 128 | self 129 | } 130 | pub fn create(&mut self, create: bool) -> &mut Self { 131 | self.create = create; 132 | self 133 | } 134 | 135 | pub fn open>(&self, path: P) -> sys::Result { 136 | sys::open( 137 | path.as_ref().to_str(), 138 | self.get_access_mode()? | self.get_creation_mode()?, 139 | ) 140 | .map(File) 141 | } 142 | 143 | fn get_access_mode(&self) -> sys::Result { 144 | match (self.read, self.write, self.append) { 145 | (true, false, false) => Ok(omode::RDONLY), 146 | (false, true, false) => Ok(omode::WRONLY), 147 | (true, true, false) => Ok(omode::RDWR), 148 | (false, _, true) => Ok(omode::WRONLY | omode::APPEND), 149 | (true, _, true) => Ok(omode::RDWR | omode::APPEND), 150 | (false, false, false) => Err(InvalidArgument), 151 | } 152 | } 153 | 154 | fn get_creation_mode(&self) -> sys::Result { 155 | match (self.write, self.append) { 156 | (true, false) => {} 157 | (false, false) => { 158 | if self.truncate || self.create { 159 | return Err(InvalidArgument); 160 | }; 161 | } 162 | (_, true) => { 163 | if self.truncate || !self.create { 164 | return Err(InvalidArgument); // 要検討 165 | } 166 | } 167 | } 168 | 169 | Ok(match (self.create, self.truncate) { 170 | (false, false) => 0, 171 | (true, false) => omode::CREATE, 172 | (false, true) => omode::TRUNC, 173 | (true, true) => omode::CREATE | omode::TRUNC, 174 | }) 175 | } 176 | } 177 | 178 | #[derive(Debug)] 179 | pub struct Metadata(Stat); 180 | 181 | pub fn metadata>(path: P) -> sys::Result { 182 | File::open(path)?.metadata() 183 | } 184 | 185 | impl Metadata { 186 | pub fn file_type(&self) -> FileType { 187 | self.0.ftype 188 | } 189 | 190 | pub fn is_dir(&self) -> bool { 191 | self.file_type() == FileType::Dir 192 | } 193 | 194 | pub fn is_file(&self) -> bool { 195 | self.file_type() == FileType::File 196 | } 197 | 198 | #[allow(clippy::len_without_is_empty)] 199 | pub fn len(&self) -> usize { 200 | self.0.size 201 | } 202 | pub fn inum(&self) -> u32 { 203 | self.0.ino 204 | } 205 | } 206 | 207 | pub struct ReadDir { 208 | fd: File, 209 | root: Arc, 210 | end_of_stream: bool, 211 | } 212 | 213 | pub struct DirEntry { 214 | dir: Arc, 215 | name: String, 216 | } 217 | 218 | pub fn read_dir>(path: P) -> sys::Result { 219 | let root = Arc::new(path.as_ref().to_path_buf()); 220 | let fd = File::open(path)?; 221 | if fd.metadata()?.is_dir() { 222 | Ok(ReadDir { 223 | fd, 224 | root, 225 | end_of_stream: false, 226 | }) 227 | } else { 228 | Err(NotADirectory) 229 | } 230 | } 231 | 232 | impl Iterator for ReadDir { 233 | type Item = sys::Result; 234 | fn next(&mut self) -> Option { 235 | let mut de: DirEnt = Default::default(); 236 | if self.end_of_stream { 237 | return None; 238 | } 239 | loop { 240 | if let Ok(0) = self.fd.read(de.as_bytes_mut()) { 241 | self.end_of_stream = true; 242 | break None; 243 | } 244 | 245 | if de.inum == 0 { 246 | continue; 247 | } 248 | let name = core::str::from_utf8(&de.name) 249 | .unwrap() 250 | .trim_end_matches(char::from(0)) 251 | .to_string(); 252 | if name == "." || name == ".." { 253 | continue; 254 | } 255 | break Some(Ok(DirEntry { 256 | dir: Arc::clone(&self.root), 257 | name, 258 | })); 259 | } 260 | } 261 | } 262 | 263 | impl DirEntry { 264 | pub fn path(&self) -> PathBuf { 265 | self.dir.join(self.name.as_str()) 266 | } 267 | pub fn file_name(&self) -> String { 268 | self.name.clone() 269 | } 270 | pub fn metadata(&self) -> sys::Result { 271 | File::open(self.path())?.metadata() 272 | } 273 | pub fn file_type(&self) -> sys::Result { 274 | self.metadata().map(|m| m.file_type()) 275 | } 276 | } 277 | 278 | #[derive(Debug, Default)] 279 | pub struct DirBuilder { 280 | recursive: bool, 281 | } 282 | 283 | impl DirBuilder { 284 | pub fn new() -> Self { 285 | DirBuilder { recursive: false } 286 | } 287 | 288 | pub fn recursive(&mut self, recursive: bool) -> &mut Self { 289 | self.recursive = recursive; 290 | self 291 | } 292 | 293 | pub fn create>(&self, path: P) -> sys::Result<()> { 294 | self._create(path.as_ref()) 295 | } 296 | 297 | fn _create(&self, path: &Path) -> sys::Result<()> { 298 | if self.recursive { 299 | self.create_dir_all(path) 300 | } else { 301 | sys::mkdir(path.to_str()) 302 | } 303 | } 304 | 305 | #[allow(clippy::only_used_in_recursion)] 306 | fn create_dir_all(&self, path: &Path) -> sys::Result<()> { 307 | if path == Path::new("") { 308 | return Ok(()); 309 | } 310 | 311 | match sys::mkdir(path.to_str()) { 312 | Ok(()) => return Ok(()), 313 | Err(NotFound) => {} 314 | Err(_) if path.is_dir() => return Ok(()), 315 | Err(e) => return Err(e), 316 | } 317 | match path.parent() { 318 | Some(p) => self.create_dir_all(p)?, 319 | None => return Err(Uncategorized), 320 | } 321 | match sys::mkdir(path.to_str()) { 322 | Ok(()) => Ok(()), 323 | Err(_) if path.is_dir() => Ok(()), 324 | Err(e) => Err(e), 325 | } 326 | } 327 | } 328 | 329 | pub fn create_dir_all>(path: P) -> sys::Result<()> { 330 | DirBuilder::new().recursive(true).create(path) 331 | } 332 | 333 | pub fn create_dir>(path: P) -> sys::Result<()> { 334 | DirBuilder::new().create(path) 335 | } 336 | 337 | pub fn hard_link, Q: AsRef>(original: P, link: Q) -> sys::Result<()> { 338 | sys::link(original.as_ref().to_str(), link.as_ref().to_str()) 339 | } 340 | 341 | pub fn remove_file>(path: P) -> sys::Result<()> { 342 | sys::unlink(path.as_ref().to_str()) 343 | } 344 | 345 | pub fn read_to_string>(path: P) -> sys::Result { 346 | let mut file = File::open(path)?; 347 | let mut string = String::new(); 348 | file.read_to_string(&mut string)?; 349 | Ok(string) 350 | } 351 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /src/kernel/bio.rs: -------------------------------------------------------------------------------- 1 | // Buffer cache. 2 | // 3 | // The buffer cache is a linked list of buf structures holding 4 | // cached copies of disk block contents. Caching disk blocks 5 | // in memory reduces the number of disk blocks used by multiple processes. 6 | // 7 | // Interface: 8 | // * To get a buffer for a particular disk block, call BCAHCE.read() and get BufGuard. 9 | // * After changing buffer data, call BufGuard.write to write it to disk. 10 | // * When done with the buffer, drop(BufGuard) with relse. 11 | // * Only one process at a time can use a buffer, 12 | // so do not keep them longer than necessary. 13 | #![allow(clippy::redundant_allocation)] 14 | use crate::{ 15 | array, 16 | fs::BSIZE, 17 | param::NBUF, 18 | sleeplock::{SleepLock, SleepLockGuard}, 19 | spinlock::Mutex, 20 | virtio_disk::DISK, 21 | }; 22 | use alloc::{ 23 | rc::{Rc, Weak}, 24 | sync::Arc, 25 | }; 26 | use core::{ 27 | cell::RefCell, 28 | ops::{Deref, DerefMut}, 29 | }; 30 | 31 | pub static BCACHE: BCache = BCache::new(); 32 | 33 | #[derive(Debug)] 34 | pub struct BCache { 35 | buf: [SleepLock; NBUF], 36 | // Linked list of all buffers, sorted by how recently the 37 | // buffer was used. 38 | lru: Mutex, 39 | } 40 | 41 | #[derive(Debug)] 42 | pub struct Data { 43 | pub data: [u8; BSIZE], 44 | pub disk: bool, // does disk "own" buf? 45 | blockno: u32, // sync with Meta 46 | dev: u32, // sync with Meta 47 | valid: bool, // has data been read from disk? 48 | } 49 | 50 | #[derive(Debug)] 51 | pub struct Lru { 52 | head: Option>, 53 | tail: Option>, 54 | n: usize, 55 | } 56 | unsafe impl Send for Lru {} 57 | 58 | #[derive(Debug)] 59 | pub struct Buf { 60 | data: Arc<&'static SleepLock>, 61 | meta: RefCell, 62 | } 63 | 64 | #[derive(Default, Debug)] 65 | struct Meta { 66 | dev: u32, 67 | blockno: u32, 68 | next: Option>, 69 | prev: Option>, 70 | } 71 | 72 | impl Lru { 73 | const fn new() -> Self { 74 | Self { 75 | head: None, 76 | tail: None, 77 | n: 0, 78 | } 79 | } 80 | 81 | fn add(&mut self, data: &'static SleepLock) { 82 | let data = Arc::new(data); 83 | let buf = Rc::new(Buf::new(data)); 84 | match self.tail.take() { 85 | Some(old_tail) => { 86 | old_tail.upgrade().unwrap().meta.borrow_mut().next = Some(Rc::clone(&buf)); 87 | buf.meta.borrow_mut().prev = Some(old_tail); 88 | } 89 | None => { 90 | self.head = Some(Rc::clone(&buf)); 91 | } 92 | } 93 | self.tail = Some(Rc::downgrade(&buf)); 94 | self.n += 1; 95 | } 96 | 97 | fn _get(&self, dev: u32, blockno: u32) -> (bool, Rc) { 98 | // Is the block already cached? 99 | for (i, b) in self.iter().enumerate() { 100 | assert!(i < 30, "iter could be an infinite loop."); 101 | if b.meta.borrow().dev == dev && b.meta.borrow().blockno == blockno { 102 | return (false, b); 103 | } 104 | } 105 | 106 | // Not cached 107 | // Recycle the least recently used LRU unused buffer 108 | for (i, b) in self.iter().rev().enumerate() { 109 | assert!(i < 30, "iter could be an infinite loop."); 110 | if Arc::strong_count(&b.data) == 1 { 111 | b.meta.borrow_mut().dev = dev; 112 | b.meta.borrow_mut().blockno = blockno; 113 | return (true, b); 114 | } 115 | } 116 | panic!("no buffers"); 117 | } 118 | 119 | fn relse(&mut self, buf: Rc) { 120 | if Arc::strong_count(&buf.data) == 1 { 121 | // if b is head, Do nothing. 122 | if buf.meta.borrow().prev.is_none() { 123 | return; 124 | } 125 | 126 | // detach b 127 | let detached_next = buf.meta.borrow_mut().next.take(); 128 | let detached_prev = buf.meta.borrow_mut().prev.take(); 129 | if let Some(ref n) = detached_next { 130 | n.meta.borrow_mut().prev = detached_prev.clone(); 131 | } 132 | if let Some(ref p) = detached_prev { 133 | p.upgrade().unwrap().meta.borrow_mut().next = detached_next.clone(); 134 | } 135 | 136 | // attach b to the head. 137 | match self.head.take() { 138 | Some(old_head) => { 139 | old_head.meta.borrow_mut().prev = Some(Rc::downgrade(&buf)); 140 | buf.meta.borrow_mut().next = Some(old_head); 141 | } 142 | None => { 143 | self.tail = Some(Rc::downgrade(&buf)); 144 | } 145 | } 146 | self.head = Some(buf); 147 | 148 | // update tail if b was tail 149 | if detached_next.is_none() { 150 | self.tail = detached_prev; 151 | } 152 | } 153 | } 154 | 155 | fn iter(&self) -> Iter { 156 | Iter { 157 | head: self.head.clone(), 158 | tail: self.tail.as_ref().and_then(|tail| tail.upgrade()), 159 | } 160 | } 161 | } 162 | 163 | struct Iter { 164 | head: Option>, 165 | tail: Option>, 166 | } 167 | 168 | impl Iterator for Iter { 169 | type Item = Rc; 170 | fn next(&mut self) -> Option { 171 | match self.head.take() { 172 | Some(old_head) => { 173 | self.head = old_head.meta.borrow().next.clone(); 174 | Some(old_head) 175 | } 176 | None => None, 177 | } 178 | } 179 | } 180 | 181 | impl DoubleEndedIterator for Iter { 182 | fn next_back(&mut self) -> Option { 183 | match self.tail.take() { 184 | Some(old_tail) => { 185 | self.tail = old_tail 186 | .meta 187 | .borrow() 188 | .prev 189 | .as_ref() 190 | .and_then(|p| p.upgrade()); 191 | Some(old_tail) 192 | } 193 | None => None, 194 | } 195 | } 196 | } 197 | 198 | pub struct BufGuard { 199 | data_guard: Option>, 200 | _ref: Option>>, 201 | _link: Option>, 202 | } 203 | 204 | impl Deref for BufGuard { 205 | type Target = SleepLockGuard<'static, Data>; 206 | fn deref(&self) -> &Self::Target { 207 | self.data_guard.as_ref().unwrap() 208 | } 209 | } 210 | 211 | impl DerefMut for BufGuard { 212 | fn deref_mut(&mut self) -> &mut Self::Target { 213 | self.data_guard.as_mut().unwrap() 214 | } 215 | } 216 | 217 | impl BufGuard { 218 | // Write buf's content to disk. Must be locked. 219 | pub fn write(&mut self) { 220 | if !self.holding() { 221 | panic!("bwrite"); 222 | } 223 | self.data_guard = DISK.rw(self.data_guard.take(), true); 224 | } 225 | 226 | pub fn pin(&self) { 227 | unsafe { Arc::increment_strong_count(Arc::as_ptr(self._ref.as_ref().unwrap())) } 228 | } 229 | pub fn unpin(&self) { 230 | unsafe { Arc::decrement_strong_count(Arc::as_ptr(self._ref.as_ref().unwrap())) } 231 | } 232 | 233 | pub fn align_to(&self) -> &[U] { 234 | let (head, body, _) = unsafe { self.data_guard.as_ref().unwrap().data.align_to::() }; 235 | assert!(head.is_empty(), "Data was not aligned"); 236 | body 237 | } 238 | pub fn align_to_mut(&mut self) -> &mut [U] { 239 | let (head, body, _) = unsafe { self.data_guard.as_mut().unwrap().data.align_to_mut::() }; 240 | assert!(head.is_empty(), "Data was not aligned"); 241 | body 242 | } 243 | } 244 | 245 | impl Drop for BufGuard { 246 | fn drop(&mut self) { 247 | if !self.holding() { 248 | panic!("drop - brelse"); 249 | } 250 | { 251 | self.data_guard.take(); // unlock sleep 252 | self._ref.take(); // Decrement refcnt 253 | } 254 | BCACHE.lru.lock().relse(self._link.take().unwrap()); 255 | } 256 | } 257 | 258 | impl Buf { 259 | fn new(data: Arc<&'static SleepLock>) -> Self { 260 | Self { 261 | data, 262 | meta: Default::default(), 263 | } 264 | } 265 | } 266 | 267 | impl Data { 268 | const fn new() -> Self { 269 | Self { 270 | data: [0; BSIZE], 271 | disk: false, 272 | blockno: 0, 273 | dev: 0, 274 | valid: false, 275 | } 276 | } 277 | 278 | pub fn blockno(&self) -> u32 { 279 | self.blockno 280 | } 281 | 282 | pub fn dev(&self) -> u32 { 283 | self.dev 284 | } 285 | } 286 | 287 | impl Deref for Data { 288 | type Target = [u8]; 289 | fn deref(&self) -> &Self::Target { 290 | &self.data 291 | } 292 | } 293 | 294 | impl DerefMut for Data { 295 | fn deref_mut(&mut self) -> &mut Self::Target { 296 | &mut self.data 297 | } 298 | } 299 | 300 | impl BCache { 301 | const fn new() -> Self { 302 | Self { 303 | buf: array![SleepLock::new(Data::new(), "buffer"); NBUF], 304 | lru: Mutex::new(Lru::new(), "bcache"), 305 | } 306 | } 307 | 308 | fn get(&self, dev: u32, blockno: u32) -> BufGuard { 309 | let (recycle, b) = self.lru.lock()._get(dev, blockno); 310 | let mut sleeplock = b.data.lock(); 311 | if recycle { 312 | sleeplock.valid = false; 313 | sleeplock.blockno = blockno; 314 | sleeplock.dev = dev; 315 | } 316 | BufGuard { 317 | data_guard: Some(sleeplock), 318 | _ref: Some(Arc::clone(&b.data)), 319 | _link: Some(b), // Do Not touch outside this function 320 | } 321 | } 322 | 323 | // Return a locked buf with the contents of the indicated block. 324 | pub fn read(&self, dev: u32, blockno: u32) -> BufGuard { 325 | let mut b = self.get(dev, blockno); 326 | if !b.valid { 327 | b.data_guard = DISK.rw(b.data_guard.take(), false); 328 | b.valid = true; 329 | } 330 | b 331 | } 332 | } 333 | 334 | pub fn init() { 335 | let mut lru = BCACHE.lru.lock(); 336 | for b in BCACHE.buf.iter() { 337 | lru.add(b); 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /src/user/lib/path.rs: -------------------------------------------------------------------------------- 1 | use alloc::string::{String, ToString}; 2 | use core::{fmt, todo}; 3 | 4 | use crate::{fs, sys}; 5 | 6 | #[repr(C)] 7 | pub struct Path { 8 | inner: str, 9 | } 10 | 11 | impl Path { 12 | pub fn new(s: &str) -> &Path { 13 | unsafe { &*(s as *const str as *const Path) } 14 | } 15 | pub fn to_str(&self) -> &str { 16 | self.inner.as_ref() 17 | } 18 | 19 | fn from_inner_mut(inner: &mut str) -> &mut Path { 20 | unsafe { &mut *(inner as *mut str as *mut Path) } 21 | } 22 | 23 | pub fn components(&self) -> Components<'_> { 24 | Components { 25 | path: &self.inner, 26 | has_root: !self.inner.is_empty() && self.inner.starts_with('/'), 27 | front: State::StartDir, 28 | back: State::Body, 29 | } 30 | } 31 | 32 | pub fn has_root(&self) -> bool { 33 | self.components().has_root 34 | } 35 | pub fn is_absolute(&self) -> bool { 36 | self.has_root() 37 | } 38 | pub fn is_relative(&self) -> bool { 39 | !self.is_absolute() 40 | } 41 | 42 | pub fn parent(&self) -> Option<&Path> { 43 | let mut comps = self.components(); 44 | let comp = comps.next_back(); 45 | comp.and_then(|p| match p { 46 | Component::Normal(_) | Component::CurDir | Component::ParentDir => { 47 | Some(comps.as_path()) 48 | } 49 | _ => None, 50 | }) 51 | } 52 | 53 | pub fn ancestors(&self) -> Ancestors<'_> { 54 | Ancestors { next: Some(self) } 55 | } 56 | 57 | pub fn file_name(&self) -> Option<&str> { 58 | self.components().next_back().and_then(|p| match p { 59 | Component::Normal(p) => Some(p), 60 | _ => None, 61 | }) 62 | } 63 | 64 | pub fn starts_with>(&self, base: P) -> bool { 65 | iter_after(self.components(), base.as_ref().components()).is_some() 66 | } 67 | 68 | pub fn ends_with>(&self, child: P) -> bool { 69 | iter_after(self.components().rev(), child.as_ref().components().rev()).is_some() 70 | } 71 | 72 | pub fn file_stem(&self) -> Option<&str> { 73 | self.file_name() 74 | .and_then(|s| s.rsplit_once('.')) 75 | .map(|(before, _)| before) 76 | } 77 | 78 | pub fn file_prefix(&self) -> Option<&str> { 79 | self.file_name() 80 | .and_then(|s| s.split_once('.')) 81 | .map(|(before, _)| before) 82 | } 83 | 84 | pub fn to_path_buf(&self) -> PathBuf { 85 | PathBuf::from(self.inner.to_string()) 86 | } 87 | 88 | pub fn join>(&self, path: P) -> PathBuf { 89 | let mut buf = self.to_path_buf(); 90 | buf.push(path); 91 | buf 92 | } 93 | 94 | pub fn is_dir(&self) -> bool { 95 | fs::metadata(self).map(|m| m.is_dir()).unwrap_or(false) 96 | } 97 | 98 | pub fn try_exists(&self) -> sys::Result { 99 | todo!() 100 | } 101 | 102 | pub fn exists(&self) -> bool { 103 | fs::metadata(self).is_ok() 104 | } 105 | } 106 | 107 | impl fmt::Debug for Path { 108 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 109 | fmt::Debug::fmt(&self.inner, f) 110 | } 111 | } 112 | 113 | impl AsRef for Path { 114 | fn as_ref(&self) -> &str { 115 | &self.inner 116 | } 117 | } 118 | 119 | impl AsRef for str { 120 | fn as_ref(&self) -> &Path { 121 | Path::new(self) 122 | } 123 | } 124 | 125 | impl AsRef for Path { 126 | fn as_ref(&self) -> &Path { 127 | self 128 | } 129 | } 130 | 131 | impl PartialEq for Path { 132 | fn eq(&self, other: &Self) -> bool { 133 | self.components().path == other.components().path 134 | } 135 | } 136 | 137 | // the core iterators 138 | #[derive(Copy, Clone, PartialEq, PartialOrd, Debug)] 139 | enum State { 140 | StartDir = 0, // / or . or nothing 141 | Body = 1, // foo/bar/baz 142 | Done = 2, 143 | } 144 | 145 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] 146 | pub enum Component<'a> { 147 | RootDir, 148 | CurDir, 149 | ParentDir, 150 | Normal(&'a str), 151 | } 152 | 153 | impl<'a> Component<'a> { 154 | pub fn to_str(self) -> &'a str { 155 | match self { 156 | Self::RootDir => "/", 157 | Self::CurDir => ".", 158 | Self::ParentDir => "..", 159 | Self::Normal(path) => path, 160 | } 161 | } 162 | } 163 | 164 | #[derive(Clone)] 165 | pub struct Components<'a> { 166 | path: &'a str, 167 | has_root: bool, // !path.is_empty() && path[0] == b'/' 168 | front: State, 169 | back: State, 170 | } 171 | 172 | pub struct Iter<'a> { 173 | inner: Components<'a>, 174 | } 175 | 176 | impl<'a> Iter<'a> { 177 | pub fn as_path(&self) -> &'a Path { 178 | self.inner.as_path() 179 | } 180 | } 181 | 182 | impl<'a> Iterator for Iter<'a> { 183 | type Item = &'a str; 184 | fn next(&mut self) -> Option { 185 | self.inner.next().map(Component::to_str) 186 | } 187 | } 188 | 189 | impl<'a> DoubleEndedIterator for Iter<'a> { 190 | fn next_back(&mut self) -> Option { 191 | self.inner.next_back().map(Component::to_str) 192 | } 193 | } 194 | 195 | impl<'a> Components<'a> { 196 | fn len_before_body(&self) -> usize { 197 | let root = if self.front <= State::StartDir && self.has_root { 198 | 1 199 | } else { 200 | 0 201 | }; 202 | let cur_dir = if self.front <= State::StartDir && self.include_cur_dir() { 203 | 1 204 | } else { 205 | 0 206 | }; 207 | root + cur_dir 208 | } 209 | fn finished(&self) -> bool { 210 | self.front == State::Done || self.back == State::Done || self.front > self.back 211 | } 212 | 213 | pub fn as_path(&self) -> &'a Path { 214 | let mut comps = self.clone(); 215 | if comps.front == State::Body { 216 | comps.trim_left(); 217 | } 218 | if comps.back == State::Body { 219 | comps.trim_right(); 220 | } 221 | Path::new(comps.path) 222 | } 223 | 224 | // should the normalized path include a leading . ? 225 | fn include_cur_dir(&self) -> bool { 226 | if self.has_root { 227 | return false; 228 | } 229 | let mut chars = self.path.chars(); 230 | match (chars.next(), chars.next()) { 231 | (Some('.'), None) => true, 232 | (Some('.'), Some(b)) => b == '/', 233 | _ => false, 234 | } 235 | } 236 | fn parse_single_component(comp: &str) -> Option> { 237 | match comp { 238 | "." => None, // .components are normalized, which is treated via include_cur_dir 239 | ".." => Some(Component::ParentDir), 240 | "" => None, 241 | _ => Some(Component::Normal(comp)), 242 | } 243 | } 244 | fn parse_next_component(&self) -> (&'a str, Option>) { 245 | match self.path.split_once('/') { 246 | Some((comp, extra)) => (extra, Self::parse_single_component(comp)), 247 | None => ("", Self::parse_single_component(self.path)), 248 | } 249 | } 250 | fn parse_next_component_back(&self) -> (&'a str, Option>) { 251 | match self.path.rsplit_once('/') { 252 | Some((extra, comp)) => (extra, Self::parse_single_component(comp)), 253 | None => ("", Self::parse_single_component(self.path)), 254 | } 255 | } 256 | // trim away repeated separators (i.e., empty components) on the left 257 | fn trim_left(&mut self) { 258 | while !self.path.is_empty() { 259 | let (extra, comp) = self.parse_next_component(); 260 | if comp.is_some() { 261 | return; 262 | } else { 263 | self.path = extra; 264 | } 265 | } 266 | } 267 | // trim away repeated separators (i.e., empty components) on the right 268 | fn trim_right(&mut self) { 269 | while !self.path.is_empty() { 270 | let (extra, comp) = self.parse_next_component_back(); 271 | if comp.is_some() { 272 | return; 273 | } else { 274 | self.path = extra; 275 | } 276 | } 277 | } 278 | } 279 | 280 | impl<'a> Iterator for Components<'a> { 281 | type Item = Component<'a>; 282 | 283 | fn next(&mut self) -> Option { 284 | while !self.finished() { 285 | match self.front { 286 | State::StartDir => { 287 | self.front = State::Body; 288 | if self.has_root { 289 | self.path = self.path.get(1..).unwrap(); 290 | return Some(Component::RootDir); 291 | } else if self.include_cur_dir() { 292 | self.path = self.path.get(1..).unwrap(); 293 | return Some(Component::CurDir); 294 | } 295 | } 296 | State::Body if !self.path.is_empty() => { 297 | let (extra, comp) = self.parse_next_component(); 298 | self.path = extra; 299 | if comp.is_some() { 300 | return comp; 301 | } 302 | } 303 | State::Body => { 304 | self.front = State::Done; 305 | } 306 | State::Done => unreachable!(), 307 | } 308 | } 309 | None 310 | } 311 | } 312 | 313 | impl<'a> DoubleEndedIterator for Components<'a> { 314 | fn next_back(&mut self) -> Option { 315 | while !self.finished() { 316 | match self.back { 317 | State::Body if self.path.len() > self.len_before_body() => { 318 | let (extra, comp) = self.parse_next_component_back(); 319 | self.path = extra; 320 | if comp.is_some() { 321 | return comp; 322 | } 323 | } 324 | State::Body => { 325 | self.back = State::StartDir; 326 | } 327 | State::StartDir => { 328 | self.back = State::Done; 329 | if self.has_root { 330 | return Some(Component::RootDir); 331 | } else if self.include_cur_dir() { 332 | return Some(Component::CurDir); 333 | } 334 | } 335 | State::Done => unreachable!(), 336 | } 337 | } 338 | None 339 | } 340 | } 341 | 342 | #[derive(Debug, Copy, Clone)] 343 | pub struct Ancestors<'a> { 344 | next: Option<&'a Path>, 345 | } 346 | 347 | impl<'a> Iterator for Ancestors<'a> { 348 | type Item = &'a Path; 349 | fn next(&mut self) -> Option { 350 | let next = self.next; 351 | self.next = next.and_then(Path::parent); 352 | next 353 | } 354 | } 355 | 356 | // Iterate through `iter` while it matches `prefix`; return `None` if `prefix` 357 | // is not a prefix of `iter`, otherwise return `Some(iter_after_prefix)` giving 358 | // `iter` after having exhausted `prefix` 359 | fn iter_after<'a, 'b, I, J>(mut iter: I, mut prefix: J) -> Option 360 | where 361 | I: Iterator> + Clone, 362 | J: Iterator>, 363 | { 364 | loop { 365 | let mut iter_next = iter.clone(); 366 | match (iter_next.next(), prefix.next()) { 367 | (Some(ref x), Some(ref y)) if x == y => (), 368 | (Some(_), Some(_)) => return None, 369 | (Some(_), None) => return Some(iter), 370 | (None, None) => return Some(iter), 371 | (None, Some(_)) => return None, 372 | } 373 | iter = iter_next; 374 | } 375 | } 376 | 377 | #[derive(Debug, Default)] 378 | #[repr(transparent)] 379 | pub struct PathBuf { 380 | inner: String, 381 | } 382 | 383 | impl PathBuf { 384 | pub fn new() -> Self { 385 | PathBuf { 386 | inner: String::new(), 387 | } 388 | } 389 | pub fn as_path(&self) -> &Path { 390 | self 391 | } 392 | pub fn push>(&mut self, path: P) { 393 | let path = path.as_ref(); 394 | let need_sep = self.inner.chars().last().map(|c| c != '/').unwrap_or(false); 395 | 396 | // absolute `path` replaces `self` 397 | if path.is_absolute() { 398 | self.inner.truncate(0); 399 | } else if need_sep { 400 | // `path` is a pure relative path 401 | self.inner.push('/'); 402 | } 403 | self.inner.push_str(path.to_str()); 404 | } 405 | } 406 | 407 | impl core::ops::Deref for PathBuf { 408 | type Target = Path; 409 | #[inline] 410 | fn deref(&self) -> &Path { 411 | Path::new(&self.inner) 412 | } 413 | } 414 | 415 | impl core::ops::DerefMut for PathBuf { 416 | #[inline] 417 | fn deref_mut(&mut self) -> &mut Path { 418 | Path::from_inner_mut(&mut self.inner) 419 | } 420 | } 421 | 422 | impl From for PathBuf { 423 | fn from(value: String) -> Self { 424 | PathBuf { inner: value } 425 | } 426 | } 427 | 428 | impl AsRef for PathBuf { 429 | fn as_ref(&self) -> &Path { 430 | self 431 | } 432 | } 433 | -------------------------------------------------------------------------------- /src/kernel/file.rs: -------------------------------------------------------------------------------- 1 | #[cfg(all(target_os = "none", feature = "kernel"))] 2 | use crate::array; 3 | #[cfg(all(target_os = "none", feature = "kernel"))] 4 | use crate::error::{Error::*, Result}; 5 | #[cfg(all(target_os = "none", feature = "kernel"))] 6 | use crate::fcntl::{FcntlCmd, OMode}; 7 | #[cfg(all(target_os = "none", feature = "kernel"))] 8 | use crate::fs::{create, IData, Inode, Path, BSIZE}; 9 | #[cfg(all(target_os = "none", feature = "kernel"))] 10 | use crate::log::LOG; 11 | #[cfg(all(target_os = "none", feature = "kernel"))] 12 | use crate::param::{MAXOPBLOCKS, NDEV, NFILE}; 13 | #[cfg(all(target_os = "none", feature = "kernel"))] 14 | use crate::pipe::Pipe; 15 | #[cfg(all(target_os = "none", feature = "kernel"))] 16 | use crate::proc::either_copyout; 17 | #[cfg(all(target_os = "none", feature = "kernel"))] 18 | use crate::sleeplock::{SleepLock, SleepLockGuard}; 19 | #[cfg(all(target_os = "none", feature = "kernel"))] 20 | use crate::spinlock::Mutex; 21 | #[cfg(all(target_os = "none", feature = "kernel"))] 22 | use crate::stat::{FileType, Stat}; 23 | #[cfg(all(target_os = "none", feature = "kernel"))] 24 | use crate::sync::{LazyLock, OnceLock}; 25 | #[cfg(all(target_os = "none", feature = "kernel"))] 26 | use crate::vm::VirtAddr; 27 | #[cfg(all(target_os = "none", feature = "kernel"))] 28 | use alloc::sync::Arc; 29 | #[cfg(all(target_os = "none", feature = "kernel"))] 30 | use core::cell::UnsafeCell; 31 | #[cfg(all(target_os = "none", feature = "kernel"))] 32 | use core::ops::Deref; 33 | 34 | #[cfg(all(target_os = "none", feature = "kernel"))] 35 | pub static DEVSW: DevSW = DevSW::new(); 36 | #[cfg(all(target_os = "none", feature = "kernel"))] 37 | pub static FTABLE: LazyLock = LazyLock::new(|| Mutex::new(array![None; NFILE], "ftable")); 38 | 39 | #[cfg(all(target_os = "none", feature = "kernel"))] 40 | type FTable = Mutex<[Option>; NFILE]>; 41 | 42 | #[cfg(all(target_os = "none", feature = "kernel"))] 43 | #[derive(Default, Clone, Debug)] 44 | pub struct File { 45 | f: Option>, 46 | readable: bool, 47 | writable: bool, 48 | cloexec: bool, 49 | } 50 | 51 | #[cfg(all(target_os = "none", feature = "kernel"))] 52 | #[derive(Debug)] 53 | pub enum VFile { 54 | Device(DNod), 55 | Inode(FNod), 56 | Pipe(Pipe), 57 | None, 58 | } 59 | 60 | // Device Node 61 | #[cfg(all(target_os = "none", feature = "kernel"))] 62 | #[derive(Debug)] 63 | pub struct DNod { 64 | driver: &'static dyn Device, 65 | ip: Inode, 66 | } 67 | 68 | // Device functions, map this trait using dyn 69 | #[cfg(all(target_os = "none", feature = "kernel"))] 70 | pub trait Device: Send + Sync { 71 | fn read(&self, dst: VirtAddr, n: usize) -> Result; 72 | fn write(&self, src: VirtAddr, n: usize) -> Result; 73 | fn major(&self) -> Major; 74 | } 75 | 76 | #[cfg(all(target_os = "none", feature = "kernel"))] 77 | impl core::fmt::Debug for dyn Device { 78 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 79 | write!(f, "Device fn {:?}", self.major()) 80 | } 81 | } 82 | 83 | #[cfg(all(target_os = "none", feature = "kernel"))] 84 | impl Deref for DNod { 85 | type Target = dyn Device; 86 | fn deref(&self) -> &Self::Target { 87 | self.driver 88 | } 89 | } 90 | 91 | // File & directory Node 92 | #[cfg(all(target_os = "none", feature = "kernel"))] 93 | #[derive(Debug)] 94 | pub struct FNod { 95 | off: UnsafeCell, // Safety: If inode lock is obtained. 96 | ip: Inode, 97 | } 98 | #[cfg(all(target_os = "none", feature = "kernel"))] 99 | unsafe impl Send for FNod {} 100 | #[cfg(all(target_os = "none", feature = "kernel"))] 101 | unsafe impl Sync for FNod {} 102 | 103 | #[cfg(all(target_os = "none", feature = "kernel"))] 104 | impl FNod { 105 | pub fn new(ip: Inode, offset: u32) -> Self { 106 | Self { 107 | off: UnsafeCell::new(offset), 108 | ip, 109 | } 110 | } 111 | fn read(&self, dst: VirtAddr, n: usize) -> Result { 112 | let mut ip = self.ip.lock(); 113 | let off = unsafe { &mut *self.off.get() }; 114 | 115 | let r = ip.read(dst, *off, n)?; 116 | *off += r as u32; 117 | Ok(r) 118 | } 119 | fn write(&self, src: VirtAddr, n: usize) -> Result { 120 | // write a few blocks at a time to avoid exceeding the maximum 121 | // log transaction size, including i-node, indirect block, 122 | // allocation blocks, and 2 blocks of slop for non-aligned 123 | // writes. this really belongs lower down, since inode write() 124 | // might be writing a device like the console. 125 | let max = ((MAXOPBLOCKS - 1 - 1 - 2) / 2) * BSIZE; 126 | let mut ret: Result = Ok(0); 127 | let mut i: usize = 0; 128 | 129 | while i < n { 130 | let mut n1 = n - i; 131 | if n1 > max { 132 | n1 = max 133 | } 134 | 135 | LOG.begin_op(); 136 | { 137 | let mut guard = self.ip.lock(); 138 | let off = unsafe { &mut *self.off.get() }; 139 | ret = guard.write(src, *off, n1); 140 | match ret { 141 | Ok(r) => { 142 | *off += r as u32; 143 | i += r; 144 | ret = Ok(i); 145 | } 146 | _ => break, // error from inode write 147 | } 148 | } 149 | LOG.end_op(); 150 | } 151 | if ret.is_err() { 152 | LOG.end_op(); 153 | } 154 | ret 155 | } 156 | } 157 | 158 | #[cfg(all(target_os = "none", feature = "kernel"))] 159 | impl VFile { 160 | fn read(&self, dst: VirtAddr, n: usize) -> Result { 161 | match self { 162 | VFile::Device(d) => d.read(dst, n), 163 | VFile::Inode(f) => f.read(dst, n), 164 | VFile::Pipe(p) => p.read(dst, n), 165 | _ => panic!("file read"), 166 | } 167 | } 168 | fn write(&self, src: VirtAddr, n: usize) -> Result { 169 | match self { 170 | VFile::Device(d) => d.write(src, n), 171 | VFile::Inode(f) => f.write(src, n), 172 | VFile::Pipe(p) => p.write(src, n), 173 | _ => panic!("file write"), 174 | } 175 | } 176 | // Get metadata about file. 177 | // addr pointing to a struct stat. 178 | pub fn stat(&self, addr: VirtAddr) -> Result<()> { 179 | let mut stat: Stat = Default::default(); 180 | 181 | match self { 182 | VFile::Device(DNod { driver: _, ref ip }) | VFile::Inode(FNod { off: _, ref ip }) => { 183 | { 184 | ip.lock().stat(&mut stat); 185 | } 186 | either_copyout(addr, &stat) 187 | } 188 | _ => Err(BadFileDescriptor), 189 | } 190 | } 191 | } 192 | 193 | #[cfg(all(target_os = "none", feature = "kernel"))] 194 | impl File { 195 | // Read from file. 196 | pub fn read(&mut self, dst: VirtAddr, n: usize) -> Result { 197 | if !self.readable { 198 | return Err(InvalidArgument); 199 | } 200 | self.f.as_ref().unwrap().read(dst, n) 201 | } 202 | 203 | // Write to file. 204 | pub fn write(&mut self, src: VirtAddr, n: usize) -> Result { 205 | if !self.writable { 206 | return Err(InvalidArgument); 207 | } 208 | self.f.as_ref().unwrap().write(src, n) 209 | } 210 | 211 | pub fn is_cloexec(&self) -> bool { 212 | self.cloexec 213 | } 214 | 215 | pub fn clear_cloexec(&mut self) { 216 | self.cloexec = false; 217 | } 218 | 219 | pub fn do_fcntl(&mut self, cmd: FcntlCmd) -> Result { 220 | use FcntlCmd::*; 221 | match cmd { 222 | SetCloexec => self.cloexec = true, 223 | _ => return Err(InvalidArgument), 224 | } 225 | Ok(0) 226 | } 227 | } 228 | 229 | #[cfg(all(target_os = "none", feature = "kernel"))] 230 | impl Deref for File { 231 | type Target = Arc; 232 | fn deref(&self) -> &Self::Target { 233 | self.f.as_ref().unwrap() 234 | } 235 | } 236 | 237 | #[cfg(all(target_os = "none", feature = "kernel"))] 238 | impl Drop for File { 239 | fn drop(&mut self) { 240 | let f = self.f.take().unwrap(); 241 | if Arc::strong_count(&f) < 2 { 242 | panic!("file drop"); 243 | } 244 | 245 | if Arc::strong_count(&f) == 2 { 246 | let mut guard = FTABLE.lock(); 247 | // drop arc in table 248 | for ff in guard.iter_mut() { 249 | match ff { 250 | Some(vff) if Arc::ptr_eq(&f, vff) => { 251 | ff.take(); // drop ref in table. ref count = 1; 252 | } 253 | _ => (), 254 | } 255 | } 256 | } 257 | 258 | // if ref count == 1 259 | if let Ok(VFile::Inode(FNod { off: _, ip }) | VFile::Device(DNod { driver: _, ip })) = 260 | Arc::try_unwrap(f) 261 | { 262 | LOG.begin_op(); 263 | drop(ip); 264 | LOG.end_op(); 265 | } 266 | } 267 | } 268 | 269 | // File Allocation Type Source 270 | #[cfg(all(target_os = "none", feature = "kernel"))] 271 | pub enum FType<'a> { 272 | Node(&'a Path), 273 | Pipe(Pipe), 274 | } 275 | 276 | #[cfg(all(target_os = "none", feature = "kernel"))] 277 | impl FTable { 278 | // Allocate a file structure 279 | // Must be called inside transaction if FType == FType::Node. 280 | pub fn alloc(&self, opts: OMode, ftype: FType<'_>) -> Result { 281 | let inner: Arc = Arc::new(match ftype { 282 | FType::Node(path) => { 283 | let ip: Inode; 284 | let mut ip_guard: SleepLockGuard<'_, IData>; 285 | 286 | if opts.is_create() { 287 | ip = create(path, FileType::File, 0, 0)?; 288 | ip_guard = ip.lock(); 289 | } else { 290 | (_, ip) = path.namei()?; 291 | ip_guard = ip.lock(); 292 | if ip_guard.itype() == FileType::Dir && !opts.is_rdonly() { 293 | return Err(IsADirectory); 294 | } 295 | } 296 | // ? 297 | match ip_guard.itype() { 298 | FileType::Device if ip_guard.major() != Major::Invalid => { 299 | let driver = DEVSW.get(ip_guard.major()).unwrap(); 300 | SleepLock::unlock(ip_guard); 301 | VFile::Device(DNod { driver, ip }) 302 | } 303 | FileType::Dir | FileType::File => { 304 | let mut offset = 0; 305 | if opts.is_trunc() && ip_guard.itype() == FileType::File { 306 | ip_guard.trunc(); 307 | } else if opts.is_append() && ip_guard.itype() == FileType::File { 308 | offset = ip_guard.size(); 309 | } 310 | SleepLock::unlock(ip_guard); 311 | VFile::Inode(FNod::new(ip, offset)) 312 | } 313 | _ => return Err(NoSuchNode), 314 | } 315 | } 316 | FType::Pipe(pi) => VFile::Pipe(pi), 317 | }); 318 | 319 | let mut guard = self.lock(); 320 | 321 | let mut empty: Option<&mut Option>> = None; 322 | for f in guard.iter_mut() { 323 | match f { 324 | None if empty.is_none() => { 325 | empty = Some(f); 326 | break; 327 | } 328 | _ => continue, 329 | } 330 | } 331 | 332 | let f = empty.ok_or(FileTableOverflow)?; 333 | f.replace(inner); 334 | Ok(File { 335 | f: f.clone(), // ref count = 2 336 | readable: opts.is_read(), 337 | writable: opts.is_write(), 338 | cloexec: opts.is_cloexec(), 339 | }) 340 | } 341 | } 342 | 343 | #[cfg(all(target_os = "none", feature = "kernel"))] 344 | pub struct DevSW { 345 | table: [OnceLock<&'static dyn Device>; NDEV], 346 | } 347 | 348 | #[cfg(all(target_os = "none", feature = "kernel"))] 349 | impl core::fmt::Debug for DevSW { 350 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 351 | write!(f, "[")?; 352 | for (count, v) in self.table.iter().enumerate() { 353 | if count != 0 { 354 | write!(f, ", ")?; 355 | } 356 | if let Some(&v) = v.get() { 357 | write!(f, "{:?}", v)?; 358 | } else { 359 | write!(f, "None")?; 360 | } 361 | } 362 | write!(f, "]") 363 | } 364 | } 365 | 366 | #[cfg(all(target_os = "none", feature = "kernel"))] 367 | impl DevSW { 368 | pub const fn new() -> Self { 369 | Self { 370 | table: array![OnceLock::new(); NDEV], 371 | } 372 | } 373 | pub fn set( 374 | &self, 375 | devnum: Major, 376 | dev: &'static dyn Device, 377 | ) -> core::result::Result<(), &'static (dyn Device + 'static)> { 378 | self.table[devnum as usize].set(dev) 379 | } 380 | 381 | pub fn get(&self, devnum: Major) -> Option<&'static dyn Device> { 382 | match self.table[devnum as usize].get() { 383 | Some(&dev) => Some(dev), 384 | None => None, 385 | } 386 | } 387 | } 388 | 389 | // Device Major Number 390 | #[repr(u16)] 391 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 392 | pub enum Major { 393 | Null = 0, 394 | Console = 1, 395 | Invalid, 396 | } 397 | impl Default for Major { 398 | fn default() -> Self { 399 | Self::Invalid 400 | } 401 | } 402 | 403 | impl Major { 404 | pub fn from_u16(bits: u16) -> Major { 405 | match bits { 406 | 0 => Major::Null, 407 | 1 => Major::Console, 408 | _ => Major::Invalid, 409 | } 410 | } 411 | } 412 | --------------------------------------------------------------------------------