rewrite virtio_disk.h
This commit is contained in:
parent
572545cb8f
commit
de4815dd5e
2
Makefile
2
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
|
||||
|
135
kernel/rustkernel/src/bio.rs
Normal file
135
kernel/rustkernel/src/bio.rs
Normal file
@ -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<BufferCache> = 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();
|
||||
// }
|
||||
//
|
@ -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],
|
||||
}
|
||||
|
@ -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 {
|
||||
|
193
kernel/rustkernel/src/virtio_disk.rs
Normal file
193
kernel/rustkernel/src/virtio_disk.rs
Normal file
@ -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();
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user