302 lines
7.5 KiB
Rust
302 lines
7.5 KiB
Rust
use core::mem::MaybeUninit;
|
|
|
|
use bytemuck::{Pod, Zeroable};
|
|
use device_api::dma::DmaAllocator;
|
|
use libk::{
|
|
device::display::PixelFormat,
|
|
dma::{BusAddress, DmaBuffer},
|
|
error::Error,
|
|
};
|
|
use libk_util::sync::IrqSafeSpinlockGuard;
|
|
use ygg_driver_virtio_core::{queue::VirtQueue, transport::Transport};
|
|
|
|
#[derive(Clone, Copy, Pod, Zeroable)]
|
|
#[repr(C)]
|
|
pub struct ControlHeader {
|
|
pub ty: u32,
|
|
pub flags: u32,
|
|
pub fence_id: u64,
|
|
pub ctx_id: u32,
|
|
pub ring_idx: u8,
|
|
_0: [u8; 3],
|
|
}
|
|
|
|
#[derive(Clone, Copy, Pod, Zeroable)]
|
|
#[repr(C)]
|
|
pub struct Rect {
|
|
pub x: u32,
|
|
pub y: u32,
|
|
pub width: u32,
|
|
pub height: u32,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Pod, Zeroable)]
|
|
#[repr(C)]
|
|
pub struct ScanoutInfo {
|
|
pub r: Rect,
|
|
pub enabled: u32,
|
|
pub flags: u32,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Pod, Zeroable)]
|
|
#[repr(C)]
|
|
pub struct ResourceCreate2d {
|
|
pub header: ControlHeader,
|
|
pub resource_id: u32,
|
|
pub format: u32,
|
|
pub width: u32,
|
|
pub height: u32,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Pod, Zeroable)]
|
|
#[repr(C)]
|
|
pub struct SetScanout {
|
|
pub header: ControlHeader,
|
|
pub r: Rect,
|
|
pub scanout_id: u32,
|
|
pub resource_id: u32,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Pod, Zeroable)]
|
|
#[repr(C)]
|
|
pub struct GpuMemEntry {
|
|
pub addr: u64,
|
|
pub length: u32,
|
|
pub _0: u32,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Pod, Zeroable)]
|
|
#[repr(C)]
|
|
pub struct ResourceAttachBacking {
|
|
pub header: ControlHeader,
|
|
pub resource_id: u32,
|
|
pub nr_entries: u32,
|
|
pub entry: GpuMemEntry,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Pod, Zeroable)]
|
|
#[repr(C)]
|
|
pub struct ResourceFlush {
|
|
pub header: ControlHeader,
|
|
pub r: Rect,
|
|
pub resource_id: u32,
|
|
pub _0: u32,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Pod, Zeroable)]
|
|
#[repr(C)]
|
|
pub struct TransferToHost2d {
|
|
pub header: ControlHeader,
|
|
pub r: Rect,
|
|
pub offset: u64,
|
|
pub resource_id: u32,
|
|
pub _0: u32,
|
|
}
|
|
|
|
pub struct ControlLock<'a, T: Transport> {
|
|
control: IrqSafeSpinlockGuard<'a, VirtQueue>,
|
|
transport: IrqSafeSpinlockGuard<'a, T>,
|
|
}
|
|
|
|
impl<'a, T: Transport> ControlLock<'a, T> {
|
|
pub const fn new(
|
|
control: IrqSafeSpinlockGuard<'a, VirtQueue>,
|
|
transport: IrqSafeSpinlockGuard<'a, T>,
|
|
) -> Self {
|
|
Self { control, transport }
|
|
}
|
|
|
|
fn send_recv<'r, Req: Pod>(
|
|
&mut self,
|
|
dma: &dyn DmaAllocator,
|
|
req: &Req,
|
|
buffer: &'r mut DmaBuffer<[MaybeUninit<u8>]>,
|
|
) -> Result<(&'r ControlHeader, &'r [u8]), Error> {
|
|
let request = DmaBuffer::new_uninit_slice(dma, size_of::<Req>())?;
|
|
let mut request = unsafe { DmaBuffer::assume_init_slice(request) };
|
|
request.copy_from_slice(bytemuck::bytes_of(req));
|
|
|
|
let len = self
|
|
.control
|
|
.add_notify_wait_pop(&[buffer], &[&request], &mut *self.transport)
|
|
.inspect_err(|error| {
|
|
log::warn!("virtio queue: {error:?}");
|
|
})
|
|
.map_err(|_| Error::InvalidArgument)? as usize;
|
|
|
|
if len < size_of::<ControlHeader>() {
|
|
log::warn!("virtio-gpu: invalid device response length: {len}");
|
|
return Err(Error::InvalidArgument);
|
|
}
|
|
|
|
let payload = unsafe { MaybeUninit::slice_assume_init_ref(&buffer[..len]) };
|
|
let header = bytemuck::from_bytes(&payload[..size_of::<ControlHeader>()]);
|
|
let data = &payload[size_of::<ControlHeader>()..len];
|
|
|
|
Ok((header, data))
|
|
}
|
|
|
|
fn send_recv_no_data<Req: Pod>(
|
|
&mut self,
|
|
dma: &dyn DmaAllocator,
|
|
req: &Req,
|
|
buffer: &mut DmaBuffer<[MaybeUninit<u8>]>,
|
|
) -> Result<(), Error> {
|
|
let (response, _) = self.send_recv(dma, req, buffer)?;
|
|
if response.ty == 0x1100 {
|
|
Ok(())
|
|
} else {
|
|
Err(Error::InvalidArgument)
|
|
}
|
|
}
|
|
|
|
pub fn query_scanouts<'r>(
|
|
&mut self,
|
|
dma: &dyn DmaAllocator,
|
|
max_scanouts: usize,
|
|
buffer: &'r mut DmaBuffer<[MaybeUninit<u8>]>,
|
|
) -> Result<&'r [ScanoutInfo], Error> {
|
|
let request = ControlHeader {
|
|
ty: 0x0100,
|
|
flags: 0,
|
|
fence_id: 0,
|
|
ctx_id: 0,
|
|
ring_idx: 0,
|
|
_0: [0; 3],
|
|
};
|
|
|
|
let (response, data) = self.send_recv(dma, &request, buffer)?;
|
|
if response.ty != 0x1101 {
|
|
log::warn!("virtio-gpu: query_scanouts returned {:#x}", response.ty);
|
|
return Err(Error::InvalidArgument);
|
|
}
|
|
let limit = size_of::<ScanoutInfo>() * max_scanouts;
|
|
|
|
let scanouts =
|
|
bytemuck::try_cast_slice(&data[..limit]).map_err(|_| Error::InvalidArgument)?;
|
|
|
|
Ok(scanouts)
|
|
}
|
|
|
|
pub fn create_resource_2d(
|
|
&mut self,
|
|
dma: &dyn DmaAllocator,
|
|
buffer: &mut DmaBuffer<[MaybeUninit<u8>]>,
|
|
width: u32,
|
|
height: u32,
|
|
pixel_format: PixelFormat,
|
|
) -> Result<u32, Error> {
|
|
let format = match pixel_format {
|
|
PixelFormat::R8G8B8A8 => 67,
|
|
_ => todo!(),
|
|
};
|
|
let request = ResourceCreate2d {
|
|
header: ControlHeader {
|
|
ty: 0x0101,
|
|
..ControlHeader::zeroed()
|
|
},
|
|
resource_id: 1,
|
|
width,
|
|
height,
|
|
format,
|
|
};
|
|
|
|
self.send_recv_no_data(dma, &request, buffer)?;
|
|
|
|
Ok(1)
|
|
}
|
|
|
|
pub fn attach_backing(
|
|
&mut self,
|
|
dma: &dyn DmaAllocator,
|
|
buffer: &mut DmaBuffer<[MaybeUninit<u8>]>,
|
|
resource_id: u32,
|
|
base: BusAddress,
|
|
length: u32,
|
|
) -> Result<(), Error> {
|
|
let request = ResourceAttachBacking {
|
|
header: ControlHeader {
|
|
ty: 0x0106,
|
|
..ControlHeader::zeroed()
|
|
},
|
|
resource_id,
|
|
nr_entries: 1,
|
|
entry: GpuMemEntry {
|
|
addr: base.into_u64(),
|
|
length,
|
|
_0: 0,
|
|
},
|
|
};
|
|
|
|
self.send_recv_no_data(dma, &request, buffer)
|
|
}
|
|
|
|
pub fn set_scanout(
|
|
&mut self,
|
|
dma: &dyn DmaAllocator,
|
|
buffer: &mut DmaBuffer<[MaybeUninit<u8>]>,
|
|
scanout_id: u32,
|
|
resource_id: u32,
|
|
width: u32,
|
|
height: u32,
|
|
) -> Result<(), Error> {
|
|
let request = SetScanout {
|
|
header: ControlHeader {
|
|
ty: 0x0103,
|
|
..ControlHeader::zeroed()
|
|
},
|
|
r: Rect {
|
|
x: 0,
|
|
y: 0,
|
|
width,
|
|
height,
|
|
},
|
|
scanout_id,
|
|
resource_id,
|
|
};
|
|
|
|
self.send_recv_no_data(dma, &request, buffer)
|
|
}
|
|
|
|
pub fn transfer_to_host_2d(
|
|
&mut self,
|
|
dma: &dyn DmaAllocator,
|
|
buffer: &mut DmaBuffer<[MaybeUninit<u8>]>,
|
|
resource_id: u32,
|
|
r: Rect,
|
|
) -> Result<(), Error> {
|
|
let request = TransferToHost2d {
|
|
header: ControlHeader {
|
|
ty: 0x0105,
|
|
..ControlHeader::zeroed()
|
|
},
|
|
r,
|
|
offset: 0,
|
|
resource_id,
|
|
_0: 0,
|
|
};
|
|
|
|
self.send_recv_no_data(dma, &request, buffer)
|
|
}
|
|
|
|
pub fn resource_flush(
|
|
&mut self,
|
|
dma: &dyn DmaAllocator,
|
|
buffer: &mut DmaBuffer<[MaybeUninit<u8>]>,
|
|
resource_id: u32,
|
|
r: Rect,
|
|
) -> Result<(), Error> {
|
|
let request = ResourceFlush {
|
|
header: ControlHeader {
|
|
ty: 0x0104,
|
|
..ControlHeader::zeroed()
|
|
},
|
|
r,
|
|
resource_id,
|
|
_0: 0,
|
|
};
|
|
|
|
self.send_recv_no_data(dma, &request, buffer)
|
|
}
|
|
}
|