diff --git a/Kernel/Modules/virtio/devices/mod.rs b/Kernel/Modules/virtio/devices/mod.rs index 7b81754719c271370fd8b1316722ba3b158ab066..6448f9585714c89b5537695f6dde785488aaa6c6 100644 --- a/Kernel/Modules/virtio/devices/mod.rs +++ b/Kernel/Modules/virtio/devices/mod.rs @@ -10,19 +10,19 @@ mod block; mod video; //mod network; -pub fn new_boxed<T: Interface+Send+Sync+'static>(dev: u32, io: device_manager::IOBinding, irq: u32) -> Box<device_manager::DriverInstance> +pub fn new_boxed<T: Interface+Send+Sync+'static>(dev_id: u32, int: T) -> Box<device_manager::DriverInstance> { - match dev + match dev_id { // 0: Reserved/invalid 0 => Box::new( NullDevice ), - //1 => Box::new( network::NetDevice::new(T::new(io, irq)) ), + //1 => Box::new( network::NetDevice::new(int) ), 1 => { log_notice!("TODO: Support VirtIO network devices (type = 1)"); Box::new(NullDevice) } - 2 => Box::new( block::BlockDevice::new(T::new(io, irq)) ), - 16 => Box::new( video::VideoDevice::new(T::new(io, irq)) ), + 2 => Box::new( block::BlockDevice::new(int) ), // 2 = Block device + 16 => Box::new( video::VideoDevice::new(int) ), // 16 = Graphics Adapter dev @ _ => { log_error!("VirtIO device has unknown device ID {:#x}", dev); Box::new(NullDevice) diff --git a/Kernel/Modules/virtio/devices/video.rs b/Kernel/Modules/virtio/devices/video.rs index 47be6b8122b3d6354bd8c8f6645c474d0c5d9199..0ad3579a8f0b2a35c764c731abf95b35bad23c62 100644 --- a/Kernel/Modules/virtio/devices/video.rs +++ b/Kernel/Modules/virtio/devices/video.rs @@ -5,7 +5,7 @@ use kernel::metadevs::video; use interface::Interface; use queue::{Queue,Buffer}; use kernel::lib::mem::aref::{Aref,ArefBorrow}; -use kernel::async::Mutex; +use kernel::sync::Mutex; pub struct VideoDevice<I> where @@ -27,7 +27,7 @@ where controlq: Queue, cursorq: Queue, - scanouts: Mutex<Vec<Option<Framebuffer<I>>>>, + scanouts: Mutex<Vec<Option<video::FramebufferRegistration>>>, } struct Framebuffer<I> @@ -35,8 +35,8 @@ where I: Interface + Send + Sync { dev: ArefBorrow<DeviceCore<I>>, - /// Handle to video metadev registration - _video_handle: video::FramebufferRegistration, + scanout_idx: usize, + dims: (u32, u32,), } impl<I> VideoDevice<I> @@ -56,7 +56,14 @@ where }); let di = core.get_display_info(); - log_debug!("di = {:?}", di); + for (i,screen) in Iterator::enumerate( di[..num_scanouts].iter() ) + { + if screen.enabled != 0 + { + log_debug!("Scanout #{} enabled: {:?} flags={:#x}", i, screen.r, screen.flags); + core.scanouts.lock()[i] = Some(video::add_output( Box::new(Framebuffer::new(core.borrow(), i, screen)) )); + } + } VideoDevice { _core: core, @@ -80,19 +87,38 @@ where }; let mut ret_hdr: hw::CtrlHeader = ::kernel::lib::PodHelpers::zeroed(); let mut ret_info: [hw::DisplayOne; 16] = ::kernel::lib::PodHelpers::zeroed(); - let h = self.controlq.send_buffers(&self.interface, &mut [ - Buffer::Read(::kernel::lib::as_byte_slice(&hdr)), - Buffer::Write(::kernel::lib::as_byte_slice_mut(&mut ret_hdr)), - Buffer::Write(::kernel::lib::as_byte_slice_mut(&mut ret_info)), - ]); - match h.wait_for_completion() + let rv = { + let h = self.controlq.send_buffers(&self.interface, &mut [ + Buffer::Read(::kernel::lib::as_byte_slice(&hdr)), + Buffer::Write(::kernel::lib::as_byte_slice_mut(&mut ret_hdr)), + Buffer::Write(::kernel::lib::as_byte_slice_mut(&mut ret_info)), + ]); + h.wait_for_completion() + }; + match rv { - Ok(bytes) => todo!("{} bytes from gpu request", bytes), - Err( () ) => panic!("TODO"), + Ok(bytes) => { + assert_eq!(bytes, ::core::mem::size_of_val(&ret_hdr) + ::core::mem::size_of_val(&ret_info), "Mismatched respose size"); + ret_info + }, + Err( () ) => panic!("TODO: Handle error waiting for VIRTIO_GPU_CMD_GET_DISPLAY_INFO response"), } } } +impl<I> Framebuffer<I> +where + I: 'static + Interface + Send + Sync +{ + fn new(dev: ArefBorrow<DeviceCore<I>>, scanout_idx: usize, info: &hw::DisplayOne) -> Self + { + Framebuffer { + dev: dev, + scanout_idx: scanout_idx, + dims: (info.r.width, info.r.height,), + } + } +} impl<I> video::Framebuffer for Framebuffer<I> where I: 'static + Interface + Send + Sync @@ -105,8 +131,10 @@ where } fn get_size(&self) -> video::Dims { - // TODO - todo!(""); + video::Dims { + w: self.dims.0, + h: self.dims.1, + } } fn set_size(&mut self, _newsize: video::Dims) -> bool { // TODO @@ -114,15 +142,19 @@ where } fn blit_inner(&mut self, dst: video::Rect, src: video::Rect) { + todo!("blit_inner"); } fn blit_ext(&mut self, dst: video::Rect, src: video::Rect, srf: &video::Framebuffer) -> bool { false } fn blit_buf(&mut self, dst: video::Rect, buf: &[u32]) { + todo!("blit_buf"); } fn fill(&mut self, dst: video::Rect, colour: u32) { + todo!("fill"); } fn move_cursor(&mut self, _p: Option<video::Pos>) { + todo!("move_cursor"); } } @@ -162,6 +194,7 @@ mod hw pub const VIRTIO_GPU_FLAG_FENCE: u32 = 1 << 0; #[repr(C)] + #[derive(Debug)] pub struct CtrlHeader { pub type_: u32, @@ -172,7 +205,6 @@ mod hw } #[repr(C)] - #[derive(Debug)] pub struct Rect { pub x: u32, @@ -180,6 +212,11 @@ mod hw pub width: u32, pub height: u32, } + impl ::core::fmt::Debug for Rect { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, "Rect {{ {},{} {}x{} }}", self.x, self.y, self.width, self.height) + } + } #[repr(C)] #[derive(Debug)] pub struct DisplayOne diff --git a/Kernel/Modules/virtio/drivers.rs b/Kernel/Modules/virtio/drivers.rs index 66d2d59dca047a0ead89e3e742370e81449e80ec..7ac858ae3e8553c774bafe3a51d02eb127ed62b0 100644 --- a/Kernel/Modules/virtio/drivers.rs +++ b/Kernel/Modules/virtio/drivers.rs @@ -49,7 +49,7 @@ impl device_manager::Driver for FdtMmioDriver return Box::new( NullDevice ); } - ::devices::new_boxed::<::interface::Mmio>(dev, io, bus_dev.get_irq(0)) + ::devices::new_boxed(dev, ::interface::Mmio::new(io, bus_dev.get_irq(0))) } } @@ -79,7 +79,6 @@ impl device_manager::Driver for Pci // TODO: The IO space may not be in BAR0? Instead referenced in PCI capabilities // - The PCI capabilities list includes entries for each region the driver uses, which can sub-slice a BAR // - Need to be able to read the capabilities list, AND get a sub-slice of a BAR - let io = bus_dev.bind_io(0); let dev = match bus_dev.get_attr("device").unwrap_u32() { 0x1000 => 1, // network card @@ -95,7 +94,129 @@ impl device_manager::Driver for Pci v @ _ => panic!("BUGCHECK: Binding with unexpected PCI device id {:#x}", v), }; - ::devices::new_boxed::<::interface::Mmio>(dev, io, irq) + let mut common_bar = None; + let mut device_cfg_bar = None; + let mut notify_bar = None; + for cap in pci_helpers::CapabilityIter::new(&*bus_dev) + { + match cap.id + { + 9 => { + let bar = cap.read_32(1) as usize; + let ofs = cap.read_32(2) as usize; + let len = cap.read_32(3) as usize; + match cap.byte0 + { + 1 => { + log_debug!("Common: BAR{} {:#x}+{:#x}", bar, ofs, len); + let io = (bar, ofs, len); + if common_bar.is_none() { + common_bar = Some(io); + } + }, + 2 => { + log_debug!("Notify: BAR{} {:#x}+{:#x}", bar, ofs, len); + let io = (bar, ofs, len); + let mult = cap.read_32(4); + + if notify_bar.is_none() { + notify_bar = Some( (io, mult,) ); + } + }, + 3 => log_debug!("Isr: BAR{} {:#x}+{:#x}", bar, ofs, len), + 4 => { + log_debug!("Device Config: BAR{} {:#x}+{:#x}", bar, ofs, len); + let io = (bar, ofs, len); + if device_cfg_bar.is_none() { + device_cfg_bar = Some(io); + } + }, + 5 => log_debug!("PCI CFG: BAR{} {:#x}+{:#x}", bar, ofs, len), + _ => {}, + } + }, + _ => {}, + } + } + + match (common_bar, device_cfg_bar, notify_bar) + { + ( Some(common), Some(dev_cfg), Some( (notify, notify_mult) ) ) => { + let io = ::interface::PciRegions { + common: bus_dev.bind_io_slice( common.0, Some((common.1, common.2)) ), + notify: bus_dev.bind_io_slice( notify.0, Some((notify.1, notify.2)) ), + notify_off_mult: notify_mult, + dev_cfg: bus_dev.bind_io_slice( dev_cfg.0, Some((dev_cfg.1, dev_cfg.2)) ), + }; + ::devices::new_boxed(dev, ::interface::Pci::new(io, irq)) + }, + (common_bar, device_cfg_bar, notify_bar,) => { + log_error!("VirtIO PCI device doesn't have a full set of capabilities - common={:?} dev_cfg={:?} notify={:?}", common_bar, device_cfg_bar, notify_bar); + return Box::new( NullDevice ); + }, + } } } +mod pci_helpers +{ + pub struct Capability<'a> { + dev: &'a ::kernel::device_manager::BusDevice, + pub id: u8, + ofs: u8, + len: u8, + pub byte0: u8, + } + impl<'a> Capability<'a> + { + pub fn read_32(&self, idx: usize) -> u32 { + assert!(idx < self.len as usize / 4); + self.dev.get_attr_idx("raw_config", self.ofs as usize + idx*4).unwrap_u32() + } + } + + pub struct CapabilityIter<'a> + { + dev: &'a ::kernel::device_manager::BusDevice, + cap_ptr: u8, + } + impl<'a> CapabilityIter<'a> + { + pub fn new(dev: &'a ::kernel::device_manager::BusDevice) -> Self + { + // TODO: Assert that it's PCI + CapabilityIter { + dev: dev, + cap_ptr: if (dev.get_attr_idx("raw_config", 0x4).unwrap_u32() >> 16) & 0x10 != 0 { + (dev.get_attr_idx("raw_config", 0x34).unwrap_u32() & 0xFC) as u8 + } + else { + 0 + }, + } + } + } + impl<'a> Iterator for CapabilityIter<'a> + { + type Item = Capability<'a>; + fn next(&mut self) -> Option<Capability<'a>> + { + if self.cap_ptr == 0 { + None + } + else { + let cap_hdr = self.dev.get_attr_idx("raw_config", self.cap_ptr as usize).unwrap_u32(); + let (id, next, len, byte0) = (cap_hdr as u8, (cap_hdr >> 8) as u8, (cap_hdr >> 16) as u8, (cap_hdr >> 24) as u8); + let rv = Capability { + dev: self.dev, + id: id, + ofs: self.cap_ptr, + len: len, + byte0: byte0, + }; + self.cap_ptr = next; + Some(rv) + } + } + } +} diff --git a/Kernel/Modules/virtio/interface.rs b/Kernel/Modules/virtio/interface.rs index 187101d2036dc767567ef91e4a71e639daa73300..fd83014fba6384b1ac5ddda01e0c4ee416611892 100644 --- a/Kernel/Modules/virtio/interface.rs +++ b/Kernel/Modules/virtio/interface.rs @@ -9,8 +9,6 @@ use queue::Queue; pub trait Interface { - fn new(io: IOBinding, irq: u32) -> Self; - fn bind_interrupt(&mut self, cb: Box<FnMut()->bool + Send + 'static>); fn negotiate_features(&mut self, supported: u32) -> u32; @@ -27,6 +25,149 @@ pub trait Interface unsafe fn cfg_write_32(&self, ofs: usize, v: u32); } +pub struct PciRegions { + pub common: IOBinding, + pub notify: IOBinding, + pub notify_off_mult: u32, + pub dev_cfg: IOBinding, +} +#[repr(usize)] +#[allow(dead_code,non_camel_case_types)] +enum PciCommonReg { + device_feature_select = 0x0, // u32 RW + device_feature = 0x4, // u32 RO + driver_feature_select = 0x8, // u32 RW + driver_feature = 0xC, // u32 RW + msix_config = 0x10, // u16 RW + num_queues = 0x12, // u16 RO + device_status = 0x14, // u8 RW + config_generation = 0x15, // u8 RO + queue_select = 0x16, // u16 + queue_size = 0x18, // u16 + queue_msix_vector = 0x1a, // u16 + queue_enable = 0x1c, // u16 + queue_notify_off = 0x1e, // u16 + queue_desc = 0x20, // u64 + queue_avail = 0x28, // u64 + queue_used = 0x30, // u64 +} +pub struct Pci { + bars: PciRegions, + + irq_gsi: u32, + irq_handle: Option<::kernel::irqs::ObjectHandle>, + + queue_notify_offsets: Vec<u32>, +} +impl Pci +{ + pub fn new(io: PciRegions, irq_gsi: u32) -> Self { + // SAFE: Unique access, read-only + let nqueues = unsafe { io.common.read_16(PciCommonReg::num_queues as usize) as usize }; + let queue_notify_offsets = (0 .. nqueues).map(|q| { + // SAFE: Unique access, no memory + unsafe { + io.common.write_16(PciCommonReg::queue_select as usize, q as u16); + io.common.read_16(PciCommonReg::queue_notify_off as usize) as u32 * io.notify_off_mult + } + }).collect(); + log_debug!("nqueues = {}, queue_notify_offsets={:?}", nqueues, queue_notify_offsets); + + let mut rv = Pci { + bars: io, + irq_gsi: irq_gsi, + irq_handle: None, + queue_notify_offsets: queue_notify_offsets, + }; + // SAFE: Unique access + unsafe { + rv.set_device_status(0x0); // Reset + rv.set_device_status(0x1); // Acknowledge + } + rv + } + unsafe fn set_device_status(&mut self, val: u8) { + self.bars.common.write_8(0x10, val); // device_status + } +} +impl Interface for Pci +{ + fn bind_interrupt(&mut self, cb: Box<FnMut()->bool + Send + 'static>) { + self.irq_handle = Some( ::kernel::irqs::bind_object(self.irq_gsi, cb) ); + } + + fn negotiate_features(&mut self, supported: u32) -> u32 { + // SAFE: Unique access + unsafe { + let dev_supported = self.bars.common.read_32(PciCommonReg::device_feature as usize); + let common = dev_supported & supported; + self.bars.common.write_32(PciCommonReg::device_feature_select as usize, common); + common + } + } + + fn get_queue(&mut self, idx: usize, size: usize) -> Option<Queue> { + if idx >= self.queue_notify_offsets.len() { + log_error!("Request for queue {} is out of valid range {}", idx, self.queue_notify_offsets.len()); + return None + } + // SAFE: Unique access, so no race possible + unsafe { + self.bars.common.write_16(PciCommonReg::queue_select as usize, idx as u16); + } + // SAFE: Unique access + let max_size = unsafe { self.bars.common.read_16(PciCommonReg::queue_size as usize) as usize }; + if max_size == 0 { + None + } + else { + let size = if size == 0 || size > max_size { max_size } else { size }; + let queue = Queue::new(idx, size); + + // SAFE: Unique access, so no race possible + unsafe { + self.bars.common.write_32(PciCommonReg::queue_size as usize, size as u32); // queue_size + let addr = queue.phys_addr_desctab(); + self.bars.common.write_32(PciCommonReg::queue_desc as usize, addr as u32); + self.bars.common.write_32(PciCommonReg::queue_desc as usize + 4, (addr >> 32) as u32); + let addr = queue.phys_addr_avail(); + self.bars.common.write_32(PciCommonReg::queue_avail as usize, addr as u32); + self.bars.common.write_32(PciCommonReg::queue_avail as usize + 4, (addr >> 32) as u32); + let addr = queue.phys_addr_used(); + self.bars.common.write_32(PciCommonReg::queue_used as usize, addr as u32); + self.bars.common.write_32(PciCommonReg::queue_used as usize + 4, (addr >> 32) as u32); + + self.bars.common.write_16(PciCommonReg::queue_enable as usize, 1); + } + + Some(queue) + } + } + + fn set_driver_ok(&mut self) { + // SAFE: Unique access + unsafe { + self.set_device_status(0x4); + } + } + + fn notify_queue(&self, idx: usize) { + log_trace!("notify_queue({})", idx); + // SAFE: Atomic write + unsafe { + self.bars.notify.write_16(self.queue_notify_offsets[idx] as usize, idx as u16) + } + } + + unsafe fn cfg_read_32(&self, ofs: usize) -> u32 { + assert!(ofs + 4 <= 0x100); + self.bars.dev_cfg.read_32(ofs) + } + unsafe fn cfg_write_32(&self, ofs: usize, v: u32) { + assert!(ofs + 4 <= 0x100); + self.bars.dev_cfg.write_32(ofs, v); + } +} /// Memory-Mapped IO binding pub struct Mmio { @@ -34,9 +175,9 @@ pub struct Mmio { irq_gsi: u32, irq_handle: Option<::kernel::irqs::ObjectHandle>, } -impl Interface for Mmio +impl Mmio { - fn new(io: IOBinding, irq_gsi: u32) -> Self { + pub fn new(io: IOBinding, irq_gsi: u32) -> Self { let mut rv = Mmio { io: io, irq_gsi: irq_gsi, @@ -50,7 +191,12 @@ impl Interface for Mmio } rv } - + unsafe fn set_device_status(&mut self, val: u32) { + self.io.write_32(0x70, val); + } +} +impl Interface for Mmio +{ fn bind_interrupt(&mut self, cb: Box<FnMut()->bool + Send + 'static>) { self.irq_handle = Some( ::kernel::irqs::bind_object(self.irq_gsi, cb) ); } @@ -83,7 +229,8 @@ impl Interface for Mmio unsafe { self.io.write_32(0x38, size as u32); //self.io.write_32(0x3C, ); // QueueAlign - TODO: What value to use here - let page = queue.phys_addr() / ::kernel::PAGE_SIZE as u64; + // TODO: This is for the legacy spec + let page = queue.phys_addr_desctab() / ::kernel::PAGE_SIZE as u64; log_debug!("size = {}, page={:#x}", size, page); self.io.write_32(0x40, page as u32); } @@ -115,8 +262,3 @@ impl Interface for Mmio self.io.write_32(0x100 + ofs, v); } } -impl Mmio { - unsafe fn set_device_status(&mut self, val: u32) { - self.io.write_32(0x70, val); - } -} diff --git a/Kernel/Modules/virtio/queue.rs b/Kernel/Modules/virtio/queue.rs index 2f9ac08e5bf79c02f810a8639fc499f84f8cc052..90dd75b5ea80222b237b0caab12badbfb871dbc4 100644 --- a/Kernel/Modules/virtio/queue.rs +++ b/Kernel/Modules/virtio/queue.rs @@ -45,12 +45,14 @@ pub const VRING_DESC_F_WRITE : u16 = 2; pub const VRING_DESC_F_INDIRECT : u16 = 4; #[repr(C)] +// sizeof = 16 pub struct VRingDesc { addr: u64, length: u32, flags: u16, next: u16, } +const SIZEOF_VRING_DESC: usize = ::core::mem::size_of::<VRingDesc>(); #[repr(C)] #[derive(Debug)] struct AvailRing { @@ -77,11 +79,14 @@ struct UsedElem { impl Queue { fn get_first_size(count: usize) -> usize { - let first = 16 * count + (2 + count + 1) * 2; + let first = + SIZEOF_VRING_DESC * count // VRingDesc entries + + (2 + count + 1) * 2 // AvailRing + ; (first + 0xFFF) & !0xFFF } fn get_alloc_size(count: usize) -> usize { - let second = 2*3 + 8 * count; + let second = 2*3 + 8 * count; // UsedRing header (and footer) plus UsedElem entries Self::get_first_size(count) + ((second + 0xFFF) & !0xFFF) } @@ -114,9 +119,15 @@ impl Queue } } - pub fn phys_addr(&self) -> u64 { + pub fn phys_addr_desctab(&self) -> u64 { ::kernel::memory::virt::get_phys(self.buffer.as_ref::<u8>(0)) as u64 } + pub fn phys_addr_avail(&self) -> u64 { + ::kernel::memory::virt::get_phys(&self.avail_ring().flags) as u64 + } + pub fn phys_addr_used(&self) -> u64 { + ::kernel::memory::virt::get_phys(&self.used_ring().flags) as u64 + } pub fn send_buffers<'a, I: Interface>(&'a self, interface: &I, buffers: &mut [Buffer<'a>]) -> Request<'a> { assert!(buffers.len() > 0); @@ -175,7 +186,7 @@ impl Queue _lh: self.avail_ring_lock.lock(), // SAFE: Locked ptr: unsafe { - let base_ptr: *const u16 = self.buffer.as_int_mut(16 * self.size); + let base_ptr: *const u16 = self.buffer.as_int_mut(SIZEOF_VRING_DESC * self.size); // NOTE: Constructing an unsized struct pointer let ptr: &mut AvailRing = ::core::mem::transmute( (base_ptr, self.size) ); assert_eq!(&ptr.flags as *const _, base_ptr);