diff --git a/src/console.rs b/src/console.rs index bd0c2c6..aa9bb52 100644 --- a/src/console.rs +++ b/src/console.rs @@ -1,7 +1,10 @@ -use crate::uart::*; +use crate::{uart::*}; use core::fmt::*; +use crate::file::DEVSW; +use crate::param::CONSOLE; pub struct Console {} + impl Write for Console { fn write_str(&mut self, s: &str) -> Result { for c in s.chars() { @@ -55,7 +58,7 @@ pub fn consoleintr(getc: fn() -> Option) { _ => { if c != 0 && input.e.wrapping_sub(input.r) < INPUT_BUF { let c = if c == b'\r' { b'\n' } else { c }; - input.buf[input.e % INPUT_BUF] = c as u8; + input.buf[input.e % INPUT_BUF] = c; input.e += 1; consputc(c); if c == b'\n' || c == ctrl('D') || input.e == input.r + INPUT_BUF { @@ -64,6 +67,8 @@ pub fn consoleintr(getc: fn() -> Option) { } } } + } else { + break; } } } @@ -87,3 +92,61 @@ pub fn consputc(c: u8) { uartputc(c as char); } } + +pub fn consoleread(_ip: usize, dst: &mut [u8], n: i32) -> i32 { + let target = n; + let mut n = n; + + unsafe { + let input = &raw mut INPUT; + while n > 0 { + // Busy wait for input - mirrors C: while(input.r == input.w); + while core::ptr::read_volatile(&(*input).r) == core::ptr::read_volatile(&(*input).w) { + // Spin-wait (busy wait) for input to arrive + core::hint::spin_loop(); + } + + // Read character and increment read pointer - mirrors C: input.buf[input.r++ % INPUT_BUF] + let c = (*input).buf[(*input).r % INPUT_BUF]; + (*input).r += 1; + + // Handle EOF (Ctrl-D) + if c == ctrl('D') { + if n < target { + // Save ^D for next time, to make sure + // caller gets a 0-byte result. + (*input).r -= 1; + } + break; + } + + // Copy character to destination - mirrors C: *dst++ = c; + dst[(target - n) as usize] = c as u8; + n -= 1; + + // Break on newline + if c == b'\n' { + break; + } + } + } + + target - n +} + +pub fn consolewrite(_ip: usize, src: &[u8], n: i32) -> i32 { + // Mirrors C: for(i = 0; i < n; i++) consputc(buf[i] & 0xff); + for i in 0..n { + consputc(src[i as usize]); + } + n +} + +pub fn consoleinit() { + // Register console device handlers in the device switch table + // Mirrors C: devsw[CONSOLE].write = consolewrite; devsw[CONSOLE].read = consoleread; + unsafe { + DEVSW[CONSOLE].read = Some(consoleread); + DEVSW[CONSOLE].write = Some(consolewrite); + } +} diff --git a/src/constants.rs b/src/constants.rs index b9cfea5..c6856f2 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -111,7 +111,7 @@ pub const MASKED: u32 = 0x00010000; // Interrupt masked pub use crate::fs_h::{ BPB, BSIZE, DINODE_SIZE, DIRSIZ, FSSIZE, IPB, LOGSIZE, MAXFILE, NDIRECT, NINDIRECT, NINODES, - ROOTINO, T_DIR, T_FILE, + ROOTINO, T_DIR, T_FILE, T_DEV, }; // ------------------------------------------------------ SYSTEM PARAMETERS (param.h) ---------------------------------------------- diff --git a/src/file.rs b/src/file.rs index c08eeed..374715d 100644 --- a/src/file.rs +++ b/src/file.rs @@ -1,15 +1,35 @@ use crate::buf::BSIZE; -use crate::constants::{T_DIR, T_FILE}; +use crate::constants::{T_DIR, T_FILE, T_DEV}; use crate::fcntl::{O_CREATE, O_RDONLY, O_RDWR, O_WRONLY}; use crate::fs; use crate::fs::DIRENT_SIZE; use crate::log::{begin_op, end_op}; -use crate::param::{MAXOPBLOCKS, NFILE}; +use crate::log; +use crate::param::{MAXOPBLOCKS, NFILE, NDEV}; + +// Device switch table entry +#[derive(Copy, Clone)] +pub struct Devsw { + pub read: Option i32>, + pub write: Option i32>, +} + +impl Devsw { + pub const fn new() -> Self { + Self { + read: None, + write: None, + } + } +} + +pub static mut DEVSW: [Devsw; NDEV] = [const { Devsw::new() }; NDEV]; #[derive(Copy, Clone, PartialEq, Eq)] pub enum FileType { None, Inode, + Device, } #[derive(Copy, Clone)] @@ -394,6 +414,22 @@ pub fn mkdir(path: &str) -> i32 { begin_op(); let ip = match create(path, T_DIR as i16, 0, 0) { + Some(ip) => ip, + None => { + log::end_op(); + return -1; + } + }; + + fs::iput(ip); + log::end_op(); + 0 +} + +pub fn mknod(path: &str, major: i16, minor: i16) -> i32 { + begin_op(); + + let ip = match create(path, T_DEV as i16, major, minor) { Some(ip) => ip, None => { end_op(); diff --git a/src/fs.rs b/src/fs.rs index 985abd0..efbc5ac 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -2,12 +2,12 @@ use core::cmp::min; use crate::{bio, log}; use crate::buf::BSIZE; -use crate::constants::{BPB, DINODE_SIZE, DIRSIZ, IPB, MAXFILE, NDIRECT, NINDIRECT, T_DIR}; -use crate::fs_h::{self, name_to_dirsiz, Dirent, Superblock}; -use crate::param::{NINODE, ROOTDEV}; +use crate::constants::{BPB, DINODE_SIZE, DIRSIZ, IPB, MAXFILE, NDIRECT, NINDIRECT, T_DIR, T_DEV}; +use crate::fs_h::{self, name_to_dirsiz, Dirent, Superblock, ROOTINO}; +use crate::file::DEVSW; +use crate::param::{NINODE, ROOTDEV, NDEV}; use crate::println; -pub use crate::fs_h::ROOTINO; pub const DIRENT_SIZE: usize = core::mem::size_of::(); #[repr(C)] @@ -507,6 +507,15 @@ pub fn readi(idx: usize, dst: &mut [u8], off: u32, n: u32) -> i32 { } let ip = &ICACHE.inode[idx]; + + // Handle device files + if ip.type_ == T_DEV as i16 { + if ip.major < 0 || (ip.major as usize) >= NDEV || DEVSW[ip.major as usize].read.is_none() { + return -1; + } + return DEVSW[ip.major as usize].read.unwrap()(idx, dst, n as i32); + } + if off > ip.size || off.checked_add(n).is_none() || ip.nlink < 1 { return -1; } @@ -549,6 +558,15 @@ pub fn writei(idx: usize, src: &[u8], off: u32, n: u32) -> i32 { } let ip = &ICACHE.inode[idx]; + + // Handle device files + if ip.type_ == T_DEV as i16 { + if ip.major < 0 || (ip.major as usize) >= NDEV || DEVSW[ip.major as usize].write.is_none() { + return -1; + } + return DEVSW[ip.major as usize].write.unwrap()(idx, src, n as i32); + } + if off > ip.size || off.checked_add(n).is_none() { return -1; } diff --git a/src/fs_h.rs b/src/fs_h.rs index ff21763..1a4f783 100644 --- a/src/fs_h.rs +++ b/src/fs_h.rs @@ -29,6 +29,7 @@ pub const LOGSIZE: u32 = 30; // max data blocks in on-disk log pub const T_DIR: u16 = 1; // directory pub const T_FILE: u16 = 2; // file +pub const T_DEV: u16 = 3; // device #[inline] pub fn name_to_dirsiz(name: &str) -> [u8; DIRSIZ] { diff --git a/src/main.rs b/src/main.rs index ebe8ee8..d6b8240 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,50 +36,29 @@ fn halt() -> ! { } fn welcome() { - // Create and write /foo/hello.txt - file::mkdir("/foo"); - - let gtxt = file::open("/foo/hello.txt", fcntl::O_CREATE | fcntl::O_WRONLY) - .unwrap_or_else(|| panic!("Failed to create /foo/hello.txt")); - let n = file::filewrite(gtxt, b"hello\0", 6); - println!("Wrote {} characters to /foo/hello.txt", n); - file::fileclose(gtxt); - - // Read /foo/hello.txt - let gtxt = file::open("/foo/hello.txt", fcntl::O_RDONLY) - .unwrap_or_else(|| panic!("Unable to open /foo/hello.txt")); - let mut welcome = [0u8; 512]; - let n = file::fileread(gtxt, &mut welcome, 6); - println!("Read {} chars from /foo/hello.txt: ", n); - println!("{}\n", core::str::from_utf8(&welcome).unwrap_or("?")); - file::fileclose(gtxt); - - // Delete /foo/hello.txt - if file::unlink("/foo/hello.txt") < 0 { - panic!("failed to unlink /foo/hello.txt"); - } - - // Check that /foo is empty - let foo = fs::namei("/foo").unwrap_or_else(|| panic!("unable to open /foo")); - if !file::isdirempty(foo) { - panic!("/foo should be empty"); - } - fs::iput(foo); - - // Check that we cannot read file /foo/hello.txt - if let Some(f) = file::open("/foo/hello.txt", fcntl::O_RDONLY) { - file::fileclose(f); - panic!("could open /foo/hello.txt after unlinking"); - } + // Use println! to verify we reach this point (goes via Console::Write, not file) + + let c = match file::open("/console", fcntl::O_RDWR) { + Some(fd) => { + fd + } + None => { + panic!("Failed to open console"); + } + }; - // Write to /welcome.txt - let wtxt = - file::open("/welcome.txt", fcntl::O_RDONLY).unwrap_or_else(|| panic!("unable to open /welcome.txt")); - let welcome_cap = welcome.len() as i32; - let n = file::fileread(wtxt, &mut welcome, welcome_cap); - println!("Read {} chars from /welcome.txt:", n); - println!("{}\n", core::str::from_utf8(&welcome).unwrap_or("?")); - file::fileclose(wtxt); + let enter_message = b"\nEnter your name: "; + file::filewrite(c, enter_message, enter_message.len() as i32); + + let mut name = [0u8; 20]; + let nice_message = b"Nice to meet you! "; + let bye_message = b"BYE!\n"; + let namelen = file::fileread(c, &mut name, 20); + file::filewrite(c, nice_message, nice_message.len() as i32); + file::filewrite(c, &name[..namelen as usize], namelen); + file::filewrite(c, bye_message, bye_message.len() as i32); // Goodbye message is 5 bytes not 6 (Rust vs C string handling) + + file::fileclose(c); } extern "C" { @@ -92,6 +71,7 @@ pub extern "C" fn entryofrust() -> ! { lapic::lapicinit(); picirq::picinit(); ioapic::ioapic_init(); + console::consoleinit(); uart::uartinit(); ide::ideinit(); tvinit(); @@ -100,6 +80,7 @@ pub extern "C" fn entryofrust() -> ! { x86::sti(); fs::iinit(param::ROOTDEV); log::initlog(param::ROOTDEV); + file::mknod("/console", param::CONSOLE as i16, param::CONSOLE as i16); welcome(); loop { diff --git a/src/param.rs b/src/param.rs index 56333d0..4a269c3 100644 --- a/src/param.rs +++ b/src/param.rs @@ -3,5 +3,7 @@ pub const NCPU: usize = 8; // maximum number of CPUs pub const MAXOPBLOCKS: usize = 10; // max # of blocks any FS op writes pub const NFILE: usize = 100; // open files per system pub const NINODE: usize = 50; // maximum number of active i-nodes +pub const NDEV: usize = 10; // maximum major device number pub const ROOTDEV: u32 = 1; // device number of file system root disk pub const LOGSIZE: usize = MAXOPBLOCKS * 3; // max data blocks in on-disk log +pub const CONSOLE: usize = 1; // console device number