From e5f3eb7f6bc38caf4f9b008c8bd6a0398ac47f7a Mon Sep 17 00:00:00 2001 From: Stephen Marz Date: Sat, 25 Apr 2020 19:34:20 -0400 Subject: [PATCH] Removed mapping the kernel memory into a process' virtual address space. --- risc_v/src/minixfs.rs | 40 +++++++++++++++++++++++++++++----------- risc_v/src/process.rs | 27 ++++++++++++++++++--------- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/risc_v/src/minixfs.rs b/risc_v/src/minixfs.rs index 674ea65..83d0026 100755 --- a/risc_v/src/minixfs.rs +++ b/risc_v/src/minixfs.rs @@ -155,15 +155,23 @@ impl MinixFileSystem { } impl FileSystem for MinixFileSystem { + /// Init is where we would cache the superblock and inode to avoid having to read + /// it over and over again, like we do for read right now. fn init(_bdev: usize) -> bool { false } + /// The goal of open is to traverse the path given by path. If we cache the inodes + /// in RAM, it might make this much quicker. For now, this doesn't do anything since + /// we're just testing read based on if we know the Inode we're looking for. fn open(_path: &String) -> Result { Err(FsError::FileNotFound) } fn read(desc: &Descriptor, buffer: *mut u8, size: u32, offset: u32) -> u32 { + // Our strategy here is to use blocks to see when we need to start reading + // based on the offset. That's offset_block. Then, the actual byte within + // that block that we need is offset_byte. let mut blocks_seen = 0u32; let offset_block = offset / BLOCK_SIZE; let mut offset_byte = offset % BLOCK_SIZE; @@ -173,6 +181,7 @@ impl FileSystem for MinixFileSystem { // The inode couldn't be read, for some reason. return 0; } + // We've already checked is_none() above, so we can safely unwrap here. let inode = inode_result.unwrap(); // First, the _size parameter (now in bytes_left) is the size of the buffer, not // necessarily the size of the file. If our buffer is bigger than the file, we're OK. @@ -189,6 +198,22 @@ impl FileSystem for MinixFileSystem { // 10 bytes, we have to read the entire block (really only 512 bytes of the block) first. // So, we use the block_buffer as the middle man, which is then copied into the buffer. let mut block_buffer = BlockBuffer::new(BLOCK_SIZE); + // Triply indirect zones point to a block of pointers (BLOCK_SIZE / 4). Each one of those pointers + // points to another block of pointers (BLOCK_SIZE / 4). Each one of those pointers yet again points + // to another block of pointers (BLOCK_SIZE / 4). This is why we have indirect, iindirect (doubly), + // and iiindirect (triply). + let mut indirect_buffer = BlockBuffer::new(BLOCK_SIZE); + let mut iindirect_buffer = BlockBuffer::new(BLOCK_SIZE); + let mut iiindirect_buffer = BlockBuffer::new(BLOCK_SIZE); + // I put the pointers *const u32 here. That means we will allocate the indirect, + // doubly indirect, and triply indirect even for small files. I initially had these + // in their respective scopes, but that required us to recreate the indirect buffer for + // doubly indirect and both the indirect and doubly indirect buffers for the + // triply indirect. Not sure which is better, but I probably wasted brain cells + // on this. + let izones = indirect_buffer.get() as *const u32; + let iizones = iindirect_buffer.get() as *const u32; + let iiizones = iiindirect_buffer.get() as *const u32; // //////////////////////////////////////////// // // DIRECT ZONES @@ -257,7 +282,6 @@ impl FileSystem for MinixFileSystem { // point to zones where the data can be found. Just like with the direct zones, // we need to make sure the zone isn't 0. A zone of 0 means skip it. if inode.zones[7] != 0 { - let mut indirect_buffer = BlockBuffer::new(BLOCK_SIZE); syc_read(desc, indirect_buffer.get_mut(), BLOCK_SIZE, BLOCK_SIZE * inode.zones[7]); let izones = indirect_buffer.get() as *const u32; for i in 0..num_indirect_pointers { @@ -298,17 +322,16 @@ impl FileSystem for MinixFileSystem { // // DOUBLY INDIRECT ZONES // //////////////////////////////////////////// if inode.zones[8] != 0 { - let mut indirect_buffer = BlockBuffer::new(BLOCK_SIZE); - let mut iindirect_buffer = BlockBuffer::new(BLOCK_SIZE); syc_read(desc, indirect_buffer.get_mut(), BLOCK_SIZE, BLOCK_SIZE * inode.zones[8]); - let izones = indirect_buffer.get() as *const u32; - let iizones = iindirect_buffer.get() as *const u32; unsafe { for i in 0..num_indirect_pointers { if izones.add(i).read() != 0 { syc_read(desc, iindirect_buffer.get_mut(), BLOCK_SIZE, BLOCK_SIZE * izones.add(i).read()); for j in 0..num_indirect_pointers { if iizones.add(j).read() != 0 { + // Notice that this inner code is the same for all end-zone pointers. I'm thinking about + // moving this out of here into a function of its own, but that might make it harder + // to follow. if offset_block <= blocks_seen { syc_read( desc, @@ -345,13 +368,7 @@ impl FileSystem for MinixFileSystem { // // TRIPLY INDIRECT ZONES // //////////////////////////////////////////// if inode.zones[9] != 0 { - let mut indirect_buffer = BlockBuffer::new(BLOCK_SIZE); - let mut iindirect_buffer = BlockBuffer::new(BLOCK_SIZE); - let mut iiindirect_buffer = BlockBuffer::new(BLOCK_SIZE); syc_read(desc, indirect_buffer.get_mut(), BLOCK_SIZE, BLOCK_SIZE * inode.zones[9]); - let izones = indirect_buffer.get() as *const u32; - let iizones = iindirect_buffer.get() as *const u32; - let iiizones = iiindirect_buffer.get() as *const u32; unsafe { for i in 0..num_indirect_pointers { if izones.add(i).read() != 0 { @@ -366,6 +383,7 @@ impl FileSystem for MinixFileSystem { ); for k in 0..num_indirect_pointers { if iiizones.add(k).read() != 0 { + // Hey look! This again. if offset_block <= blocks_seen { syc_read( desc, diff --git a/risc_v/src/process.rs b/risc_v/src/process.rs index b3809e1..4c2a676 100644 --- a/risc_v/src/process.rs +++ b/risc_v/src/process.rs @@ -33,6 +33,10 @@ pub static mut PROCESS_LIST: Option> = None; // it's probably easier and faster just to increase the pid: static mut NEXT_PID: u16 = 1; +// The following set_* and get_by_pid functions are C-style functions +// They probably need to be re-written in a more Rusty style, but for +// now they are how we control processes by PID. + /// Set a process' state to running. This doesn't do any checks. /// If this PID is not found, this returns false. Otherwise, it /// returns true. @@ -157,7 +161,12 @@ fn init_process() { // the scheduler is called in an interrupt context, nothing else // can happen until a process becomes available. println!("Init is still here :), alright, back to sleep."); + // 500 wfi's should take 500 context switches before we print Init is still here. + // Depending on our context switch time, this might be around 3 seconds. for _ in 0..500 { + // We can only write wfi here because init_process is being ran + // as a kernel process. If we ran this as a user process, it'd + // need a system call to execute a privileged instruction. unsafe { asm!("wfi") }; } } @@ -383,6 +392,14 @@ pub struct Process { sleep_until: usize, } +// Most of this operating system runs more of a C-style, where +// we have direct access to the structure members. By default, Rust +// will make them private unless we add the keyword pub in front of +// EVERY member. I wrote the process structure this way to show +// both ways Rust allows us to access members. Just like Python, +// the first parameter (the *this parameter in C++) is a reference +// to ourself. We can write static functions as a member of this +// structure by omitting a self. impl Process { pub fn get_frame_address(&self) -> usize { self.frame as usize @@ -422,7 +439,7 @@ impl Process { pub fn new_default(func: fn()) -> Self { let func_addr = func as usize; - let func_vaddr = func_addr; //- 0x6000_0000; + let func_vaddr = func_addr; // println!("func_addr = {:x} -> {:x}", func_addr, func_vaddr); // We will convert NEXT_PID below into an atomic increment when // we start getting into multi-hart processing. For now, we want @@ -473,14 +490,6 @@ impl Process { let modifier = i * 0x1000; map(pt, func_vaddr + modifier, func_addr + modifier, EntryBits::UserReadWriteExecute.val(), 0); } - // This is the make_syscall function - // The reason we need this is because we're running a process - // that is inside of the kernel. When we start loading from a block - // devices, we can load the instructions anywhere in memory. - for i in 0..=7 { - let addr = 0x8000_0000 | i << 12; - map(pt, addr, addr, EntryBits::UserReadExecute.val(), 0); - } ret_proc } }