From d86c65a380509653b9e47eabde666f886f54673a Mon Sep 17 00:00:00 2001 From: Stephen Marz Date: Sat, 25 Apr 2020 17:34:06 -0400 Subject: [PATCH] Added comments and removed llvm_asm -- it looks like I'll have to use this in the future --- risc_v/src/main.rs | 5 ++- risc_v/src/minixfs.rs | 49 +++++++++++++++++++++++++--- risc_v/src/syscall.rs | 76 +++++++++++++++++++++++++------------------ 3 files changed, 93 insertions(+), 37 deletions(-) diff --git a/risc_v/src/main.rs b/risc_v/src/main.rs index 8a5ae18..06d6715 100755 --- a/risc_v/src/main.rs +++ b/risc_v/src/main.rs @@ -4,7 +4,6 @@ #![no_main] #![no_std] #![feature(panic_info_message, - llvm_asm, asm, global_asm, allocator_api, @@ -124,6 +123,10 @@ extern "C" { fn switch_to_user(frame: usize) -> !; } +/// Switch to user is an assembly function that loads +/// a frame. Since it will jump to another program counter, +/// it will never return back here. We don't care if we leak +/// the stack, since we will recapture the stack during m_trap. fn rust_switch_to_user(frame: usize) -> ! { unsafe { switch_to_user(frame); diff --git a/risc_v/src/minixfs.rs b/risc_v/src/minixfs.rs index 620098b..edf3327 100755 --- a/risc_v/src/minixfs.rs +++ b/risc_v/src/minixfs.rs @@ -184,6 +184,10 @@ impl FileSystem for MinixFileSystem { size }; let mut bytes_read = 0u32; + // The block buffer automatically drops when we quit early due to an error or we've read enough. + // This will be the holding port when we go out and read a block. Recall that even if we want + // 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); // //////////////////////////////////////////// @@ -207,24 +211,39 @@ impl FileSystem for MinixFileSystem { // That makes it easy since all we have to do is multiply the block size // by whatever we get. If it's 0, we skip it and move on. let zone_offset = inode.zones[i] * BLOCK_SIZE; + // We read the zone, which is where the data is located. The zone offset is simply the block + // size times the zone number. This makes it really easy to read! syc_read(desc, block_buffer.get_mut(), BLOCK_SIZE, zone_offset); + // There's a little bit of math to see how much we need to read. We don't want to read + // more than the buffer passed in can handle, and we don't want to read if we haven't + // taken care of the offset. For example, an offset of 10000 with a size of 2 means we + // can only read bytes 10,000 and 10,001. let read_this_many = if BLOCK_SIZE - offset_byte > bytes_left { bytes_left } else { BLOCK_SIZE - offset_byte }; + // Once again, here we actually copy the bytes into the final destination, the buffer. This memcpy + // is written in cpu.rs. unsafe { memcpy(buffer.add(bytes_read as usize), block_buffer.get().add(offset_byte as usize), read_this_many as usize); } + // Regardless of whether we have an offset or not, we reset the offset byte back to 0. This + // probably will get set to 0 many times, but who cares? offset_byte = 0; + // Reset the statistics to see how many bytes we've read versus how many are left. bytes_read += read_this_many; bytes_left -= read_this_many; + // If no more bytes are left, then we're done. if bytes_left == 0 { return bytes_read; } } + // The blocks_seen is for the offset. We need to skip a certain number of blocks FIRST before getting + // to the offset. The reason we need to read the zones is because we need to skip zones of 0, and they + // do not contribute as a "seen" block. blocks_seen += 1; } // //////////////////////////////////////////// @@ -238,6 +257,7 @@ impl FileSystem for MinixFileSystem { 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 { + // Where do I put unsafe? Dereferencing the pointers and memcpy are the unsafe functions. unsafe { if izones.add(i).read() != 0 { if offset_block <= blocks_seen { @@ -352,6 +372,8 @@ impl FileSystem for MinixFileSystem { } } } + // Anyone else love this stairstep style? I probably should put the pointers in a function by themselves, + // but I think that'll make it more difficult to see what's actually happening. bytes_read } @@ -373,10 +395,16 @@ impl FileSystem for MinixFileSystem { } } -pub fn syc_read(desc: &Descriptor, buffer: *mut u8, size: u32, offset: u32) -> u8 { +/// This is a wrapper function around the syscall_block_read. This allows me to do +/// other things before I call the system call (or after). However, all the things I +/// wanted to do are no longer there, so this is a worthless function. +fn syc_read(desc: &Descriptor, buffer: *mut u8, size: u32, offset: u32) -> u8 { syscall_block_read(desc.blockdev, buffer, size, offset) } +// We have to start a process when reading from a file since the block +// device will block. We only want to block in a process context, not an +// interrupt context. struct ProcArgs { pub pid: u16, pub dev: usize, @@ -386,29 +414,42 @@ struct ProcArgs { pub node: u32, } +// This is the actual code ran inside of the read process. fn read_proc(args_addr: usize) { let args_ptr = args_addr as *mut ProcArgs; let args = unsafe { args_ptr.as_ref().unwrap() }; + // The descriptor will come from the user after an open() call. However, + // for now, all we really care about is args.dev, args.node, and args.pid. let desc = Descriptor { blockdev: args.dev, node: args.node, loc: 0, size: 500, pid: args.pid, }; + // Start the read! Since we're in a kernel process, we can block by putting this + // process into a waiting state and wait until the block driver returns. let bytes = MinixFileSystem::read(&desc, args.buffer, args.size, args.offset); // Let's write the return result into regs[10], which is A0. - let ptr = unsafe { get_by_pid(args.pid) }; - if !ptr.is_null() { - unsafe { + unsafe { + let ptr = get_by_pid(args.pid); + if !ptr.is_null() { (*(*ptr).get_frame()).regs[10] = bytes as usize; } } + // This is the process making the system call. The system itself spawns another process + // which goes out to the block device. Since we're passed the read call, we need to awaken + // the process and get it ready to go. The only thing this process needs to clean up is the + // tfree(), but the user process doesn't care about that. set_running(args.pid); + + // tfree() is used to free a pointer created by talloc. tfree(args_ptr); } +/// System calls will call process_read, which will spawn off a kernel process to read +/// the requested data. pub fn process_read(pid: u16, dev: usize, node: u32, buffer: *mut u8, size: u32, offset: u32) { // println!("FS read {}, {}, 0x{:x}, {}, {}", pid, dev, buffer as usize, size, offset); let args = talloc::().unwrap(); diff --git a/risc_v/src/syscall.rs b/risc_v/src/syscall.rs index 7c7ac56..b98f962 100755 --- a/risc_v/src/syscall.rs +++ b/risc_v/src/syscall.rs @@ -6,19 +6,22 @@ use crate::{block::block_op, cpu::TrapFrame, minixfs, - process::{delete_process, set_sleeping, set_waiting}}; + page::{virt_to_phys, Table}, + process::{delete_process, get_by_pid, set_sleeping, set_waiting}}; -pub fn do_syscall(mepc: usize, frame: *mut TrapFrame) -> usize { +/// do_syscall is called from trap.rs to invoke a system call. No discernment is +/// made here whether this is a U-mode, S-mode, or M-mode system call. +/// Since we can't do anything unless we dereference the passed pointer, +/// I went ahead and made the entire function unsafe. +pub unsafe fn do_syscall(mepc: usize, frame: *mut TrapFrame) -> usize { let syscall_number; - unsafe { - // Libgloss expects the system call number in A7, so let's follow - // their lead. - // A7 is X17, so it's register number 17. - syscall_number = (*frame).regs[17]; - } + // Libgloss expects the system call number in A7, so let's follow + // their lead. + // A7 is X17, so it's register number 17. + syscall_number = (*frame).regs[17]; match syscall_number { - 0 | 93 => unsafe { + 0 | 93 => { // Exit // Currently, we cannot kill a process, it runs forever. We will delete // the process later and free the resources, but for now, we want to get @@ -30,30 +33,48 @@ pub fn do_syscall(mepc: usize, frame: *mut TrapFrame) -> usize { println!("Test syscall"); mepc + 4 }, - 2 => unsafe { + 2 => { // Sleep set_sleeping((*frame).pid as u16, (*frame).regs[10]); 0 }, - 63 => unsafe { + 63 => { // Read system call // This is an asynchronous call. This will get the process going. We won't hear the answer until // we an interrupt back. // TODO: The buffer is a virtual memory address that needs to be translated to a physical memory // location. // This needs to be put into a process and ran. + // The buffer (regs[12]) needs to be translated when ran from a user process using virt_to_phys. + // If this turns out to be a page fault, we need to NOT proceed with the read! + let mut physical_buffer = (*frame).regs[12]; + // If the MMU is turned on, we have to translate the address. Eventually, I will put this + // code into a convenient function, but for now, it will show how translation will be done. + if (*frame).satp != 0 { + let p = get_by_pid((*frame).pid as u16); + let table = ((*p).get_table_address() as *mut Table).as_ref().unwrap(); + let paddr = virt_to_phys(table, (*frame).regs[12]); + if paddr.is_none() { + (*frame).regs[10] = -1isize as usize; + return mepc + 4; + } + physical_buffer = paddr.unwrap(); + } + // TODO: Not only do we need to check the buffer, but it is possible that the buffer spans + // multiple pages. We need to check all pages that this might span. We can't just do paddr + // and paddr + size, since there could be a missing page somewhere in between. let _ = minixfs::process_read( - (*frame).pid as u16, - (*frame).regs[10] as usize, - (*frame).regs[11] as u32, - (*frame).regs[12] as *mut u8, - (*frame).regs[13] as u32, - (*frame).regs[14] as u32 - ); + (*frame).pid as u16, + (*frame).regs[10] as usize, + (*frame).regs[11] as u32, + physical_buffer as *mut u8, + (*frame).regs[13] as u32, + (*frame).regs[14] as u32, + ); // If we return 0, the trap handler will schedule another process. 0 }, - 180 => unsafe { + 180 => { // println!( // "Pid: {}, Dev: {}, Buffer: 0x{:x}, Size: {}, Offset: {}", // (*frame).pid, @@ -63,14 +84,7 @@ pub fn do_syscall(mepc: usize, frame: *mut TrapFrame) -> usize { // (*frame).regs[13] // ); set_waiting((*frame).pid as u16); - let _ = block_op((*frame).regs[10], - (*frame).regs[11] as *mut u8, - (*frame).regs[12] as u32, - (*frame).regs[13] as u64, - false, - (*frame).pid as u16 - - ); + let _ = block_op((*frame).regs[10], (*frame).regs[11] as *mut u8, (*frame).regs[12] as u32, (*frame).regs[13] as u64, false, (*frame).pid as u16); 0 }, _ => { @@ -81,17 +95,15 @@ pub fn do_syscall(mepc: usize, frame: *mut TrapFrame) -> usize { } extern "C" { - fn make_syscall(sysno: usize, arg0: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) -> usize; + fn make_syscall(sysno: usize, arg0: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) -> usize; } fn do_make_syscall(sysno: usize, arg0: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) -> usize { - unsafe { - make_syscall(sysno, arg0, arg1, arg2, arg3, arg4, arg5) - } + unsafe { make_syscall(sysno, arg0, arg1, arg2, arg3, arg4, arg5) } } pub fn syscall_exit() { - let _ = do_make_syscall(93, 0, 0, 0, 0, 0, 0); + let _ = do_make_syscall(93, 0, 0, 0, 0, 0, 0); } pub fn syscall_fs_read(dev: usize, inode: u32, buffer: *mut u8, size: u32, offset: u32) -> usize {