From 64f2e05bd991353bfa26cef23bc34e8d70f2bbe9 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Wed, 4 Mar 2026 10:22:05 +0000 Subject: [PATCH 1/3] Implemented device virtual file system --- src/console.rs | 59 ++++++++++++++++++++++++++++++++++++++ src/file.rs | 78 ++++++++++++++++++++++++++++++++++++-------------- src/fs.rs | 23 +++++++++++++-- src/lib.rs | 45 +++++++---------------------- src/param.rs | 2 ++ 5 files changed, 150 insertions(+), 57 deletions(-) diff --git a/src/console.rs b/src/console.rs index 69b0669..cd4b939 100644 --- a/src/console.rs +++ b/src/console.rs @@ -1,5 +1,7 @@ use crate::uart::*; use core::fmt::*; +use crate::file::DEVSW; +use crate::param::CONSOLE; pub struct Console {} impl Write for Console { @@ -82,3 +84,60 @@ pub fn consoleintr(getc: fn() -> i32) { } } } + +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 (*input).r == (*input).w { + // Block until data is available + } + + // Read character and increment read pointer - mirrors C: input.buf[input.r++ % INPUT_BUF] + let c = (*input).buf[(*input).r % INPUT_BUF] as i32; + (*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 == '\n' as i32 { + 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] as i32); + } + 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/file.rs b/src/file.rs index 8f2d079..427448b 100644 --- a/src/file.rs +++ b/src/file.rs @@ -1,15 +1,35 @@ use core::str; use crate::buf::BSIZE; -use crate::constants::{T_DIR, T_FILE, DIRSIZ, DIRENT_SIZE}; +use crate::constants::{T_DIR, T_FILE, T_DEV, DIRSIZ, DIRENT_SIZE}; use crate::fcntl::{O_CREATE, O_RDONLY, O_RDWR, O_WRONLY}; use crate::fs; -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)] @@ -108,9 +128,9 @@ pub fn fileclose(f_idx: usize) { } if ff.type_ == FileType::Inode { - crate::log::begin_op(); + log::begin_op(); fs::iput(ff.ip); - crate::log::end_op(); + log::end_op(); } } @@ -190,7 +210,7 @@ pub fn filewrite(f_idx: usize, src: &[u8], n: i32) -> i32 { n1 = max; } - crate::log::begin_op(); + log::begin_op(); fs::iread(f.ip); let off = unsafe { FTABLE.file[f_idx].off }; let r = fs::writei(f.ip, &src[i as usize..], off, n1 as u32); @@ -199,7 +219,7 @@ pub fn filewrite(f_idx: usize, src: &[u8], n: i32) -> i32 { FTABLE.file[f_idx].off += r as u32; } } - crate::log::end_op(); + log::end_op(); if r < 0 { break; @@ -240,12 +260,12 @@ pub fn isdirempty(dp_idx: usize) -> bool { } pub fn unlink(path: &str, name: &mut [u8; DIRSIZ]) -> i32 { - crate::log::begin_op(); + log::begin_op(); let dp = match fs::nameiparent(path, name) { Some(idx) => idx, None => { - crate::log::end_op(); + log::end_op(); return -1; } }; @@ -256,7 +276,7 @@ pub fn unlink(path: &str, name: &mut [u8; DIRSIZ]) -> i32 { if name_str == "." || name_str == ".." { fs::iput(dp); - crate::log::end_op(); + log::end_op(); return -1; } @@ -265,7 +285,7 @@ pub fn unlink(path: &str, name: &mut [u8; DIRSIZ]) -> i32 { Some(idx) => idx, None => { fs::iput(dp); - crate::log::end_op(); + log::end_op(); return -1; } }; @@ -279,7 +299,7 @@ pub fn unlink(path: &str, name: &mut [u8; DIRSIZ]) -> i32 { if fs::inode_type(ip) == T_DIR && !isdirempty(ip) { fs::iput(ip); fs::iput(dp); - crate::log::end_op(); + log::end_op(); return -1; } @@ -298,7 +318,7 @@ pub fn unlink(path: &str, name: &mut [u8; DIRSIZ]) -> i32 { fs::iupdate(ip); fs::iput(ip); - crate::log::end_op(); + log::end_op(); 0 } @@ -345,13 +365,13 @@ fn create(path: &str, type_: i16, major: i16, minor: i16) -> Option { } pub fn open(path: &str, omode: i32) -> Option { - crate::log::begin_op(); + log::begin_op(); let ip = if (omode & O_CREATE) != 0 { let ip = match create(path, T_FILE as i16, 0, 0) { Some(ip) => ip, None => { - crate::log::end_op(); + log::end_op(); return None; } }; @@ -360,14 +380,14 @@ pub fn open(path: &str, omode: i32) -> Option { let ip = match fs::namei(path) { Some(ip) => ip, None => { - crate::log::end_op(); + log::end_op(); return None; } }; fs::iread(ip); if fs::inode_type(ip) == T_DIR && omode != O_RDONLY { fs::iput(ip); - crate::log::end_op(); + log::end_op(); return None; } ip @@ -377,7 +397,7 @@ pub fn open(path: &str, omode: i32) -> Option { Some(idx) => idx, None => { fs::iput(ip); - crate::log::end_op(); + log::end_op(); return None; } }; @@ -391,22 +411,38 @@ pub fn open(path: &str, omode: i32) -> Option { f.writable = (omode & O_WRONLY) != 0 || (omode & O_RDWR) != 0; } - crate::log::end_op(); + log::end_op(); Some(f_idx) } pub fn mkdir(path: &str) -> i32 { - crate::log::begin_op(); + log::begin_op(); let ip = match create(path, T_DIR as i16, 0, 0) { Some(ip) => ip, None => { - crate::log::end_op(); + log::end_op(); + return -1; + } + }; + + fs::iput(ip); + log::end_op(); + 0 +} + +pub fn mknod(path: &str, major: i16, minor: i16) -> i32 { + log::begin_op(); + + let ip = match create(path, T_DEV as i16, major, minor) { + Some(ip) => ip, + None => { + log::end_op(); return -1; } }; fs::iput(ip); - crate::log::end_op(); + log::end_op(); 0 } diff --git a/src/fs.rs b/src/fs.rs index 6d56bdd..12dcf4c 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -3,10 +3,11 @@ use core::cmp::min; use crate::bio; use crate::buf::BSIZE; use crate::log; -use crate::param::{NINODE, ROOTDEV}; +use crate::param::{NINODE, ROOTDEV, NDEV}; use crate::println; use crate::constants::{NDIRECT, NINDIRECT, DIRSIZ, DIRENT_SIZE, DINODE_SIZE, IPB, BPB, MAXFILE}; -use crate::constants::{ROOTINO}; +use crate::constants::{ROOTINO, T_DEV}; +use crate::file::DEVSW; pub use crate::constants::T_DIR; @@ -516,6 +517,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; } @@ -557,6 +567,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/lib.rs b/src/lib.rs index 211a050..133cbc6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,44 +54,19 @@ fn print_cstr(bytes: &[u8]) { } fn welcome() { - // Create and write /foo/hello.txt - file::mkdir("/foo"); + let c = file::open("console", fcntl::O_RDWR) + .unwrap_or_else(|| panic!("Failed to open console")); - 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); + file::filewrite(c, b"\nEnter your name: ", 18); - 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); - print_cstr(&welcome); - console::consputc('\n' as i32); - file::fileclose(gtxt); + let mut name = [0u8; 20]; + let namelen = file::fileread(c, &mut name, 20); - // Delete /foo/hello.txt - let mut name = [0u8; constants::DIRSIZ]; - file::unlink("/foo/hello.txt", &mut name); + file::filewrite(c, b"Nice to meet you! ", 18); + file::filewrite(c, &name[..namelen as usize], namelen); + file::filewrite(c, b"BYE!\n", 6); - let foo = fs::namei("/foo").unwrap_or_else(|| panic!("unable to open /foo")); - if !file::isdirempty(foo) { - panic!("/foo should be empty"); - } - - if let Some(_gtxt) = file::open("/foo/hello.txt", fcntl::O_RDONLY) { - panic!("Could open /foo/hello.txt after unlinking"); - } - - // Print welcome message - let wtxt = file::open("/welcome.txt", fcntl::O_RDONLY) - .unwrap_or_else(|| panic!("Unable to open /welcome.txt")); - let n = file::fileread(wtxt, &mut welcome, 512); - println!("Read {} chars from /welcome.txt:\n", n); - print_cstr(&welcome); - file::fileclose(wtxt); + file::fileclose(c); } extern "C" { @@ -104,6 +79,7 @@ pub extern "C" fn entryofrust() -> ! { lapic::lapicinit(); picirq::picinit(); ioapic::ioapic_init(); + console::consoleinit(); uart::uartinit(); ide::ideinit(); tvinit(); @@ -112,6 +88,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 From 03cd9c8d963087e5a456b8b0aa772f2a89469395 Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Wed, 4 Mar 2026 11:42:37 +0000 Subject: [PATCH 2/3] Fixed Consoleread to use volatile reads --- src/console.rs | 14 ++++++++------ src/lib.rs | 23 ++++++++++++++++------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/console.rs b/src/console.rs index cd4b939..ad6ce9a 100644 --- a/src/console.rs +++ b/src/console.rs @@ -1,4 +1,4 @@ -use crate::uart::*; +use crate::{println, uart::*}; use core::fmt::*; use crate::file::DEVSW; use crate::param::CONSOLE; @@ -71,13 +71,14 @@ pub fn consoleintr(getc: fn() -> i32) { } _ => { if c != 0 && (*input).e.wrapping_sub((*input).r) < INPUT_BUF { + let orig_c = c; let c = if c == '\r' as i32 { '\n' as i32 } else { c }; (*input).buf[(*input).e % INPUT_BUF] = c as u8; (*input).e += 1; consputc(c); if c == '\n' as i32 || c == CTRL_D || (*input).e == (*input).r + INPUT_BUF { (*input).w = (*input).e; - } + } } } } @@ -88,15 +89,16 @@ pub fn consoleintr(getc: fn() -> i32) { 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 (*input).r == (*input).w { - // Block until data is available + 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] as i32; (*input).r += 1; diff --git a/src/lib.rs b/src/lib.rs index 133cbc6..e5cedbe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,17 +54,26 @@ fn print_cstr(bytes: &[u8]) { } fn welcome() { - let c = file::open("console", fcntl::O_RDWR) - .unwrap_or_else(|| panic!("Failed to open console")); + // 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"); + } + }; - file::filewrite(c, b"\nEnter your name: ", 18); + let n = file::filewrite(c, b"\nEnter your name: ", 18); 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, b"Nice to meet you! ", 18); + file::filewrite(c, nice_message, nice_message.len() as i32); file::filewrite(c, &name[..namelen as usize], namelen); - file::filewrite(c, b"BYE!\n", 6); + 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); } @@ -88,7 +97,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); + file::mknod("/console", param::CONSOLE as i16, param::CONSOLE as i16); welcome(); loop { From 776250f8c59d21d7def200312c2ac4ad960d34bc Mon Sep 17 00:00:00 2001 From: Yash-Rawat-IIT-D Date: Wed, 4 Mar 2026 11:46:44 +0000 Subject: [PATCH 3/3] Restored missing enter_message --- src/console.rs | 5 ++--- src/lib.rs | 5 +++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/console.rs b/src/console.rs index ad6ce9a..7ad6727 100644 --- a/src/console.rs +++ b/src/console.rs @@ -1,4 +1,4 @@ -use crate::{println, uart::*}; +use crate::{uart::*}; use core::fmt::*; use crate::file::DEVSW; use crate::param::CONSOLE; @@ -71,7 +71,6 @@ pub fn consoleintr(getc: fn() -> i32) { } _ => { if c != 0 && (*input).e.wrapping_sub((*input).r) < INPUT_BUF { - let orig_c = c; let c = if c == '\r' as i32 { '\n' as i32 } else { c }; (*input).buf[(*input).e % INPUT_BUF] = c as u8; (*input).e += 1; @@ -89,7 +88,7 @@ pub fn consoleintr(getc: fn() -> i32) { 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 { diff --git a/src/lib.rs b/src/lib.rs index e5cedbe..0795bed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,8 +64,9 @@ fn welcome() { panic!("Failed to open console"); } }; - - let n = file::filewrite(c, b"\nEnter your name: ", 18); + + 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! ";