diff --git a/Makefile b/Makefile index cb32d70..4f6546b 100644 --- a/Makefile +++ b/Makefile @@ -65,7 +65,7 @@ LDFLAGS = -z max-page-size=4096 $K/kernel: $(OBJS) $K/kernel.ld $U/initcode $R/src cargo +nightly -Z unstable-options -C $R build --release - $(OBJDUMP) -S $R/target/$(TARGET_TRIPLE)/release/librustkernel.a > $R/target/$(TARGET_TRIPLE)/release/librustkernel.asm +# $(OBJDUMP) -S $R/target/$(TARGET_TRIPLE)/release/librustkernel.a > $R/target/$(TARGET_TRIPLE)/release/librustkernel.asm $(LD) $(LDFLAGS) -T $K/kernel.ld -o $K/kernel $(OBJS) $R/target/$(TARGET_TRIPLE)/release/librustkernel.a $(OBJDUMP) -S $K/kernel > $K/kernel.asm $(OBJDUMP) -t $K/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $K/kernel.sym diff --git a/kernel/rustkernel/src/bio.rs b/kernel/rustkernel/src/bio.rs new file mode 100644 index 0000000..2fb579b --- /dev/null +++ b/kernel/rustkernel/src/bio.rs @@ -0,0 +1,135 @@ +//! Buffer cache. +//! +//! The buffer cache is a linked list of buf strctures holding +//! cached copies of disk block contents. Caching disk blocks +//! in memory reduces the number of disk reads and also provides +//! a synchronization point for disk blocks used by multiple processes. +//! +//! Interface: +//! - To get a buffer for a particular disk block, call bread. +//! - After changing buffer data, call bwrite to write it to disk. +//! - When done with the buffer, call brelse. +//! - Do not use the buffer after calling brelse. +//! - Only one process at a time can use a buffer, +//! so do not keep them longer than necessary. + +use crate::{ + sync::sleeplock::{holdingsleep, acquiresleep, releasesleep}, + sync::spinlock::Spinlock, + buf::Buffer, + param::NBUF, + virtio_disk::virtio_disk_rw, +}; +use core::ptr::addr_of_mut; + +pub struct BufferCache { + pub buffers: [Buffer; NBUF], +} +impl BufferCache { + /// Look through the buffer cache for block on device dev. + /// + /// If not found, allocate a buffer. + /// In either case, return locked buffer. + fn get(&mut self, dev: u32, blockno: u32) { + for buf in &mut self.buffers { + if buf.dev == dev && buf.blockno == blockno { + buf.refcnt += 1; + + } + } + } +} + +#[repr(C)] +pub struct BCache { + pub lock: Spinlock, + pub buf: [Buffer; NBUF], + pub head: Buffer, +} + +extern "C" { + pub static mut bcache: BCache; + pub fn binit(); + // pub fn bget(dev: u32, blockno: u32) -> *mut Buffer; + pub fn bread(dev: u32, blockno: u32) -> *mut Buffer; + pub fn bwrite(b: *mut Buffer); + pub fn brelse(b: *mut Buffer); + pub fn bpin(b: *mut Buffer); + pub fn bunpin(b: *mut Buffer); +} + +// pub static BUFFER_CACHE: Mutex = Mutex::new(); + +// #[no_mangle] +// pub unsafe extern "C" fn bget(dev: u32, blockno: u32) -> *mut Buffer { +// let mut b: *mut Buffer; +// let _guard = bcache.lock.lock(); +// +// // Is the block already cached? +// b = bcache.head.next; +// while b != addr_of_mut!(bcache.head) { +// if (*b).dev == dev && (*b).blockno == blockno { +// (*b).refcnt += 1; +// acquiresleep(addr_of_mut!((*b).lock)); +// // (*b).lock.lock_unguarded(); +// return b; +// } else { +// b = (*b).next; +// } +// } +// +// // Not cached. +// // Recycle the least recently used unused buffer. +// b = bcache.head.prev; +// while b != addr_of_mut!(bcache.head) { +// if (*b).refcnt == 0 { +// (*b).dev = dev; +// (*b).blockno = blockno; +// (*b).valid = 0; +// (*b).refcnt = 1; +// // (*b).lock.lock_unguarded(); +// acquiresleep(addr_of_mut!((*b).lock)); +// return b; +// } +// } +// +// panic!("bget: no buffers"); +// } + +// /// Return a locked buffer with the contents of the indicated block. +// #[no_mangle] +// pub unsafe extern "C" fn bread(dev: u32, blockno: u32) -> *mut Buffer { +// let b = bget(dev, blockno); +// +// if (*b).valid == 0 { +// virtio_disk_rw(b, 0); +// (*b).valid = 1; +// } +// +// b +// } +// +// #[no_mangle] +// pub unsafe extern "C" fn bwrite(b: *mut Buffer) { +// if holdingsleep(addr_of_mut!((*b).lock)) == 0 { +// // if !(*b).lock.held_by_current_proc() { +// panic!("bwrite"); +// } +// +// virtio_disk_rw(b, 1); +// } + +// #[no_mangle] +// pub unsafe extern "C" fn bpin(b: *mut Buffer) { +// let _guard = bcache.lock.lock(); +// (*b).refcnt += 1; +// // bcache.lock.unlock(); +// } +// +// #[no_mangle] +// pub unsafe extern "C" fn bunpin(b: *mut Buffer) { +// let _guard = bcache.lock.lock(); +// (*b).refcnt -= 1; +// // bcache.lock.unlock(); +// } +// \ No newline at end of file diff --git a/kernel/rustkernel/src/buf.rs b/kernel/rustkernel/src/buf.rs index ba07a49..785e603 100644 --- a/kernel/rustkernel/src/buf.rs +++ b/kernel/rustkernel/src/buf.rs @@ -1,7 +1,7 @@ use crate::{fs::BSIZE, sync::sleeplock::Sleeplock}; #[repr(C)] -pub struct Buf { +pub struct Buffer { /// Has data been read from disk? pub valid: i32, /// Does disk "own" buf? @@ -10,7 +10,7 @@ pub struct Buf { pub blockno: u32, pub lock: Sleeplock, pub refcnt: u32, - pub prev: *mut Buf, - pub next: *mut Buf, + pub prev: *mut Buffer, + pub next: *mut Buffer, pub data: [u8; BSIZE as usize], } diff --git a/kernel/rustkernel/src/lib.rs b/kernel/rustkernel/src/lib.rs index 7300d48..0890ac9 100644 --- a/kernel/rustkernel/src/lib.rs +++ b/kernel/rustkernel/src/lib.rs @@ -7,6 +7,7 @@ extern crate alloc; extern crate core; +pub mod bio; pub mod buf; pub mod console; pub mod file; @@ -23,6 +24,7 @@ pub mod syscall; pub mod sysproc; pub mod trap; pub mod uart; +pub mod virtio_disk; extern "C" { // pub fn printfinit(); @@ -33,7 +35,6 @@ extern "C" { pub fn binit(); pub fn iinit(); pub fn fileinit(); - pub fn virtio_disk_init(); pub fn userinit(); // pub fn scheduler(); } @@ -61,7 +62,7 @@ pub unsafe extern "C" fn main() -> ! { binit(); iinit(); fileinit(); - virtio_disk_init(); + virtio_disk::virtio_disk_init(); userinit(); STARTED = true; } else { @@ -79,11 +80,27 @@ pub unsafe extern "C" fn main() -> ! { #[panic_handler] fn panic_wrapper(panic_info: &core::panic::PanicInfo) -> ! { if let Some(s) = panic_info.payload().downcast_ref::<&str>() { - crate::printf::print!("panic: {}\n", s); + crate::printf::print!("kernel panic: {}\n", s); } else { crate::printf::print!("kernel panic\n"); } + // crate::printf::print!(" ______\n"); + // crate::printf::print!("< fuck!! >\n"); + // crate::printf::print!(" ------\n"); + // crate::printf::print!(" \\ ^__^ \n"); + // crate::printf::print!(" \\ (oo)\\_______\n"); + // crate::printf::print!(" (__)\\ )\\/\\\\\n"); + // crate::printf::print!(" ||----w |\n"); + // crate::printf::print!(" || ||\n"); + + crate::printf::print!("███████╗██╗ ██╗ ██████╗██╗ ██╗██╗██╗\n"); + crate::printf::print!("██╔════╝██║ ██║██╔════╝██║ ██╔╝██║██║\n"); + crate::printf::print!("█████╗ ██║ ██║██║ █████╔╝ ██║██║\n"); + crate::printf::print!("██╔══╝ ██║ ██║██║ ██╔═██╗ ╚═╝╚═╝\n"); + crate::printf::print!("██║ ╚██████╔╝╚██████╗██║ ██╗██╗██╗\n"); + crate::printf::print!("╚═╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝╚═╝╚═╝\n"); + unsafe { crate::PANICKED = true }; loop { diff --git a/kernel/rustkernel/src/virtio_disk.rs b/kernel/rustkernel/src/virtio_disk.rs new file mode 100644 index 0000000..24be969 --- /dev/null +++ b/kernel/rustkernel/src/virtio_disk.rs @@ -0,0 +1,193 @@ +//! Virtio device driver. +//! +//! For both the MMIO interface, and virtio descriptors. +//! Only tested with qemu. +//! +//! The virtio spec: https://docs.oasis-open.org/virtio/virtio/v1.1/virtio-v1.1.pdf +//! qemu ... -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 + +use crate::{sync::spinlock::Spinlock, buf::Buffer}; +use core::ffi::c_char; + +// Virtio MMIO control registers, mapped starting at 0x10001000 +// From qemu virtio_mmio.h + +/// 0x74726976 +pub const VIRTIO_MMIO_MAGIC_VALUE: u64 = 0x000u64; +/// Version - should be 2. +pub const VIRTIO_MMIO_VERSION: u64 = 0x004u64; +/// Device type. +/// +/// 1: Network +/// 2: Disk +pub const VIRTIO_MMIO_DEVICE_ID: u64 = 0x008u64; +/// 0x554d4551 +pub const VIRTIO_MMIO_VENDOR_ID: u64 = 0x00cu64; +pub const VIRTIO_MMIO_DEVICE_FEATURES: u64 = 0x010u64; +pub const VIRTIO_MMIO_DRIVER_FEATURES: u64 = 0x020u64; +/// Select queue, write-only. +pub const VIRTIO_MMIO_QUEUE_SEL: u64 = 0x030u64; +/// Max size of current queue, read-only. +pub const VIRTIO_MMIO_QUEUE_NUM_MAX: u64 = 0x034u64; +/// Size of current queue, write-only. +pub const VIRTIO_MMIO_QUEUE_NUM: u64 = 0x038u64; +/// Ready bit. +pub const VIRTIO_MMIO_QUEUE_READY: u64 = 0x044u64; +/// Write-only. +pub const VIRTIO_MMIO_QUEUE_NOTIFY: u64 = 0x050u64; +/// Read-only. +pub const VIRTIO_MMIO_INTERRUPT_STATUS: u64 = 0x060u64; +/// Write-only. +pub const VIRTIO_MMIO_INTERRUPT_ACK: u64 = 0x064u64; +/// Read/write. +pub const VIRTIO_MMIO_STATUS: u64 = 0x070u64; +/// Physical address for descriptor table, write-only. +pub const VIRTIO_MMIO_QUEUE_DESC_LOW: u64 = 0x080u64; +pub const VIRTIO_MMIO_QUEUE_DESC_HIGH: u64 = 0x084u64; +/// Physical address for available ring, write-only. +pub const VIRTIO_MMIO_DRIVER_DESC_LOW: u64 = 0x090u64; +pub const VIRTIO_MMIO_DRIVER_DESC_HIGH: u64 = 0x094u64; +/// Physical address for used ring, write-only. +pub const VIRTIO_MMIO_DEVICE_DESC_LOW: u64 = 0x0a0u64; +pub const VIRTIO_MMIO_DEVICE_DESC_HIGH: u64 = 0x0a4u64; + +// Status register bits, from qemu virtio_config.h. +pub const VIRTIO_CONFIG_S_ACKNOWLEDGE: u8 = 0x01u8; +pub const VIRTIO_CONFIG_S_DRIVER: u8 = 0x02u8; +pub const VIRTIO_CONFIG_S_DRIVER_OK: u8 = 0x04u8; +pub const VIRTIO_CONFIG_S_FEATURES_OK: u8 = 0x08u8; + +// Device feature bits +/// Disk is read-only. +pub const VIRTIO_BLK_F_RO: u8 = 5u8; +/// Supports SCSI command passthrough. +pub const VIRTIO_BLK_F_SCSI: u8 = 7u8; +/// Writeback mode available in config. +pub const VIRTIO_BLK_F_CONFIG_WCE: u8 = 11u8; +/// Support more than one vq. +pub const VIRTIO_BLK_F_MQ: u8 = 12u8; +pub const VIRTIO_F_ANY_LAYOUT: u8 = 27u8; +pub const VIRTIO_RING_F_INDIRECT_DESC: u8 = 28u8; +pub const VIRTIO_RING_F_EVENT_IDX: u8 = 29u8; + +/// This many virtio descriptors. +/// +/// Must be a power of two. +pub const NUM_DESCRIPTORS: usize = 8usize; + +/// A single descriptor, from the spec. +#[repr(C)] +pub struct VirtqDescriptor { + pub addr: u64, + pub len: u32, + pub flags: u16, + pub next: u16, +} + +/// Chained with another descriptor. +pub const VRING_DESC_F_NEXT: u16 = 1u16; +/// Device writes (vs read). +pub const VRING_DESC_F_WRITE: u16 = 2u16; + +/// The entire avail ring, from the spec. +#[repr(C)] +pub struct VirtqAvailable { + /// Always zero. + pub flags: u16, + /// Driver will write ring[idx] next. + pub idx: u16, + /// Descriptor numbers of chain heads. + pub ring: [u16; NUM_DESCRIPTORS], + pub unused: u16, +} + +/// One entry in the "used" ring, with which the +/// device tells the driver about completed requests. +#[repr(C)] +pub struct VirtqUsedElement { + /// Index of start of completed descriptor chain. + pub id: u32, + pub len: u32, +} + +#[repr(C)] +pub struct VirtqUsed { + /// Always zero. + pub flags: u16, + /// Device increments it when it adds a ring[] entry. + pub idx: u16, + pub ring: [VirtqUsedElement; NUM_DESCRIPTORS], +} + +// These are specific to virtio block devices (disks), +// Described in section 5.2 of the spec. + +/// Read the disk. +pub const VIRTIO_BLK_T_IN: u32 = 0u32; +/// Write the disk. +pub const VIRTIO_BLK_T_OUT: u32 = 1u32; + +/// The format of the first descriptor in a disk request. +/// +/// To be followed by two more descriptors containing +/// the block, and a one-byte status. +#[repr(C)] +pub struct VirtioBlockRequest { + /// 0: Write the disk. + /// 1: Read the disk. + pub kind: u32, + pub reserved: u32, + pub sector: u64, +} + +#[repr(C)] +pub struct DiskInfo { + pub b: *mut Buffer, + pub status: c_char, +} + +#[repr(C)] +pub struct Disk { + /// A set (not a ring) of DMA descriptors, with which the + /// driver tells the device where to read and write individual + /// disk operations. There are NUM descriptors. + /// + /// Most commands consist of a "chain" (linked list) + /// of a couple of these descriptors. + pub descriptors: *mut VirtqDescriptor, + /// A ring in which the driver writes descriptor numbers + /// that the driver would like the device to process. It + /// only includes the head descriptor of each chain. The + /// ring has NUM elements. + pub available: *mut VirtqAvailable, + /// A ring in which the device writes descriptor numbers + /// that the device has finished processing (just the + /// head of each chain). There are NUM used ring entries. + pub used: *mut VirtqUsed, + + // Our own book-keeping. + /// Is a descriptor free? + pub free: [c_char; NUM_DESCRIPTORS], + /// We've looked this far in used[2..NUM]. + pub used_idx: u16, + + /// Track info about in-flight operations, + /// for use when completion interrupt arrives. + /// + /// Indexed by first descriptor index of chain. + pub info: [DiskInfo; NUM_DESCRIPTORS], + + + /// Disk command headers. + /// One-for-one with descriptors, for convenience. + pub ops: [VirtioBlockRequest; NUM_DESCRIPTORS], + + pub vdisk_lock: Spinlock, +} + +extern "C" { + pub static mut disk: Disk; + pub fn virtio_disk_init(); + pub fn virtio_disk_rw(buf: *mut Buffer, write: i32); + pub fn virtio_disk_intr(); +}