Skip to content
Snippets Groups Projects
Commit d6656a89 authored by John Hodge's avatar John Hodge
Browse files

VirtIO - Draft up a GPU driver (doesn't even run yet, virtio PCI is a pain)

parent 73128462
No related merge requests found
......@@ -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};
......
......@@ -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)
}
}
......@@ -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)
......
/*
*/
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,
}
}
......@@ -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)
}
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment