diff --git a/crate/memory/src/paging/mod.rs b/crate/memory/src/paging/mod.rs index e663747..0c7ecc1 100644 --- a/crate/memory/src/paging/mod.rs +++ b/crate/memory/src/paging/mod.rs @@ -45,11 +45,20 @@ pub trait PageTable { /// When 'vaddr' is not mapped, map it to 'paddr'. fn map_if_not_exists(&mut self, vaddr: VirtAddr, paddr: usize) -> bool { - if let None = self.get_entry(vaddr) { - self.map(vaddr, paddr); - true - } else { - false + let entry = self.get_entry(vaddr); + match entry { + None => { + self.map(vaddr, paddr); + true + }, + Some(page_table_entry) => { + if page_table_entry.present() { + false + } else { + self.map(vaddr, paddr); + true + } + } } } } diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index b6c5217..76d496b 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -26,10 +26,12 @@ sv39 = [] board_u540 = ["sv39"] # (for aarch64 RaspberryPi3) nographic = [] -board_raspi3 = ["bcm2837"] +board_raspi3 = ["bcm2837", "link_user"] # (for riscv64) board_k210 = ["m_mode"] raspi3_use_generic_timer = ["bcm2837/use_generic_timer"] +# Hard link user program +link_user = [] [profile.dev] # MUST >= 1 : Enable RVO to avoid stack overflow diff --git a/kernel/Makefile b/kernel/Makefile index fcee52d..43d09bc 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -77,7 +77,9 @@ qemu_opts += \ else ifeq ($(arch), riscv32) qemu_opts += \ -machine virt \ - -kernel $(bin) + -kernel $(bin) \ + -drive file=$(SFSIMG),format=raw,id=sfs \ + -device virtio-blk-device,drive=sfs ifdef m_mode qemu_opts += -cpu rv32imacu-nommu endif @@ -85,7 +87,9 @@ endif else ifeq ($(arch), riscv64) qemu_opts += \ -machine virt \ - -kernel $(bin) + -kernel $(bin) \ + -drive file=$(SFSIMG),format=raw,id=sfs \ + -device virtio-blk-device,drive=sfs ifdef m_mode qemu_opts += -cpu rv64imacu-nommu endif @@ -93,7 +97,7 @@ endif else ifeq ($(arch), aarch64) qemu_opts += \ -machine $(board) \ - -serial null \ + -serial null -serial mon:stdio \ -kernel $(bin) endif @@ -103,8 +107,6 @@ endif ifeq ($(graphic), off) qemu_opts += -nographic -else -qemu_opts += -serial stdio endif ### build args ### @@ -192,6 +194,7 @@ justrunnet: build justrunui: build @qemu-system-$(arch) $(qemu_opts) \ -device virtio-gpu-device \ + -device virtio-mouse-device debug: $(kernel) $(bin) @qemu-system-$(arch) $(qemu_opts) -s -S & diff --git a/kernel/src/drivers/block/mod.rs b/kernel/src/drivers/block/mod.rs new file mode 100644 index 0000000..10ccd0c --- /dev/null +++ b/kernel/src/drivers/block/mod.rs @@ -0,0 +1 @@ +pub mod virtio_blk; \ No newline at end of file diff --git a/kernel/src/drivers/block/virtio_blk.rs b/kernel/src/drivers/block/virtio_blk.rs new file mode 100644 index 0000000..d373c66 --- /dev/null +++ b/kernel/src/drivers/block/virtio_blk.rs @@ -0,0 +1,184 @@ +use alloc::prelude::*; +use alloc::sync::Arc; +use alloc::vec; +use core::cmp::min; +use core::fmt; +use core::mem::{size_of, zeroed}; +use core::slice; + +use bitflags::*; +use device_tree::Node; +use device_tree::util::SliceRead; +use log::*; +use rcore_memory::PAGE_SIZE; +use rcore_memory::paging::PageTable; +use volatile::Volatile; + +use simple_filesystem::BlockedDevice; + +use crate::arch::cpu; +use crate::memory::active_table; +use crate::sync::SpinNoIrqLock as Mutex; + +use super::super::{DeviceType, Driver, DRIVERS}; +use super::super::bus::virtio_mmio::*; + +pub struct VirtIOBlk { + interrupt_parent: u32, + interrupt: u32, + header: usize, + queue: VirtIOVirtqueue, + capacity: usize +} + +#[derive(Clone)] +pub struct VirtIOBlkDriver(Arc>); + + +#[repr(C)] +#[derive(Debug)] +struct VirtIOBlkConfig { + capacity: Volatile, // number of 512 sectors +} + +#[repr(C)] +#[derive(Default)] +struct VirtIOBlkReq { + req_type: u32, + reserved: u32, + sector: u64, +} + +#[repr(C)] +struct VirtIOBlkResp { + data: [u8; VIRTIO_BLK_BLK_SIZE], + status: u8 +} + +const VIRTIO_BLK_T_IN: u32 = 0; +const VIRTIO_BLK_T_OUT: u32 = 1; + +const VIRTIO_BLK_S_OK: u8 = 0; +const VIRTIO_BLK_S_IOERR: u8 = 1; +const VIRTIO_BLK_S_UNSUPP: u8 = 2; + +const VIRTIO_BLK_BLK_SIZE: usize = 512; + +bitflags! { + struct VirtIOBlkFeature : u64 { + const BARRIER = 1 << 0; + const SIZE_MAX = 1 << 1; + const SEG_MAX = 1 << 2; + const GEOMETRY = 1 << 4; + const RO = 1 << 5; + const BLK_SIZE = 1 << 6; + const SCSI = 1 << 7; + const FLUSH = 1 << 9; + const TOPOLOGY = 1 << 10; + const CONFIG_WCE = 1 << 11; + const DISCARD = 1 << 13; + const WRITE_ZEROES = 1 << 14; + // device independent + const NOTIFY_ON_EMPTY = 1 << 24; // legacy + const ANY_LAYOUT = 1 << 27; // legacy + const RING_INDIRECT_DESC = 1 << 28; + const RING_EVENT_IDX = 1 << 29; + const UNUSED = 1 << 30; // legacy + const VERSION_1 = 1 << 32; // detect legacy + const ACCESS_PLATFORM = 1 << 33; // since virtio v1.1 + const RING_PACKED = 1 << 34; // since virtio v1.1 + const IN_ORDER = 1 << 35; // since virtio v1.1 + const ORDER_PLATFORM = 1 << 36; // since virtio v1.1 + const SR_IOV = 1 << 37; // since virtio v1.1 + const NOTIFICATION_DATA = 1 << 38; // since virtio v1.1 + } +} + +impl Driver for VirtIOBlkDriver { + fn try_handle_interrupt(&mut self) -> bool { + let mut driver = self.0.lock(); + + // ensure header page is mapped + active_table().map_if_not_exists(driver.header as usize, driver.header as usize); + let header = unsafe { &mut *(driver.header as *mut VirtIOHeader) }; + let interrupt = header.interrupt_status.read(); + if interrupt != 0 { + header.interrupt_ack.write(interrupt); + debug!("Got interrupt {:?}", interrupt); + return true; + } + return false; + } + + fn device_type(&self) -> DeviceType { + DeviceType::Block + } +} + +impl BlockedDevice for VirtIOBlkDriver { + const BLOCK_SIZE_LOG2: u8 = 9; // 512 + fn read_at(&mut self, block_id: usize, buf: &mut [u8]) -> bool { + let mut driver = self.0.lock(); + // ensure header page is mapped + active_table().map_if_not_exists(driver.header as usize, driver.header as usize); + + let mut req = VirtIOBlkReq::default(); + req.req_type = VIRTIO_BLK_T_IN; + req.reserved = 0; + req.sector = block_id as u64; + let input = [0; size_of::()]; + let output = unsafe { slice::from_raw_parts(&req as *const VirtIOBlkReq as *const u8, size_of::()) }; + driver.queue.add_and_notify(&[&input], &[output], 0); + driver.queue.get_block(); + let resp = unsafe { &*(&input as *const u8 as *const VirtIOBlkResp) }; + if resp.status == VIRTIO_BLK_S_OK { + let len = min(buf.len(), VIRTIO_BLK_BLK_SIZE); + buf[..len].clone_from_slice(&resp.data[..len]); + true + } else { + false + } + } + + fn write_at(&mut self, block_id: usize, buf: &[u8]) -> bool { + unimplemented!() + } +} + +pub fn virtio_blk_init(node: &Node) { + let reg = node.prop_raw("reg").unwrap(); + let from = reg.as_slice().read_be_u64(0).unwrap(); + let header = unsafe { &mut *(from as *mut VirtIOHeader) }; + + header.status.write(VirtIODeviceStatus::DRIVER.bits()); + + let device_features_bits = header.read_device_features(); + let device_features = VirtIOBlkFeature::from_bits_truncate(device_features_bits); + info!("Device features {:?}", device_features); + + // negotiate these flags only + let supported_features = VirtIOBlkFeature::empty(); + let driver_features = (device_features & supported_features).bits(); + header.write_driver_features(driver_features); + + // read configuration space + let config = unsafe { &mut *((from + VIRTIO_CONFIG_SPACE_OFFSET) as *mut VirtIOBlkConfig) }; + info!("Config: {:?}", config); + info!("Found a block device of size {}KB", config.capacity.read() / 2); + + // virtio 4.2.4 Legacy interface + // configure two virtqueues: ingress and egress + header.guest_page_size.write(PAGE_SIZE as u32); // one page + + let mut driver = VirtIOBlkDriver(Arc::new(Mutex::new(VirtIOBlk { + interrupt: node.prop_u32("interrupts").unwrap(), + interrupt_parent: node.prop_u32("interrupt-parent").unwrap(), + header: from as usize, + queue: VirtIOVirtqueue::new(header, 0, 16), + capacity: config.capacity.read() as usize, + }))); + + header.status.write(VirtIODeviceStatus::DRIVER_OK.bits()); + + DRIVERS.lock().push(Box::new(driver)); +} \ No newline at end of file diff --git a/kernel/src/drivers/bus/virtio_mmio.rs b/kernel/src/drivers/bus/virtio_mmio.rs index b308994..1608856 100644 --- a/kernel/src/drivers/bus/virtio_mmio.rs +++ b/kernel/src/drivers/bus/virtio_mmio.rs @@ -1,15 +1,24 @@ +use alloc::{vec, vec::Vec}; +use alloc::alloc::{GlobalAlloc, Layout}; use core::mem::size_of; +use core::slice; +use core::sync::atomic::{fence, Ordering}; use bitflags::*; use device_tree::Node; use device_tree::util::SliceRead; use log::*; +use rcore_memory::PAGE_SIZE; use rcore_memory::paging::PageTable; use volatile::{ReadOnly, Volatile, WriteOnly}; +use crate::arch::memory; +use crate::HEAP_ALLOCATOR; use crate::memory::active_table; +use super::super::block::virtio_blk; use super::super::gpu::virtio_gpu; +use super::super::input::virtio_input; use super::super::net::virtio_net; // virtio 4.2.4 Legacy interface @@ -53,6 +62,212 @@ pub struct VirtIOHeader { config_generation: ReadOnly } +#[repr(C)] +pub struct VirtIOVirtqueue { + header: usize, + queue_address: usize, + queue_num: usize, + queue: usize, + desc: usize, // *mut VirtIOVirtqueueDesc, + avail: usize, // *mut VirtIOVirtqueueAvailableRing, + used: usize, // *mut VirtIOVirtqueueUsedRing, + desc_state: Vec, + num_used: usize, + free_head: usize, + avail_idx: u16, + last_used_idx: u16, +} + +impl VirtIOVirtqueue { + // Initialize a virtqueue + pub fn new(header: &mut VirtIOHeader, queue: usize, queue_num: usize) -> VirtIOVirtqueue { + header.queue_sel.write(queue as u32); + assert_eq!(header.queue_pfn.read(), 0); // not in use + let queue_num_max = header.queue_num_max.read(); + assert!(queue_num_max >= queue_num as u32); // queue available + assert!(queue_num & (queue_num - 1) == 0); // power of two + let align = PAGE_SIZE; + let size = virtqueue_size(queue_num, align); + assert!(size % align == 0); + // alloc continuous pages + let address = unsafe { + HEAP_ALLOCATOR.alloc_zeroed(Layout::from_size_align(size, align).unwrap()) + } as usize; + + header.queue_num.write(queue_num as u32); + header.queue_align.write(align as u32); + header.queue_pfn.write((address as u32) >> 12); + + // link desc together + let desc = unsafe { slice::from_raw_parts_mut(address as *mut VirtIOVirtqueueDesc, queue_num) }; + for i in 0..(queue_num - 1) { + desc[i].next.write((i + 1) as u16); + } + + VirtIOVirtqueue { + header: header as *mut VirtIOHeader as usize, + queue_address: address, + queue_num, + queue, + desc: address, + avail: address + size_of::() * queue_num, + used: address + virtqueue_used_elem_offset(queue_num, align), + desc_state: vec![0; queue_num], + num_used: 0, + free_head: 0, + avail_idx: 0, + last_used_idx: 0, + } + } + + pub fn can_add(&self, input_len: usize, output_len: usize) -> bool { + return input_len + output_len + self.num_used <= self.queue_num; + } + + // Add buffers to the virtqueue + // Return true on success, false otherwise + // ref. linux virtio_ring.c virtqueue_add + pub fn add(&mut self, input: &[&[u8]], output: &[&[u8]], user_data: usize) -> bool { + assert!(input.len() + output.len() > 0); + if !self.can_add(input.len(), output.len()) { + return false; + } + + let desc = unsafe { slice::from_raw_parts_mut(self.desc as *mut VirtIOVirtqueueDesc, self.queue_num) }; + let head = self.free_head; + let mut prev = 0; + let mut cur = self.free_head; + for i in 0..output.len() { + desc[cur].flags.write(VirtIOVirtqueueFlag::NEXT.bits()); + desc[cur].addr.write(output[i].as_ptr() as u64); + desc[cur].len.write(output[i].len() as u32); + prev = cur; + cur = desc[cur].next.read() as usize; + } + for i in 0..input.len() { + desc[cur].flags.write((VirtIOVirtqueueFlag::NEXT | VirtIOVirtqueueFlag::WRITE).bits()); + desc[cur].addr.write(input[i].as_ptr() as u64); + desc[cur].len.write(input[i].len() as u32); + prev = cur; + cur = desc[cur].next.read() as usize; + } + desc[prev].flags.write(desc[prev].flags.read() & !(VirtIOVirtqueueFlag::NEXT.bits())); + + self.num_used += input.len() + output.len(); + self.free_head = cur; + + let avail = unsafe { &mut *(self.avail as *mut VirtIOVirtqueueAvailableRing) }; + + let avail_slot = self.avail_idx as usize & (self.queue_num - 1); + avail.ring[avail_slot].write(head as u16); + + // write barrier + fence(Ordering::SeqCst); + + self.avail_idx += 1; + avail.idx.write(self.avail_idx); + self.desc_state[head] = user_data; + return true; + } + + // Add buffers to the virtqueue and notify device about it + pub fn add_and_notify(&mut self, input: &[&[u8]], output: &[&[u8]], user_data: usize) -> bool { + let res = self.add(input, output, user_data); + if res { + self.notify(); + } + return res; + } + + pub fn can_get(&self) -> bool { + let used = unsafe { &mut *(self.used as *mut VirtIOVirtqueueUsedRing) }; + return self.last_used_idx != used.idx.read(); + } + + // Get device used buffers (input, output, length, user_data) + // ref. linux virtio_ring.c virtqueue_get_buf_ctx + pub fn get(&mut self) -> Option<(Vec<&'static [u8]>, Vec<&'static [u8]>, usize, usize)> { + let used = unsafe { &mut *(self.used as *mut VirtIOVirtqueueUsedRing) }; + if self.last_used_idx == used.idx.read() { + return None + } + // read barrier + fence(Ordering::SeqCst); + + let last_used_slot = self.last_used_idx as usize & (self.queue_num - 1); + let index = used.ring[last_used_slot].id.read() as usize; + let len = used.ring[last_used_slot].len.read(); + + let user_data = self.desc_state[last_used_slot]; + self.desc_state[last_used_slot] = 0; + + let mut cur = index; + let desc = unsafe { slice::from_raw_parts_mut(self.desc as *mut VirtIOVirtqueueDesc, self.queue_num) }; + let mut input = Vec::new(); + let mut output = Vec::new(); + loop { + let flags = VirtIOVirtqueueFlag::from_bits_truncate(desc[cur].flags.read()); + let buffer = unsafe { slice::from_raw_parts(desc[cur].addr.read() as *const u8, desc[cur].len.read() as usize) }; + if flags.contains(VirtIOVirtqueueFlag::WRITE) { + input.push(buffer); + } else { + output.push(buffer); + } + + if flags.contains(VirtIOVirtqueueFlag::NEXT) { + cur = desc[cur].next.read() as usize; + self.num_used -= 1; + } else { + desc[cur].next.write(self.free_head as u16); + self.num_used -= 1; + break + } + } + + self.free_head = index; + self.last_used_idx += 1; + + Some((input, output, len as usize, user_data)) + } + + // Get device used buffers until succeed + // See get() above + pub fn get_block(&mut self) -> (Vec<&'static [u8]>, Vec<&'static [u8]>, usize, usize) { + loop { + let res = self.get(); + if res.is_some() { + return res.unwrap(); + } + } + } + + // Notify device about new buffers + pub fn notify(&mut self) { + let header = unsafe { &mut *(self.header as *mut VirtIOHeader) }; + header.queue_notify.write(self.queue as u32); + } +} + +pub const VIRTIO_CONFIG_SPACE_OFFSET: u64 = 0x100; + +impl VirtIOHeader { + pub fn read_device_features(&mut self) -> u64 { + let mut device_features_bits: u64; + self.device_features_sel.write(0); // device features [0, 32) + device_features_bits = self.device_features.read().into(); + self.device_features_sel.write(1); // device features [32, 64) + device_features_bits = device_features_bits + ((self.device_features.read() as u64) << 32); + device_features_bits + } + + pub fn write_driver_features(&mut self, driver_features: u64) { + self.driver_features_sel.write(0); // driver features [0, 32) + self.driver_features.write((driver_features & 0xFFFFFFFF) as u32); + self.driver_features_sel.write(1); // driver features [32, 64) + self.driver_features.write(((driver_features & 0xFFFFFFFF00000000) >> 32) as u32); + } +} + bitflags! { pub struct VirtIODeviceStatus : u32 { const ACKNOWLEDGE = 1; @@ -87,7 +302,7 @@ pub struct VirtIOVirtqueueAvailableRing { pub flags: Volatile, pub idx: Volatile, pub ring: [Volatile; 32], // actual size: queue_size - used_event: Volatile + used_event: Volatile // unused } #[repr(C)] @@ -103,7 +318,7 @@ pub struct VirtIOVirtqueueUsedRing { pub flags: Volatile, pub idx: Volatile, pub ring: [VirtIOVirtqueueUsedElem; 32], // actual size: queue_size - avail_event: Volatile + avail_event: Volatile // unused } // virtio 2.4.2 Legacy Interfaces: A Note on Virtqueue Layout @@ -121,8 +336,9 @@ pub fn virtio_probe(node: &Node) { let from = reg.as_slice().read_be_u64(0).unwrap(); let size = reg.as_slice().read_be_u64(8).unwrap(); // assuming one page + assert_eq!(size as usize, PAGE_SIZE); active_table().map(from as usize, from as usize); - let mut header = unsafe { &mut *(from as *mut VirtIOHeader) }; + let header = unsafe { &mut *(from as *mut VirtIOHeader) }; let magic = header.magic.read(); let version = header.version.read(); let device_id = header.device_id.read(); @@ -135,8 +351,12 @@ pub fn virtio_probe(node: &Node) { header.status.write(VirtIODeviceStatus::ACKNOWLEDGE.bits()); if device_id == 1 { // net device virtio_net::virtio_net_init(node); + } else if device_id == 2 { // blk device + virtio_blk::virtio_blk_init(node); } else if device_id == 16 { // gpu device virtio_gpu::virtio_gpu_init(node); + } else if device_id == 18 { // input device + virtio_input::virtio_input_init(node); } else { println!("Unrecognized virtio device {}", device_id); } diff --git a/kernel/src/drivers/gpu/mod.rs b/kernel/src/drivers/gpu/mod.rs index 0d8a41d..f134983 100644 --- a/kernel/src/drivers/gpu/mod.rs +++ b/kernel/src/drivers/gpu/mod.rs @@ -1 +1,2 @@ -pub mod virtio_gpu; \ No newline at end of file +pub mod virtio_gpu; +mod test; \ No newline at end of file diff --git a/kernel/src/drivers/gpu/test.rs b/kernel/src/drivers/gpu/test.rs new file mode 100644 index 0000000..d79a38b --- /dev/null +++ b/kernel/src/drivers/gpu/test.rs @@ -0,0 +1,59 @@ +use core::slice; + +// from Wikipedia +fn hsv_to_rgb(h: u32, s: f32, v: f32) -> (f32, f32, f32) { + let hi = (h / 60) % 6; + let f = (h % 60) as f32 / 60.0; + let p = v * (1.0 - s); + let q = v * (1.0 - f * s); + let t = v * (1.0 - (1.0 - f) * s); + match hi { + 0 => (v, t, p), + 1 => (q, v, p), + 2 => (p, v, t), + 3 => (p, q, v), + 4 => (t, p, v), + 5 => (v, p, q), + _ => unreachable!() + } +} + +pub fn mandelbrot(width: u32, height: u32, frame_buffer: *mut u32) { + let size = width * height * 4; + let frame_buffer_data = unsafe { + slice::from_raw_parts_mut(frame_buffer as *mut u32, (size / 4) as usize) + }; + for x in 0..width { + for y in 0..height { + let index = y * width + x; + let scale = 5e-3; + let xx = (x as f32 - width as f32 / 2.0) * scale; + let yy = (y as f32 - height as f32 / 2.0) * scale; + let mut re = xx as f32; + let mut im = yy as f32; + let mut iter: u32 = 0; + loop { + iter = iter + 1; + let new_re = re * re - im * im + xx as f32; + let new_im = re * im * 2.0 + yy as f32; + if new_re * new_re + new_im * new_im > 1e3 { + break; + } + re = new_re; + im = new_im; + + if iter == 60 { + break; + } + } + iter = iter * 6; + let (r, g, b) = hsv_to_rgb(iter, 1.0, 0.5); + let rr = (r * 256.0) as u32; + let gg = (g * 256.0) as u32; + let bb = (b * 256.0) as u32; + let color = (bb << 16) | (gg << 8) | rr; + frame_buffer_data[index as usize] = color; + } + println!("working on x {}/{}", x, width); + } +} \ No newline at end of file diff --git a/kernel/src/drivers/gpu/virtio_gpu.rs b/kernel/src/drivers/gpu/virtio_gpu.rs index 3818f89..072244e 100644 --- a/kernel/src/drivers/gpu/virtio_gpu.rs +++ b/kernel/src/drivers/gpu/virtio_gpu.rs @@ -17,6 +17,7 @@ use crate::memory::active_table; use super::super::{DeviceType, Driver, DRIVERS}; use super::super::bus::virtio_mmio::*; +use super::test::mandelbrot; const VIRTIO_GPU_EVENT_DISPLAY : u32 = 1 << 0; @@ -24,13 +25,10 @@ struct VirtIOGpu { interrupt_parent: u32, interrupt: u32, header: usize, - // 0 for transmit, 1 for cursor - queue_num: u32, - queue_address: usize, - queue_page: [usize; 2], - last_used_idx: u16, + queue_buffer: [usize; 2], frame_buffer: usize, - rect: VirtIOGpuRect + rect: VirtIOGpuRect, + queues: [VirtIOVirtqueue; 2] } #[repr(C)] @@ -46,7 +44,7 @@ bitflags! { const VIRGL = 1 << 0; const EDID = 1 << 1; // device independent - const NOFIFY_ON_EMPTY = 1 << 24; // legacy + const NOTIFY_ON_EMPTY = 1 << 24; // legacy const ANY_LAYOUT = 1 << 27; // legacy const RING_INDIRECT_DESC = 1 << 28; const RING_EVENT_IDX = 1 << 29; @@ -180,7 +178,10 @@ struct VirtIOGpuResourceFlush { } const VIRTIO_QUEUE_TRANSMIT: usize = 0; -const VIRTIO_QUEUE_RECEIVE: usize = 1; +const VIRTIO_QUEUE_CURSOR: usize = 1; + +const VIRTIO_BUFFER_TRANSMIT: usize = 0; +const VIRTIO_BUFFER_RECEIVE: usize = 1; const VIRTIO_GPU_RESOURCE_ID: u32 = 0xbabe; @@ -194,13 +195,11 @@ impl Driver for VirtIOGpu { // ensure header page is mapped active_table().map_if_not_exists(self.header as usize, self.header as usize); - let mut header = unsafe { &mut *(self.header as *mut VirtIOHeader) }; + let header = unsafe { &mut *(self.header as *mut VirtIOHeader) }; let interrupt = header.interrupt_status.read(); if interrupt != 0 { header.interrupt_ack.write(interrupt); debug!("Got interrupt {:?}", interrupt); - let response = unsafe { &mut *(self.queue_page[VIRTIO_QUEUE_RECEIVE] as *mut VirtIOGpuCtrlHdr) }; - debug!("response in interrupt: {:?}", response); return true; } return false; @@ -211,51 +210,24 @@ impl Driver for VirtIOGpu { } } -fn setup_rings(driver: &mut VirtIOGpu) { - let mut ring = unsafe { - &mut *((driver.queue_address + size_of::() * driver.queue_num as usize) as *mut VirtIOVirtqueueAvailableRing) - }; - - // re-add two buffers to desc - // chaining read buffer and write buffer into one desc - for buffer in 0..2 { - let mut desc = unsafe { &mut *(driver.queue_address as *mut VirtIOVirtqueueDesc).add(buffer) }; - desc.addr.write(driver.queue_page[buffer] as u64); - desc.len.write(PAGE_SIZE as u32); - if buffer == VIRTIO_QUEUE_TRANSMIT { - // device readable - desc.flags.write(VirtIOVirtqueueFlag::NEXT.bits()); - desc.next.write(1); - } else { - // device writable - desc.flags.write(VirtIOVirtqueueFlag::WRITE.bits()); - } - ring.ring[buffer].write(0); - } -} - -fn notify_device(driver: &mut VirtIOGpu) { - let mut header = unsafe { &mut *(driver.header as *mut VirtIOHeader) }; - let mut ring = unsafe { - &mut *((driver.queue_address + size_of::() * driver.queue_num as usize) as *mut VirtIOVirtqueueAvailableRing) - }; - ring.idx.write(ring.idx.read() + 1); - header.queue_notify.write(0); +fn request(driver: &mut VirtIOGpu) { + let input = unsafe { slice::from_raw_parts(driver.queue_buffer[VIRTIO_BUFFER_RECEIVE] as *const u8, PAGE_SIZE) }; + let output = unsafe { slice::from_raw_parts(driver.queue_buffer[VIRTIO_BUFFER_TRANSMIT] as *const u8, PAGE_SIZE) }; + driver.queues[VIRTIO_QUEUE_TRANSMIT].add_and_notify(&[input], &[output], 0); } fn setup_framebuffer(driver: &mut VirtIOGpu) { // get display info - setup_rings(driver); - let mut request_get_display_info = unsafe { &mut *(driver.queue_page[VIRTIO_QUEUE_TRANSMIT] as *mut VirtIOGpuCtrlHdr) }; + let request_get_display_info = unsafe { &mut *(driver.queue_buffer[VIRTIO_BUFFER_TRANSMIT] as *mut VirtIOGpuCtrlHdr) }; *request_get_display_info = VirtIOGpuCtrlHdr::with_type(VIRTIO_GPU_CMD_GET_DISPLAY_INFO); - notify_device(driver); - let response_get_display_info = unsafe { &mut *(driver.queue_page[VIRTIO_QUEUE_RECEIVE] as *mut VirtIOGpuRespDisplayInfo) }; + request(driver); + driver.queues[VIRTIO_QUEUE_TRANSMIT].get_block(); + let response_get_display_info = unsafe { &mut *(driver.queue_buffer[VIRTIO_BUFFER_RECEIVE] as *mut VirtIOGpuRespDisplayInfo) }; info!("response: {:?}", response_get_display_info); driver.rect = response_get_display_info.rect; // create resource 2d - setup_rings(driver); - let mut request_resource_create_2d = unsafe { &mut *(driver.queue_page[VIRTIO_QUEUE_TRANSMIT] as *mut VirtIOGpuResourceCreate2D) }; + let request_resource_create_2d = unsafe { &mut *(driver.queue_buffer[VIRTIO_BUFFER_TRANSMIT] as *mut VirtIOGpuResourceCreate2D) }; *request_resource_create_2d = VirtIOGpuResourceCreate2D { header: VirtIOGpuCtrlHdr::with_type(VIRTIO_GPU_CMD_RESOURCE_CREATE_2D), resource_id: VIRTIO_GPU_RESOURCE_ID, @@ -263,8 +235,9 @@ fn setup_framebuffer(driver: &mut VirtIOGpu) { width: response_get_display_info.rect.width, height: response_get_display_info.rect.height }; - notify_device(driver); - let response_resource_create_2d = unsafe { &mut *(driver.queue_page[VIRTIO_QUEUE_RECEIVE] as *mut VirtIOGpuCtrlHdr) }; + request(driver); + driver.queues[VIRTIO_QUEUE_TRANSMIT].get_block(); + let response_resource_create_2d = unsafe { &mut *(driver.queue_buffer[VIRTIO_BUFFER_RECEIVE] as *mut VirtIOGpuCtrlHdr) }; info!("response: {:?}", response_resource_create_2d); // alloc continuous pages for the frame buffer @@ -274,8 +247,7 @@ fn setup_framebuffer(driver: &mut VirtIOGpu) { } as usize; mandelbrot(driver.rect.width, driver.rect.height, frame_buffer as *mut u32); driver.frame_buffer = frame_buffer; - setup_rings(driver); - let mut request_resource_attach_backing = unsafe { &mut *(driver.queue_page[VIRTIO_QUEUE_TRANSMIT] as *mut VirtIOGpuResourceAttachBacking) }; + let request_resource_attach_backing = unsafe { &mut *(driver.queue_buffer[VIRTIO_BUFFER_TRANSMIT] as *mut VirtIOGpuResourceAttachBacking) }; *request_resource_attach_backing = VirtIOGpuResourceAttachBacking { header: VirtIOGpuCtrlHdr::with_type(VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING), resource_id: VIRTIO_GPU_RESOURCE_ID, @@ -284,88 +256,30 @@ fn setup_framebuffer(driver: &mut VirtIOGpu) { length: size, padding: 0 }; - notify_device(driver); - let response_resource_attach_backing = unsafe { &mut *(driver.queue_page[VIRTIO_QUEUE_RECEIVE] as *mut VirtIOGpuCtrlHdr) }; - debug!("response: {:?}", response_resource_attach_backing); + request(driver); + driver.queues[VIRTIO_QUEUE_TRANSMIT].get_block(); + let response_resource_attach_backing = unsafe { &mut *(driver.queue_buffer[VIRTIO_BUFFER_RECEIVE] as *mut VirtIOGpuCtrlHdr) }; + info!("response: {:?}", response_resource_attach_backing); // map frame buffer to screen - setup_rings(driver); - let mut request_set_scanout = unsafe { &mut *(driver.queue_page[VIRTIO_QUEUE_TRANSMIT] as *mut VirtIOGpuSetScanout) }; + let request_set_scanout = unsafe { &mut *(driver.queue_buffer[VIRTIO_BUFFER_TRANSMIT] as *mut VirtIOGpuSetScanout) }; *request_set_scanout = VirtIOGpuSetScanout { header: VirtIOGpuCtrlHdr::with_type(VIRTIO_GPU_CMD_SET_SCANOUT), rect: response_get_display_info.rect, scanout_id: 0, resource_id: VIRTIO_GPU_RESOURCE_ID }; - notify_device(driver); - let response_set_scanout = unsafe { &mut *(driver.queue_page[VIRTIO_QUEUE_RECEIVE] as *mut VirtIOGpuCtrlHdr) }; + request(driver); + driver.queues[VIRTIO_QUEUE_TRANSMIT].get_block(); + let response_set_scanout = unsafe { &mut *(driver.queue_buffer[VIRTIO_BUFFER_RECEIVE] as *mut VirtIOGpuCtrlHdr) }; info!("response: {:?}", response_set_scanout); flush_frame_buffer_to_screen(driver); } -// from Wikipedia -fn hsv_to_rgb(h: u32, s: f32, v: f32) -> (f32, f32, f32) { - let hi = (h / 60) % 6; - let f = (h % 60) as f32 / 60.0; - let p = v * (1.0 - s); - let q = v * (1.0 - f * s); - let t = v * (1.0 - (1.0 - f) * s); - match hi { - 0 => (v, t, p), - 1 => (q, v, p), - 2 => (p, v, t), - 3 => (p, q, v), - 4 => (t, p, v), - 5 => (v, p, q), - _ => unreachable!() - } -} - -fn mandelbrot(width: u32, height: u32, frame_buffer: *mut u32) { - let size = width * height * 4; - let frame_buffer_data = unsafe { - slice::from_raw_parts_mut(frame_buffer as *mut u32, (size / 4) as usize) - }; - for x in 0..width { - for y in 0..height { - let index = y * width + x; - let scale = 5e-3; - let xx = (x as f32 - width as f32 / 2.0) * scale; - let yy = (y as f32 - height as f32 / 2.0) * scale; - let mut re = xx as f32; - let mut im = yy as f32; - let mut iter: u32 = 0; - loop { - iter = iter + 1; - let new_re = re * re - im * im + xx as f32; - let new_im = re * im * 2.0 + yy as f32; - if new_re * new_re + new_im * new_im > 1e3 { - break; - } - re = new_re; - im = new_im; - - if iter == 60 { - break; - } - } - iter = iter * 6; - let (r, g, b) = hsv_to_rgb(iter, 1.0, 0.5); - let rr = (r * 256.0) as u32; - let gg = (g * 256.0) as u32; - let bb = (b * 256.0) as u32; - let color = (bb << 16) | (gg << 8) | rr; - frame_buffer_data[index as usize] = color; - } - println!("working on x {}/{}", x, width); - } -} - fn flush_frame_buffer_to_screen(driver: &mut VirtIOGpu) { // copy data from guest to host - setup_rings(driver); - let mut request_transfer_to_host_2d = unsafe { &mut *(driver.queue_page[VIRTIO_QUEUE_TRANSMIT] as *mut VirtIOGpuTransferToHost2D) }; + let request_transfer_to_host_2d = unsafe { &mut *(driver.queue_buffer[VIRTIO_BUFFER_TRANSMIT] as *mut VirtIOGpuTransferToHost2D) }; *request_transfer_to_host_2d = VirtIOGpuTransferToHost2D { header: VirtIOGpuCtrlHdr::with_type(VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D), rect: driver.rect, @@ -373,49 +287,43 @@ fn flush_frame_buffer_to_screen(driver: &mut VirtIOGpu) { resource_id: VIRTIO_GPU_RESOURCE_ID, padding: 0 }; - notify_device(driver); - let response_transfer_to_host_2d = unsafe { &mut *(driver.queue_page[VIRTIO_QUEUE_RECEIVE] as *mut VirtIOGpuCtrlHdr) }; + request(driver); + driver.queues[VIRTIO_QUEUE_TRANSMIT].get_block(); + let response_transfer_to_host_2d = unsafe { &mut *(driver.queue_buffer[VIRTIO_BUFFER_RECEIVE] as *mut VirtIOGpuCtrlHdr) }; info!("response: {:?}", response_transfer_to_host_2d); // flush data to screen - setup_rings(driver); - let mut request_resource_flush = unsafe { &mut *(driver.queue_page[VIRTIO_QUEUE_TRANSMIT] as *mut VirtIOGpuResourceFlush) }; + let request_resource_flush = unsafe { &mut *(driver.queue_buffer[VIRTIO_BUFFER_TRANSMIT] as *mut VirtIOGpuResourceFlush) }; *request_resource_flush = VirtIOGpuResourceFlush { header: VirtIOGpuCtrlHdr::with_type(VIRTIO_GPU_CMD_RESOURCE_FLUSH), rect: driver.rect, resource_id: VIRTIO_GPU_RESOURCE_ID, padding: 0 }; - notify_device(driver); - let response_resource_flush = unsafe { &mut *(driver.queue_page[VIRTIO_QUEUE_RECEIVE] as *mut VirtIOGpuCtrlHdr) }; + request(driver); + driver.queues[VIRTIO_QUEUE_TRANSMIT].get_block(); + let response_resource_flush = unsafe { &mut *(driver.queue_buffer[VIRTIO_BUFFER_RECEIVE] as *mut VirtIOGpuCtrlHdr) }; info!("response: {:?}", response_resource_flush); } pub fn virtio_gpu_init(node: &Node) { let reg = node.prop_raw("reg").unwrap(); let from = reg.as_slice().read_be_u64(0).unwrap(); - let mut header = unsafe { &mut *(from as *mut VirtIOHeader) }; + let header = unsafe { &mut *(from as *mut VirtIOHeader) }; header.status.write(VirtIODeviceStatus::DRIVER.bits()); - let mut device_features_bits: u64; - header.device_features_sel.write(0); // device features [0, 32) - device_features_bits = header.device_features.read().into(); - header.device_features_sel.write(1); // device features [32, 64) - device_features_bits = device_features_bits + ((header.device_features.read() as u64) << 32); + let device_features_bits = header.read_device_features(); let device_features = VirtIOGpuFeature::from_bits_truncate(device_features_bits); info!("Device features {:?}", device_features); // negotiate these flags only let supported_features = VirtIOGpuFeature::empty(); let driver_features = (device_features & supported_features).bits(); - header.driver_features_sel.write(0); // driver features [0, 32) - header.driver_features.write((driver_features & 0xFFFFFFFF) as u32); - header.driver_features_sel.write(1); // driver features [32, 64) - header.driver_features.write(((driver_features & 0xFFFFFFFF00000000) >> 32) as u32); + header.write_driver_features(driver_features); // read configuration space - let mut config = unsafe { &mut *((from + 0x100) as *mut VirtIOGpuConfig) }; + let config = unsafe { &mut *((from + VIRTIO_CONFIG_SPACE_OFFSET) as *mut VirtIOGpuConfig) }; info!("Config: {:?}", config); // virtio 4.2.4 Legacy interface @@ -427,48 +335,22 @@ pub fn virtio_gpu_init(node: &Node) { interrupt: node.prop_u32("interrupts").unwrap(), interrupt_parent: node.prop_u32("interrupt-parent").unwrap(), header: from as usize, - queue_num, - queue_address: 0, - queue_page: [0, 0], - last_used_idx: 0, + queue_buffer: [0, 0], frame_buffer: 0, - rect: VirtIOGpuRect::default() + rect: VirtIOGpuRect::default(), + queues: [VirtIOVirtqueue::new(header, VIRTIO_QUEUE_TRANSMIT, queue_num), + VirtIOVirtqueue::new(header, VIRTIO_QUEUE_CURSOR, queue_num)] }; - // 0 for control, 1 for cursor, we use controlq only - for queue in 0..2 { - header.queue_sel.write(queue); - assert_eq!(header.queue_pfn.read(), 0); // not in use - // 0 for transmit, 1 for receive - let queue_num_max = header.queue_num_max.read(); - assert!(queue_num_max >= queue_num); // queue available - let size = virtqueue_size(queue_num as usize, PAGE_SIZE); - assert!(size % PAGE_SIZE == 0); - // alloc continuous pages - let address = unsafe { - HEAP_ALLOCATOR.alloc_zeroed(Layout::from_size_align(size, PAGE_SIZE).unwrap()) + for buffer in 0..2 { + // allocate a page for each buffer + let page = unsafe { + HEAP_ALLOCATOR.alloc_zeroed(Layout::from_size_align(PAGE_SIZE, PAGE_SIZE).unwrap()) } as usize; - - debug!("queue {} using page address {:#X} with size {}", queue, address as usize, size); - - header.queue_num.write(queue_num); - header.queue_align.write(PAGE_SIZE as u32); - header.queue_pfn.write((address as u32) >> 12); - - if queue == 0 { - driver.queue_address = address; - // 0 for transmit, 1 for receive - for buffer in 0..2 { - // allocate a page for each buffer - let page = unsafe { - HEAP_ALLOCATOR.alloc_zeroed(Layout::from_size_align(PAGE_SIZE, PAGE_SIZE).unwrap()) - } as usize; - driver.queue_page[buffer as usize] = page; - debug!("buffer {} using page address {:#X}", buffer, page as usize); - } - } - header.queue_notify.write(queue); + driver.queue_buffer[buffer as usize] = page; + debug!("buffer {} using page address {:#X}", buffer, page as usize); } + header.status.write(VirtIODeviceStatus::DRIVER_OK.bits()); setup_framebuffer(&mut driver); diff --git a/kernel/src/drivers/input/mod.rs b/kernel/src/drivers/input/mod.rs new file mode 100644 index 0000000..2b99e94 --- /dev/null +++ b/kernel/src/drivers/input/mod.rs @@ -0,0 +1 @@ +pub mod virtio_input; \ No newline at end of file diff --git a/kernel/src/drivers/input/virtio_input.rs b/kernel/src/drivers/input/virtio_input.rs new file mode 100644 index 0000000..0771b9b --- /dev/null +++ b/kernel/src/drivers/input/virtio_input.rs @@ -0,0 +1,210 @@ +use alloc::prelude::*; +use alloc::vec; +use core::fmt; +use core::mem::size_of; +use core::mem::transmute_copy; +use core::slice; + +use bitflags::*; +use device_tree::Node; +use device_tree::util::SliceRead; +use log::*; +use rcore_memory::PAGE_SIZE; +use rcore_memory::paging::PageTable; +use volatile::Volatile; + +use crate::arch::cpu; +use crate::memory::active_table; + +use super::super::{DeviceType, Driver, DRIVERS}; +use super::super::bus::virtio_mmio::*; + +struct VirtIOInput { + interrupt_parent: u32, + interrupt: u32, + header: usize, + // 0 for event, 1 for status + queues: [VirtIOVirtqueue; 2], + x: isize, + y: isize, +} + +const VIRTIO_INPUT_CFG_UNSET: u8 = 0x00; +const VIRTIO_INPUT_CFG_ID_NAME: u8 = 0x01; +const VIRTIO_INPUT_CFG_ID_SERIAL: u8 = 0x02; +const VIRTIO_INPUT_CFG_ID_DEVIDS: u8 = 0x03; +const VIRTIO_INPUT_CFG_PROP_BITS: u8 = 0x10; +const VIRTIO_INPUT_CFG_EV_BITS: u8 = 0x11; +const VIRTIO_INPUT_CFG_ABS_INFO: u8 = 0x12; + +#[repr(C)] +#[derive(Debug)] +struct VirtIOInputConfig { + select: Volatile, + subsel: Volatile, + size: u8, + reversed: [u8; 5], + data: [u8; 32] +} + +#[repr(C)] +#[derive(Debug)] +struct VirtIOInputAbsInfo { + min: u32, + max: u32, + fuzz: u32, + flat: u32, + res: u32 +} + +#[repr(C)] +#[derive(Debug)] +struct VirtIOInputDevIDs { + bustype: u16, + vendor: u16, + product: u16, + version: u16 +} + +#[repr(C)] +#[derive(Clone, Default)] +struct VirtIOInputEvent { + event_type: u16, + code: u16, + value: u32 +} + +impl fmt::Display for VirtIOInputEvent { + // linux event codes + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self.event_type { + 0 => { + match self.code { + 0 => write!(f, "SYN_REPORT"), + _ => write!(f, "Unknown SYN code {}", self.code) + } + } + 2 => { + match self.code { + 0 => { + write!(f, "REL_X {}", self.value) + } + 1 => { + write!(f, "REL_Y {}", self.value) + } + _ => write!(f, "Unknown REL code {}", self.code) + } + } + _ => write!(f, "Unknown event type {}", self.event_type) + } + } +} + +bitflags! { + struct VirtIOInputFeature : u64 { + // device independent + const NOTIFY_ON_EMPTY = 1 << 24; // legacy + const ANY_LAYOUT = 1 << 27; // legacy + const RING_INDIRECT_DESC = 1 << 28; + const RING_EVENT_IDX = 1 << 29; + const UNUSED = 1 << 30; // legacy + const VERSION_1 = 1 << 32; // detect legacy + const ACCESS_PLATFORM = 1 << 33; // since virtio v1.1 + const RING_PACKED = 1 << 34; // since virtio v1.1 + const IN_ORDER = 1 << 35; // since virtio v1.1 + const ORDER_PLATFORM = 1 << 36; // since virtio v1.1 + const SR_IOV = 1 << 37; // since virtio v1.1 + const NOTIFICATION_DATA = 1 << 38; // since virtio v1.1 + } +} + +const VIRTIO_QUEUE_EVENT: usize = 0; +const VIRTIO_QUEUE_STATUS: usize = 1; + +impl Driver for VirtIOInput { + fn try_handle_interrupt(&mut self) -> bool { + // for simplicity + if cpu::id() > 0 { + return false + } + + // ensure header page is mapped + active_table().map_if_not_exists(self.header as usize, self.header as usize); + let header = unsafe { &mut *(self.header as *mut VirtIOHeader) }; + let interrupt = header.interrupt_status.read(); + if interrupt != 0 { + header.interrupt_ack.write(interrupt); + debug!("Got interrupt {:?}", interrupt); + loop { + if let Some((input, output, _, _)) = self.queues[VIRTIO_QUEUE_EVENT].get() { + let event: VirtIOInputEvent = unsafe { transmute_copy(&input[0][0]) }; + if event.event_type == 2 && event.code == 0 { + // X + self.x += event.value as isize; + } else if event.event_type == 2 && event.code == 1 { + // X + self.y += event.value as isize; + } + trace!("got {}", event); + self.queues[VIRTIO_QUEUE_EVENT].add(&input, &output, 0); + } else { + break; + } + } + println!("mouse is at x {} y {}", self.x, self.y); + return true; + } + return false; + } + + fn device_type(&self) -> DeviceType { + DeviceType::Input + } +} + +pub fn virtio_input_init(node: &Node) { + let reg = node.prop_raw("reg").unwrap(); + let from = reg.as_slice().read_be_u64(0).unwrap(); + let header = unsafe { &mut *(from as *mut VirtIOHeader) }; + + header.status.write(VirtIODeviceStatus::DRIVER.bits()); + + let device_features_bits = header.read_device_features(); + let device_features = VirtIOInputFeature::from_bits_truncate(device_features_bits); + println!("Device features {:?}", device_features); + + // negotiate these flags only + let supported_features = VirtIOInputFeature::empty(); + let driver_features = (device_features & supported_features).bits(); + header.write_driver_features(driver_features); + + // read configuration space + let config = unsafe { &mut *((from + VIRTIO_CONFIG_SPACE_OFFSET) as *mut VirtIOInputConfig) }; + info!("Config: {:?}", config); + + // virtio 4.2.4 Legacy interface + // configure two virtqueues: ingress and egress + header.guest_page_size.write(PAGE_SIZE as u32); // one page + + let queue_num = 32; + let mut driver = VirtIOInput { + interrupt: node.prop_u32("interrupts").unwrap(), + interrupt_parent: node.prop_u32("interrupt-parent").unwrap(), + header: from as usize, + queues: [VirtIOVirtqueue::new(header, VIRTIO_QUEUE_EVENT, queue_num), + VirtIOVirtqueue::new(header, VIRTIO_QUEUE_STATUS, queue_num)], + x: 0, + y: 0 + }; + + let buffer = vec![VirtIOInputEvent::default(); queue_num]; + let input_buffers: &mut [VirtIOInputEvent] = Box::leak(buffer.into_boxed_slice()); + for i in 0..queue_num { + let buffer = unsafe { slice::from_raw_parts((&input_buffers[i]) as *const VirtIOInputEvent as *const u8, size_of::()) }; + driver.queues[VIRTIO_QUEUE_EVENT].add(&[buffer], &[], 0); + } + + header.status.write(VirtIODeviceStatus::DRIVER_OK.bits()); + + DRIVERS.lock().push(Box::new(driver)); +} \ No newline at end of file diff --git a/kernel/src/drivers/mod.rs b/kernel/src/drivers/mod.rs index a56271a..d5a3743 100644 --- a/kernel/src/drivers/mod.rs +++ b/kernel/src/drivers/mod.rs @@ -9,14 +9,18 @@ use crate::sync::SpinNoIrqLock; mod device_tree; pub mod bus; pub mod net; +pub mod block; mod gpu; +mod input; pub enum DeviceType { Net, - Gpu + Gpu, + Input, + Block } -pub trait Driver : Send { +pub trait Driver : Send + AsAny { // if interrupt belongs to this driver, handle it and return true // return false otherwise fn try_handle_interrupt(&mut self) -> bool; @@ -25,7 +29,7 @@ pub trait Driver : Send { fn device_type(&self) -> DeviceType; } -pub trait NetDriver: Driver + AsAny { +pub trait NetDriver: Driver { // get mac address for this device fn get_mac(&self) -> EthernetAddress; diff --git a/kernel/src/drivers/net/virtio_net.rs b/kernel/src/drivers/net/virtio_net.rs index d1c6049..eef326d 100644 --- a/kernel/src/drivers/net/virtio_net.rs +++ b/kernel/src/drivers/net/virtio_net.rs @@ -31,11 +31,8 @@ pub struct VirtIONet { interrupt: u32, header: usize, mac: EthernetAddress, - queue_num: u32, // 0 for receive, 1 for transmit - queue_address: [usize; 2], - queue_page: [usize; 2], - last_used_idx: [u16; 2], + queues: [VirtIOVirtqueue; 2], } #[derive(Clone)] @@ -51,33 +48,17 @@ impl Driver for VirtIONetDriver { return false } - let mut driver = self.0.lock(); + let driver = self.0.lock(); // ensure header page is mapped active_table().map_if_not_exists(driver.header as usize, driver.header as usize); - let mut header = unsafe { &mut *(driver.header as *mut VirtIOHeader) }; + let header = unsafe { &mut *(driver.header as *mut VirtIOHeader) }; let interrupt = header.interrupt_status.read(); if interrupt != 0 { header.interrupt_ack.write(interrupt); let interrupt_status = VirtIONetworkInterruptStatus::from_bits_truncate(interrupt); debug!("Got interrupt {:?}", interrupt_status); - if interrupt_status.contains(VirtIONetworkInterruptStatus::USED_RING_UPDATE) { - // need to change when queue_num is larger than one - let queue = VIRTIO_QUEUE_TRANSMIT; - let used_ring_offset = virtqueue_used_elem_offset(driver.queue_num as usize, PAGE_SIZE); - let mut used_ring = unsafe { - &mut *((driver.queue_address[queue] + used_ring_offset) as *mut VirtIOVirtqueueUsedRing) - }; - if driver.last_used_idx[queue] < used_ring.idx.read() { - assert_eq!(driver.last_used_idx[queue], used_ring.idx.read() - 1); - info!("Processing queue {} from {} to {}", queue, driver.last_used_idx[queue], used_ring.idx.read()); - driver.last_used_idx[queue] = used_ring.idx.read(); - } - } else if interrupt_status.contains(VirtIONetworkInterruptStatus::CONFIGURATION_CHANGE) { - // TODO: update mac and status - unimplemented!("virtio-net configuration change not implemented"); - } return true; } else { @@ -92,22 +73,12 @@ impl Driver for VirtIONetDriver { impl VirtIONet { fn transmit_available(&self) -> bool { - let used_ring_offset = virtqueue_used_elem_offset(self.queue_num as usize, PAGE_SIZE); - let mut used_ring = unsafe { - &mut *((self.queue_address[VIRTIO_QUEUE_TRANSMIT] + used_ring_offset) as *mut VirtIOVirtqueueUsedRing) - }; - let result = self.last_used_idx[VIRTIO_QUEUE_TRANSMIT] == used_ring.idx.read(); - result + self.queues[VIRTIO_QUEUE_TRANSMIT].can_add(1, 0) } fn receive_available(&self) -> bool { - let used_ring_offset = virtqueue_used_elem_offset(self.queue_num as usize, PAGE_SIZE); - let mut used_ring = unsafe { - &mut *((self.queue_address[VIRTIO_QUEUE_RECEIVE] + used_ring_offset) as *mut VirtIOVirtqueueUsedRing) - }; - let result = self.last_used_idx[VIRTIO_QUEUE_RECEIVE] < used_ring.idx.read(); - result + self.queues[VIRTIO_QUEUE_RECEIVE].can_get() } } @@ -132,7 +103,7 @@ impl<'a> phy::Device<'a> for VirtIONetDriver { fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> { let driver = self.0.lock(); if driver.transmit_available() && driver.receive_available() { - // ugly borrow rules bypass + // potential racing Some((VirtIONetRxToken(self.clone()), VirtIONetTxToken(self.clone()))) } else { @@ -158,36 +129,22 @@ impl<'a> phy::Device<'a> for VirtIONetDriver { } impl phy::RxToken for VirtIONetRxToken { - fn consume(self, timestamp: Instant, f: F) -> Result + fn consume(self, _timestamp: Instant, f: F) -> Result where F: FnOnce(&[u8]) -> Result { - let buffer = { + let (input, output, _, user_data) = { let mut driver = (self.0).0.lock(); // ensure header page is mapped active_table().map_if_not_exists(driver.header as usize, driver.header as usize); - let mut header = unsafe { &mut *(driver.header as *mut VirtIOHeader) }; - let used_ring_offset = virtqueue_used_elem_offset(driver.queue_num as usize, PAGE_SIZE); - let mut used_ring = unsafe { - &mut *((driver.queue_address[VIRTIO_QUEUE_RECEIVE] + used_ring_offset) as *mut VirtIOVirtqueueUsedRing) - }; - assert!(driver.last_used_idx[VIRTIO_QUEUE_RECEIVE] == used_ring.idx.read() - 1); - driver.last_used_idx[VIRTIO_QUEUE_RECEIVE] = used_ring.idx.read(); - let mut payload = unsafe { slice::from_raw_parts_mut((driver.queue_page[VIRTIO_QUEUE_RECEIVE] + size_of::()) as *mut u8, PAGE_SIZE - 10)}; - let buffer = payload.to_vec(); - for i in 0..(PAGE_SIZE - size_of::()) { - payload[i] = 0; - } - - let mut ring = unsafe { - &mut *((driver.queue_address[VIRTIO_QUEUE_RECEIVE] + size_of::() * driver.queue_num as usize) as *mut VirtIOVirtqueueAvailableRing) - }; - ring.idx.write(ring.idx.read() + 1); - header.queue_notify.write(VIRTIO_QUEUE_RECEIVE as u32); - buffer + driver.queues[VIRTIO_QUEUE_RECEIVE].get().unwrap() }; - f(&buffer) + let result = f(&input[0][size_of::()..]); + + let mut driver = (self.0).0.lock(); + driver.queues[VIRTIO_QUEUE_RECEIVE].add_and_notify(&input, &output, user_data); + result } } @@ -195,32 +152,28 @@ impl phy::TxToken for VirtIONetTxToken { fn consume(self, _timestamp: Instant, len: usize, f: F) -> Result where F: FnOnce(&mut [u8]) -> Result, { - let mut driver = (self.0).0.lock(); - - // ensure header page is mapped - active_table().map_if_not_exists(driver.header as usize, driver.header as usize); + let output = { + let mut driver = (self.0).0.lock(); - let mut header = unsafe { &mut *(driver.header as *mut VirtIOHeader) }; - let payload_target = unsafe { slice::from_raw_parts_mut((driver.queue_page[VIRTIO_QUEUE_TRANSMIT] + size_of::()) as *mut u8, len)}; - let result = f(payload_target); - let mut net_header = unsafe { &mut *(driver.queue_page[VIRTIO_QUEUE_TRANSMIT] as *mut VirtIONetHeader) }; + // ensure header page is mapped + active_table().map_if_not_exists(driver.header as usize, driver.header as usize); - let mut header = unsafe { &mut *(driver.header as *mut VirtIOHeader) }; - let mut ring = unsafe { - &mut *((driver.queue_address[VIRTIO_QUEUE_TRANSMIT] + size_of::() * driver.queue_num as usize) as *mut VirtIOVirtqueueAvailableRing) + if let Some((_, output, _, _)) = driver.queues[VIRTIO_QUEUE_TRANSMIT].get() { + unsafe { slice::from_raw_parts_mut(output[0].as_ptr() as *mut u8, output[0].len())} + } else { + // allocate a page for buffer + let page = unsafe { + HEAP_ALLOCATOR.alloc_zeroed(Layout::from_size_align(PAGE_SIZE, PAGE_SIZE).unwrap()) + } as usize; + unsafe { slice::from_raw_parts_mut(page as *mut u8, PAGE_SIZE) } + } }; + let output_buffer = &mut output[size_of::()..(size_of::() +len)]; + let result = f(output_buffer); + println!("output {:?}", output_buffer); - // re-add buffer to desc - let mut desc = unsafe { &mut *(driver.queue_address[VIRTIO_QUEUE_TRANSMIT] as *mut VirtIOVirtqueueDesc) }; - desc.addr.write(driver.queue_page[VIRTIO_QUEUE_TRANSMIT] as u64); - desc.len.write((len + size_of::()) as u32); - - // memory barrier - fence(Ordering::SeqCst); - - // add desc to available ring - ring.idx.write(ring.idx.read() + 1); - header.queue_notify.write(VIRTIO_QUEUE_TRANSMIT as u32); + let mut driver = (self.0).0.lock(); + assert!(driver.queues[VIRTIO_QUEUE_TRANSMIT].add_and_notify(&[], &[output], 0)); result } } @@ -296,108 +249,49 @@ struct VirtIONetHeader { pub fn virtio_net_init(node: &Node) { let reg = node.prop_raw("reg").unwrap(); let from = reg.as_slice().read_be_u64(0).unwrap(); - let mut header = unsafe { &mut *(from as *mut VirtIOHeader) }; + let header = unsafe { &mut *(from as *mut VirtIOHeader) }; header.status.write(VirtIODeviceStatus::DRIVER.bits()); - let mut device_features_bits: u64; - header.device_features_sel.write(0); // device features [0, 32) - device_features_bits = header.device_features.read().into(); - header.device_features_sel.write(1); // device features [32, 64) - device_features_bits = device_features_bits + ((header.device_features.read() as u64) << 32); + let device_features_bits = header.read_device_features(); let device_features = VirtIONetFeature::from_bits_truncate(device_features_bits); debug!("Device features {:?}", device_features); // negotiate these flags only let supported_features = VirtIONetFeature::MAC | VirtIONetFeature::STATUS; let driver_features = (device_features & supported_features).bits(); - header.driver_features_sel.write(0); // driver features [0, 32) - header.driver_features.write((driver_features & 0xFFFFFFFF) as u32); - header.driver_features_sel.write(1); // driver features [32, 64) - header.driver_features.write(((driver_features & 0xFFFFFFFF00000000) >> 32) as u32); + header.write_driver_features(driver_features); // read configuration space - let mut mac: [u8; 6]; - let mut status: VirtIONetworkStatus; - let mut config = unsafe { &mut *((from + 0x100) as *mut VirtIONetworkConfig) }; - mac = config.mac; - status = VirtIONetworkStatus::from_bits_truncate(config.status.read()); + let config = unsafe { &mut *((from + VIRTIO_CONFIG_SPACE_OFFSET) as *mut VirtIONetworkConfig) }; + let mac = config.mac; + let status = VirtIONetworkStatus::from_bits_truncate(config.status.read()); debug!("Got MAC address {:?} and status {:?}", mac, status); // virtio 4.2.4 Legacy interface // configure two virtqueues: ingress and egress header.guest_page_size.write(PAGE_SIZE as u32); // one page - let queue_num = 1; // for simplicity + let queue_num = 2; // for simplicity let mut driver = VirtIONet { interrupt: node.prop_u32("interrupts").unwrap(), interrupt_parent: node.prop_u32("interrupt-parent").unwrap(), header: from as usize, mac: EthernetAddress(mac), - queue_num: queue_num, - queue_address: [0, 0], - queue_page: [0, 0], - last_used_idx: [0, 0], + queues: [VirtIOVirtqueue::new(header, VIRTIO_QUEUE_RECEIVE, queue_num), + VirtIOVirtqueue::new(header, VIRTIO_QUEUE_TRANSMIT, queue_num)], }; - // 0 for receive, 1 for transmit - for queue in 0..2 { - header.queue_sel.write(queue as u32); - assert_eq!(header.queue_pfn.read(), 0); // not in use - let queue_num_max = header.queue_num_max.read(); - assert!(queue_num_max >= queue_num); // queue available - let size = virtqueue_size(queue_num as usize, PAGE_SIZE); - assert!(size % PAGE_SIZE == 0); - // alloc continuous pages - let address = unsafe { - HEAP_ALLOCATOR.alloc_zeroed(Layout::from_size_align(size, PAGE_SIZE).unwrap()) - } as usize; - driver.queue_address[queue] = address; - debug!("queue {} using page address {:#X} with size {}", queue, address as usize, size); - - header.queue_num.write(queue_num); - header.queue_align.write(PAGE_SIZE as u32); - header.queue_pfn.write((address as u32) >> 12); - - // allocate a page for buffer - let page = unsafe { - HEAP_ALLOCATOR.alloc_zeroed(Layout::from_size_align(PAGE_SIZE, PAGE_SIZE).unwrap()) - } as usize; - driver.queue_page[queue] = page; - - // fill first desc - let mut desc = unsafe { &mut *(address as *mut VirtIOVirtqueueDesc) }; - desc.addr.write(page as u64); - desc.len.write(PAGE_SIZE as u32); - if queue == VIRTIO_QUEUE_RECEIVE { - // device writable - desc.flags.write(VirtIOVirtqueueFlag::WRITE.bits()); - } else if queue == VIRTIO_QUEUE_TRANSMIT { - // driver readable - desc.flags.write(0); - } - // memory barrier - fence(Ordering::SeqCst); - - - if queue == VIRTIO_QUEUE_RECEIVE { - // add the desc to the ring - let mut ring = unsafe { - &mut *((address + size_of::() * queue_num as usize) as *mut VirtIOVirtqueueAvailableRing) - }; - ring.ring[0].write(0); - // wait for first packet - ring.idx.write(ring.idx.read() + 1); - } - - // notify device about the new buffer - header.queue_notify.write(queue as u32); - debug!("queue {} using page address {:#X}", queue, page); - } + // allocate a page for buffer + let page = unsafe { + HEAP_ALLOCATOR.alloc_zeroed(Layout::from_size_align(PAGE_SIZE, PAGE_SIZE).unwrap()) + } as usize; + let input = unsafe { slice::from_raw_parts(page as *const u8, PAGE_SIZE) }; + driver.queues[VIRTIO_QUEUE_RECEIVE].add_and_notify(&[input], &[], 0); header.status.write(VirtIODeviceStatus::DRIVER_OK.bits()); - let mut net_driver = VirtIONetDriver(Arc::new(Mutex::new(driver))); + let net_driver = VirtIONetDriver(Arc::new(Mutex::new(driver))); DRIVERS.lock().push(Box::new(net_driver.clone())); NET_DRIVERS.lock().push(Box::new(net_driver)); diff --git a/kernel/src/fs.rs b/kernel/src/fs.rs index 6de9049..ffb87e4 100644 --- a/kernel/src/fs.rs +++ b/kernel/src/fs.rs @@ -1,25 +1,40 @@ use simple_filesystem::*; use alloc::{boxed::Box, sync::Arc, string::String, collections::VecDeque, vec::Vec}; use core::any::Any; +use core::ops::Deref; use lazy_static::lazy_static; #[cfg(target_arch = "x86_64")] use crate::arch::driver::ide; use crate::sync::Condvar; use crate::sync::SpinNoIrqLock as Mutex; +use crate::drivers::{self, AsAny}; +use crate::drivers::block::virtio_blk::VirtIOBlkDriver; lazy_static! { pub static ref ROOT_INODE: Arc = { - #[cfg(any(target_arch = "riscv32", target_arch = "riscv64", target_arch = "aarch64"))] + #[cfg(not(feature = "link_user"))] + let device = { + #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] + { + Box::new(drivers::DRIVERS.lock().iter() + .map(|device| device.deref().as_any().downcast_ref::()) + .find(|maybe_blk| maybe_blk.is_some()) + .expect("VirtIOBlk not found") + .unwrap().clone()) + } + #[cfg(target_arch = "x86_64")] + { + Box::new(ide::IDE::new(1)) + } + }; + #[cfg(feature = "link_user")] let device = { extern { fn _user_img_start(); fn _user_img_end(); } - // Hard link user program Box::new(unsafe { MemBuf::new(_user_img_start, _user_img_end) }) }; - #[cfg(target_arch = "x86_64")] - let device = Box::new(ide::IDE::new(1)); let sfs = SimpleFileSystem::open(device).expect("failed to open SFS"); sfs.root_inode() diff --git a/kernel/src/net/test.rs b/kernel/src/net/test.rs index dbc5530..b3dee09 100644 --- a/kernel/src/net/test.rs +++ b/kernel/src/net/test.rs @@ -17,7 +17,7 @@ pub extern fn server(_arg: usize) -> ! { } } - let mut driver = { + let driver = { let ref_driver = &mut *NET_DRIVERS.lock()[0]; ref_driver.as_any().downcast_ref::().unwrap().clone() }; @@ -60,7 +60,7 @@ pub extern fn server(_arg: usize) -> ! { } let client = match socket.recv() { - Ok((data, endpoint)) => { + Ok((_, endpoint)) => { Some(endpoint) } Err(_) => None