Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 65 additions & 2 deletions src/console.rs
Original file line number Diff line number Diff line change
@@ -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() {
Expand Down Expand Up @@ -55,7 +58,7 @@ pub fn consoleintr(getc: fn() -> Option<u8>) {
_ => {
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 {
Expand All @@ -64,6 +67,8 @@ pub fn consoleintr(getc: fn() -> Option<u8>) {
}
}
}
} else {
break;
}
}
}
Expand All @@ -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);
}
}
2 changes: 1 addition & 1 deletion src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) ----------------------------------------------
Expand Down
40 changes: 38 additions & 2 deletions src/file.rs
Original file line number Diff line number Diff line change
@@ -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<fn(usize, &mut [u8], i32) -> i32>,
pub write: Option<fn(usize, &[u8], i32) -> 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)]
Expand Down Expand Up @@ -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();
Expand Down
26 changes: 22 additions & 4 deletions src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Dirent>();

#[repr(C)]
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down
1 change: 1 addition & 0 deletions src/fs_h.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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] {
Expand Down
67 changes: 24 additions & 43 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand All @@ -92,6 +71,7 @@ pub extern "C" fn entryofrust() -> ! {
lapic::lapicinit();
picirq::picinit();
ioapic::ioapic_init();
console::consoleinit();
uart::uartinit();
ide::ideinit();
tvinit();
Expand All @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions src/param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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