From 973ec7449b90bdb8c535086ab32671ff7dd4c375 Mon Sep 17 00:00:00 2001 From: Stephen Marz Date: Sun, 15 Mar 2020 11:09:01 -0400 Subject: [PATCH] Added comments to describe functions and enumerations. Also, assumptions have been spelled out. --- risc_v/ch9/src/block.rs | 27 ++++++++++++++++++++++++++- risc_v/ch9/src/virtio.rs | 20 +++++++++++++++++--- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/risc_v/ch9/src/block.rs b/risc_v/ch9/src/block.rs index 3f84d4f..6825f57 100755 --- a/risc_v/ch9/src/block.rs +++ b/risc_v/ch9/src/block.rs @@ -48,6 +48,14 @@ pub struct Config { unused1: [u8; 3], } +// The header/data/status is a block request +// packet. We send the header to tell the direction +// (blktype: IN/OUT) and then the starting sector +// we want to read. Then, we put the data buffer +// as the Data structure and finally an 8-bit +// status. The device will write one of three values +// in here: 0 = success, 1 = io error, 2 = unsupported +// operation. #[repr(C)] pub struct Header { blktype: u32, @@ -74,6 +82,10 @@ pub struct Request { } // Internal block device structure +// We keep our own used_idx and idx for +// descriptors. There is a shared index, but that +// tells us or the device if we've kept up with where +// we are for the available (us) or used (device) ring. pub struct BlockDevice { queue: *mut Queue, dev: *mut u32, @@ -213,15 +225,28 @@ pub fn setup_block_device(ptr: *mut u32) -> bool { pub fn fill_next_descriptor(bd: &mut BlockDevice, desc: Descriptor) -> u16 { unsafe { + // The ring structure increments here first. This allows us to skip + // index 0, which then in the used ring will show that .id > 0. This + // is one way to error check. We will eventually get back to 0 as + // this index is cyclical. However, it shows if the first read/write + // actually works. bd.idx = (bd.idx + 1) % VIRTIO_RING_SIZE as u16; (*bd.queue).desc[bd.idx as usize] = desc; if (*bd.queue).desc[bd.idx as usize].flags & virtio::VIRTIO_DESC_F_NEXT != 0 { + // If the next flag is set, we need another descriptor. (*bd.queue).desc[bd.idx as usize].next = (bd.idx + 1) % VIRTIO_RING_SIZE as u16; } bd.idx } } - +/// This is now a common block operation for both reads and writes. Therefore, +/// when one thing needs to change, we can change it for both reads and writes. +/// There is a lot of error checking that I haven't done. The block device reads +/// sectors at a time, which are 512 bytes. Therefore, our buffer must be capable +/// of storing multiples of 512 bytes depending on the size. The size is also +/// a multiple of 512, but we don't really check that. +/// We DO however, check that we aren't writing to an R/O device. This would +/// cause a I/O error if we tried to write to a R/O device. pub fn block_op(dev: usize, buffer: *mut u8, size: u32, offset: u64, write: bool) { unsafe { if let Some(bdev) = BLOCK_DEVICES[dev - 1].as_mut() { diff --git a/risc_v/ch9/src/virtio.rs b/risc_v/ch9/src/virtio.rs index 88e93cd..8853d2d 100755 --- a/risc_v/ch9/src/virtio.rs +++ b/risc_v/ch9/src/virtio.rs @@ -55,9 +55,8 @@ pub struct Used { pub struct Queue { pub desc: [Descriptor; VIRTIO_RING_SIZE], pub avail: Available, - // Calculating padding, we need the used ring to start on a page - // boundary. We take the page size, subtract the amount the descriptor ring takes - // then subtract the available structure and ring. + // Calculating padding, we need the used ring to start on a page boundary. We take the page size, subtract the + // amount the descriptor ring takes then subtract the available structure and ring. pub padding0: [u8; PAGE_SIZE - size_of::() * VIRTIO_RING_SIZE - size_of::()], pub used: Used, } @@ -97,6 +96,10 @@ pub enum DeviceTypes { Memory = 24, } +// Enumerations in Rust aren't easy to convert back +// and forth. Furthermore, we're going to use a u32 +// pointer, so we need to "undo" the scaling that +// Rust will do with the .add() function. impl MmioOffsets { pub fn val(self) -> usize { self as usize @@ -120,6 +123,8 @@ pub enum StatusField { DeviceNeedsReset = 64, } +// The status field will be compared to the status register. So, +// I've made some helper functions to checking that register easier. impl StatusField { pub fn val(self) -> usize { self as usize @@ -158,6 +163,11 @@ pub const MMIO_VIRTIO_END: usize = 0x1000_8000; pub const MMIO_VIRTIO_STRIDE: usize = 0x1000; pub const MMIO_VIRTIO_MAGIC: u32 = 0x74_72_69_76; +// The VirtioDevice is essentially a structure we can put into an array +// to determine what virtio devices are attached to the system. Right now, +// we're using the 1..=8 linearity of the VirtIO devices on QEMU to help +// with reducing the data structure itself. Otherwise, we might be forced +// to use an MMIO pointer. pub struct VirtioDevice { pub devtype: DeviceTypes, } @@ -283,6 +293,10 @@ pub fn setup_input_device(_ptr: *mut u32) -> bool { false } +// The External pin (PLIC) trap will lead us here if it is +// determined that interrupts 1..=8 are what caused the interrupt. +// In here, we try to figure out where to direct the interrupt +// and then handle it. pub fn handle_interrupt(interrupt: u32) { let idx = interrupt as usize - 1; unsafe {