diff --git a/Kernel/Core/lib/mod.rs b/Kernel/Core/lib/mod.rs index 48aafd7240673cae8e7b82e0ffc161ca6d26e7fc..2559aff13805542a291d95b7b89064439ae7e787 100644 --- a/Kernel/Core/lib/mod.rs +++ b/Kernel/Core/lib/mod.rs @@ -14,7 +14,7 @@ pub use self::sparse_vec::SparseVec; pub use self::string::String; pub use self::lazy_static::LazyStatic; pub use self::vec_deque::VecDeque; -pub use self::pod::POD; +pub use self::pod::{POD, PodHelpers}; pub use self::pod::{as_byte_slice, as_byte_slice_mut}; diff --git a/Kernel/Core/lib/pod.rs b/Kernel/Core/lib/pod.rs index 85e24523c7e455b1802c10eaf242ecc05eb4e731..a4ed5a5bbbe91e4cdacc5c4ff7be91ab645f115c 100644 --- a/Kernel/Core/lib/pod.rs +++ b/Kernel/Core/lib/pod.rs @@ -6,6 +6,7 @@ /// Plain-old-data trait pub unsafe auto trait POD {} + //impl<T: ::core::ops::Drop> !POD for T {} // - I would love this, but it collides with every other !POD impl impl<T> !POD for ::core::cell::UnsafeCell<T> {} impl<T> !POD for ::core::ptr::NonNull<T> {} @@ -27,3 +28,21 @@ pub fn as_byte_slice_mut<T: ?Sized + POD>(s: &mut T) -> &mut [u8] { // SAFE: Plain-old-data unsafe { ::core::slice::from_raw_parts_mut(s as *mut _ as *mut u8, ::core::mem::size_of_val(s)) } } + +pub trait PodHelpers +{ + fn zeroed() -> Self where Self: Sized + POD { + // SAFE: This method is only ever valid when Self: POD, which allows any bit pattern + unsafe { ::core::mem::zeroed() } + } + fn as_byte_slice(&self) -> &[u8]; + fn as_byte_slice_mut(&mut self) -> &mut [u8]; +} +impl<T: ?Sized + POD> PodHelpers for T { + fn as_byte_slice(&self) -> &[u8] { + as_byte_slice(self) + } + fn as_byte_slice_mut(&mut self) -> &mut [u8] { + as_byte_slice_mut(self) + } +} diff --git a/Kernel/Modules/virtio/devices/mod.rs b/Kernel/Modules/virtio/devices/mod.rs index 11f25151d2c060d4e51f3530185a60267ce2e801..7b81754719c271370fd8b1316722ba3b158ab066 100644 --- a/Kernel/Modules/virtio/devices/mod.rs +++ b/Kernel/Modules/virtio/devices/mod.rs @@ -7,9 +7,10 @@ use kernel::device_manager; use interface::Interface; mod block; +mod video; //mod network; -pub fn new_boxed<T: Interface+Send+'static>(dev: u32, io: device_manager::IOBinding, irq: u32) -> Box<device_manager::DriverInstance> +pub fn new_boxed<T: Interface+Send+Sync+'static>(dev: u32, io: device_manager::IOBinding, irq: u32) -> Box<device_manager::DriverInstance> { match dev { @@ -21,6 +22,7 @@ pub fn new_boxed<T: Interface+Send+'static>(dev: u32, io: device_manager::IOBind Box::new(NullDevice) } 2 => Box::new( block::BlockDevice::new(T::new(io, irq)) ), + 16 => Box::new( video::VideoDevice::new(T::new(io, irq)) ), 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 new file mode 100644 index 0000000000000000000000000000000000000000..47be6b8122b3d6354bd8c8f6645c474d0c5d9199 --- /dev/null +++ b/Kernel/Modules/virtio/devices/video.rs @@ -0,0 +1,192 @@ +/* + */ +use kernel::prelude::*; +use kernel::metadevs::video; +use interface::Interface; +use queue::{Queue,Buffer}; +use kernel::lib::mem::aref::{Aref,ArefBorrow}; +use kernel::async::Mutex; + +pub struct VideoDevice<I> +where + I: Interface + Send + Sync +{ + _core: Aref<DeviceCore<I>> +} +impl<I> ::kernel::device_manager::DriverInstance for VideoDevice<I> +where + I: Interface + Send + Sync +{ +} + +struct DeviceCore<I> +where + I: Interface + Send + Sync +{ + interface: I, + controlq: Queue, + cursorq: Queue, + + scanouts: Mutex<Vec<Option<Framebuffer<I>>>>, +} + +struct Framebuffer<I> +where + I: Interface + Send + Sync +{ + dev: ArefBorrow<DeviceCore<I>>, + /// Handle to video metadev registration + _video_handle: video::FramebufferRegistration, +} + +impl<I> VideoDevice<I> +where + I: 'static + Interface + Send + Sync +{ + pub fn new(mut int: I) -> Self + { + // SAFE: Read-only field + let num_scanouts = unsafe { int.cfg_read_32(8) } as usize; + + let core = Aref::new(DeviceCore { + controlq: int.get_queue(0, 0).expect("Queue #0 'controlq' missing on virtio gpu device"), + cursorq: int.get_queue(1, 0).expect("Queue #1 'cursorq' missing on virtio gpu device"), + scanouts: Mutex::new(Vec::from_fn(num_scanouts, |_| None)), + interface: int, + }); + + let di = core.get_display_info(); + log_debug!("di = {:?}", di); + + VideoDevice { + _core: core, + } + } +} + +impl<I> DeviceCore<I> +where + I: Interface + Send + Sync +{ + + fn get_display_info(&self) -> /*SmallVec<*/[hw::DisplayOne; 16]//> + { + let hdr = hw::CtrlHeader { + type_: hw::VIRTIO_GPU_CMD_GET_DISPLAY_INFO as u32, + flags: hw::VIRTIO_GPU_FLAG_FENCE, + fence_id: 1, + ctx_id: 0, + _padding: 0, + }; + 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() + { + Ok(bytes) => todo!("{} bytes from gpu request", bytes), + Err( () ) => panic!("TODO"), + } + } +} + +impl<I> video::Framebuffer for Framebuffer<I> +where + I: 'static + Interface + Send + Sync +{ + fn as_any(&self) -> &Any { + self as &Any + } + fn activate(&mut self) { + // TODO + } + + fn get_size(&self) -> video::Dims { + // TODO + todo!(""); + } + fn set_size(&mut self, _newsize: video::Dims) -> bool { + // TODO + false + } + + fn blit_inner(&mut self, dst: video::Rect, src: video::Rect) { + } + 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]) { + } + fn fill(&mut self, dst: video::Rect, colour: u32) { + } + fn move_cursor(&mut self, _p: Option<video::Pos>) { + } +} + + +mod hw +{ + #[repr(u32)] + #[allow(non_camel_case_types)] + #[allow(dead_code)] + pub enum CtrlType + { + /* 2d commands */ + VIRTIO_GPU_CMD_GET_DISPLAY_INFO = 0x0100, + VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, + VIRTIO_GPU_CMD_RESOURCE_UNREF, + VIRTIO_GPU_CMD_SET_SCANOUT, + VIRTIO_GPU_CMD_RESOURCE_FLUSH, + VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, + VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, + VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, + /* cursor commands */ + VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300, + VIRTIO_GPU_CMD_MOVE_CURSOR, + /* success responses */ + VIRTIO_GPU_RESP_OK_NODATA = 0x1100, + VIRTIO_GPU_RESP_OK_DISPLAY_INFO, + /* error responses */ + VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200, + VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, + VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID, + VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, + } + pub use self::CtrlType::*; + + pub const VIRTIO_GPU_FLAG_FENCE: u32 = 1 << 0; + + #[repr(C)] + pub struct CtrlHeader + { + pub type_: u32, + pub flags: u32, + pub fence_id: u64, + pub ctx_id: u32, + pub _padding: u32, + } + + #[repr(C)] + #[derive(Debug)] + pub struct Rect + { + pub x: u32, + pub y: u32, + pub width: u32, + pub height: u32, + } + #[repr(C)] + #[derive(Debug)] + pub struct DisplayOne + { + pub r: Rect, + pub enabled: u32, + pub flags: u32, + } +} + diff --git a/Kernel/Modules/virtio/drivers.rs b/Kernel/Modules/virtio/drivers.rs index e3368a619b2268d7ddc4bb3ca9659b1be5d2956c..66d2d59dca047a0ead89e3e742370e81449e80ec 100644 --- a/Kernel/Modules/virtio/drivers.rs +++ b/Kernel/Modules/virtio/drivers.rs @@ -7,10 +7,12 @@ use kernel::device_manager; use devices::NullDevice; +static S_PCI_DRIVER: Pci = Pci; static S_FDT_MMIO_DRIVER: FdtMmioDriver = FdtMmioDriver; pub fn register() { + device_manager::register_driver(&S_PCI_DRIVER); device_manager::register_driver(&S_FDT_MMIO_DRIVER); } @@ -51,4 +53,49 @@ impl device_manager::Driver for FdtMmioDriver } } +struct Pci; +impl device_manager::Driver for Pci +{ + fn name(&self) -> &str { + "virtio-pci" + } + fn bus_type(&self) -> &str { + "pci" + } + fn handles(&self, bus_dev: &::kernel::device_manager::BusDevice) -> u32 + { + let vendor = bus_dev.get_attr("vendor").unwrap_u32(); + let device = bus_dev.get_attr("device").unwrap_u32(); + if vendor == 0x1AF4 && (0x1000 <= device && device <= 0x107F) { + 2 + } + else { + 0 + } + } + fn bind(&self, bus_dev: &mut ::kernel::device_manager::BusDevice) -> Box<::kernel::device_manager::DriverInstance+'static> + { + let irq = bus_dev.get_irq(0); + // 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 + 0x1001 => 2, // block dev + 0x1002 => 5, // memory baloon + 0x1003 => 3, // console + 0x1004 => 8, // SCSI host + 0x1005 => 4, // entropy source + v @ 0x1006 ... 0x1008 => todo!("Unknown PCI ID {:#x}", v), + 0x1009 => 9, // "9P transport" + v @ 0x100A ... 0x103F => todo!("Unknown PCI ID {:#x}", v), + v @ 0x1040 ... 0x107F => v - 0x1040, + v @ _ => panic!("BUGCHECK: Binding with unexpected PCI device id {:#x}", v), + }; + + ::devices::new_boxed::<::interface::Mmio>(dev, io, irq) + } +}