virtio-gpu: start working on the scheme

Signed-off-by: Anhad Singh <andypython@protonmail.com>
This commit is contained in:
Anhad Singh
2023-07-04 13:17:58 +10:00
parent 7cc2f4eff7
commit c0950d0686
4 changed files with 344 additions and 19 deletions
Generated
+7
View File
@@ -1011,6 +1011,12 @@ dependencies = [
"uuid",
]
[[package]]
name = "paste"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4b27ab7be369122c218afc2079489cdcb4b517c0a3fc386ff11e1fedfcc2b35"
[[package]]
name = "paw"
version = "1.0.0"
@@ -1953,6 +1959,7 @@ dependencies = [
"futures",
"log",
"orbclient",
"paste",
"pcid",
"redox-daemon",
"redox_syscall 0.3.5",
+1
View File
@@ -9,6 +9,7 @@ log = "0.4"
static_assertions = "1.1.0"
futures = { version = "0.3.28", features = ["executor"] }
anyhow = "1.0.71"
paste = "1.0.13"
virtio-core = { path = "../virtio-core" }
pcid = { path = "../pcid" }
+189 -1
View File
@@ -3,7 +3,7 @@
//! XXX: 3D mode will offload rendering ops to the host gpu and therefore requires a GPU with 3D support
//! on the host machine.
#![feature(async_closure)]
#![feature(int_roundings)]
use std::fs::File;
use std::io::{Read, Write};
@@ -19,6 +19,28 @@ mod scheme;
// const VIRTIO_GPU_EVENT_DISPLAY: u32 = 1 << 0;
const VIRTIO_GPU_MAX_SCANOUTS: usize = 16;
macro_rules! make_getter_setter {
($($field:ident: $return_ty:ty),*) => {
$(
pub fn $field(&self) -> $return_ty {
self.$field.get()
}
paste::item! {
pub fn [<set_ $field>](&mut self, value: $return_ty) {
self.$field.set(value)
}
}
)*
};
(@$field:ident: $return_ty:ty) => {
pub fn $field(&mut self, value: $return_ty) {
self.$field.set(value)
}
};
}
#[repr(C)]
pub struct GpuConfig {
/// Signals pending events to the driver.
@@ -120,6 +142,28 @@ pub struct GpuRect {
pub height: VolatileCell<u32>,
}
impl GpuRect {
pub fn new(x: u32, y: u32, width: u32, height: u32) -> Self {
Self {
x: VolatileCell::new(x),
y: VolatileCell::new(y),
width: VolatileCell::new(width),
height: VolatileCell::new(height),
}
}
#[inline]
pub fn width(&self) -> u32 {
self.width.get()
}
#[inline]
pub fn height(&self) -> u32 {
self.height.get()
}
}
#[derive(Debug)]
#[repr(C)]
pub struct DisplayInfo {
@@ -148,6 +192,150 @@ impl Default for GetDisplayInfo {
}
}
#[derive(Debug, Copy, Clone)]
#[repr(u32)]
pub enum ResourceFormat {
Unknown = 0,
Bgrx = 2,
Xrgb = 4,
}
#[derive(Debug)]
#[repr(C)]
pub struct ResourceCreate2d {
pub header: ControlHeader,
resource_id: VolatileCell<u32>,
format: VolatileCell<ResourceFormat>,
width: VolatileCell<u32>,
height: VolatileCell<u32>,
}
impl ResourceCreate2d {
make_getter_setter!(resource_id: u32, format: ResourceFormat, width: u32, height: u32);
}
impl Default for ResourceCreate2d {
fn default() -> Self {
Self {
header: ControlHeader {
ty: VolatileCell::new(CommandTy::ResourceCreate2d),
..Default::default()
},
resource_id: VolatileCell::new(0),
format: VolatileCell::new(ResourceFormat::Unknown),
width: VolatileCell::new(0),
height: VolatileCell::new(0),
}
}
}
#[derive(Debug)]
#[repr(C)]
pub struct MemEntry {
pub address: u64,
pub length: u32,
pub padding: u32
}
#[derive(Debug)]
#[repr(C)]
pub struct AttachBacking {
pub header: ControlHeader,
pub resource_id: u32,
pub num_entries: u32
}
impl AttachBacking {
pub fn new(resource_id: u32, num_entries: u32) -> Self {
Self {
header: ControlHeader {
ty: VolatileCell::new(CommandTy::ResourceAttachBacking),
..Default::default()
},
resource_id,
num_entries
}
}
}
#[derive(Debug)]
#[repr(C)]
pub struct ResourceFlush {
pub header: ControlHeader,
pub rect: GpuRect,
pub resource_id: u32,
pub padding: u32
}
impl ResourceFlush {
pub fn new(resource_id: u32, rect: GpuRect) -> Self {
Self {
header: ControlHeader {
ty: VolatileCell::new(CommandTy::ResourceFlush),
..Default::default()
},
rect,
resource_id,
padding: 0
}
}
}
#[repr(C)]
#[derive(Debug)]
pub struct SetScanout {
pub header: ControlHeader,
pub rect: GpuRect,
pub scanout_id: u32,
pub resource_id: u32,
}
impl SetScanout {
pub fn new(scanout_id: u32, resource_id: u32, rect: GpuRect) -> Self {
Self {
header: ControlHeader {
ty: VolatileCell::new(CommandTy::SetScanout),
..Default::default()
},
rect,
scanout_id,
resource_id,
}
}
}
#[derive(Debug)]
#[repr(C)]
pub struct XferToHost2d {
pub header: ControlHeader,
pub rect: GpuRect,
pub offset: u64,
pub resource_id: u32,
pub padding: u32,
}
impl XferToHost2d {
pub fn new(resource_id: u32, rect: GpuRect) -> Self {
Self {
header: ControlHeader {
ty: VolatileCell::new(CommandTy::TransferToHost2d),
..Default::default()
},
rect,
resource_id,
offset: 0,
padding: 0,
}
}
}
fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
let mut pcid_handle = PcidServerHandle::connect_default()?;
+147 -18
View File
@@ -14,16 +14,12 @@ use virtio_core::{
utils::VolatileCell,
};
use crate::{CommandTy, ControlHeader, GetDisplayInfo};
use crate::*;
pub enum Handle {
Input(InputHandle),
Screen(ScreenHandle),
}
pub struct InputHandle {}
pub struct ScreenHandle {
id: usize,
Screen {
id: usize
},
}
pub struct Display<'a> {
@@ -64,29 +60,163 @@ impl<'a> Display<'a> {
Ok(response)
}
async fn get_resolution(&self) -> Result<(u32, u32), Error> {
let display_info = self.get_display_info().await?;
let width = display_info.display_info[self.display_id].rect.width();
let height = display_info.display_info[self.display_id].rect.height();
Ok((width, height))
}
async fn get_fpath(&mut self, buffer: &mut [u8]) -> syscall::Result<usize> {
let display_info = self.get_display_info().await.unwrap();
let width = display_info.display_info[self.display_id].rect.width();
let height = display_info.display_info[self.display_id].rect.height();
let path = format!("display:0.0/{}/{}", width, height);
// Copy the path into the target buffer.
buffer[..path.len()].copy_from_slice(path.as_bytes());
Ok(path.len())
}
async fn send_request<T>(&mut self, request: Dma<T>) -> Result<Dma<ControlHeader>, Error> {
let header = Dma::new(ControlHeader::default())?;
let command = ChainBuilder::new()
.chain(Buffer::new(&request))
.chain(Buffer::new(&header).flags(DescriptorFlags::WRITE_ONLY))
.build();
self.control_queue.send(command).await;
Ok(header)
}
async fn flush_resource(&mut self, flush: ResourceFlush) -> Result<(), Error> {
let header = self.send_request(Dma::new(flush)?).await?;
assert_eq!(header.ty.get(), CommandTy::RespOkNodata);
Ok(())
}
async fn map_screen(&mut self, offset: usize, size: usize) -> Result<usize, Error> {
// Create a host resource using `VIRTIO_GPU_CMD_RESOURCE_CREATE_2D`.
let (width, height) = self.get_resolution().await?;
let mut request = Dma::new(ResourceCreate2d::default())?;
request.set_width(width);
request.set_height(height);
request.set_format(ResourceFormat::Bgrx);
request.set_resource_id(1); // FIXME(andypython): dynamically allocate resource identifiers
self.send_request(request).await?;
// Allocate a framebuffer from guest ram, and attach it as backing storage to the
// resource just created, using `VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING`. Scatter
// lists are supported, so the framebuffer doesnt need to be contignous in guest
// physical memory.
let bpp = 32;
let fb_size = (width as usize * height as usize * bpp / 8).next_multiple_of(syscall::PAGE_SIZE);
let mut entries = unsafe { Dma::zeroed_unsized(fb_size / syscall::PAGE_SIZE) }?;
for i in 0..fb_size / syscall::PAGE_SIZE {
let address = unsafe { syscall::physalloc(syscall::PAGE_SIZE) }? as u64;
let mapped = unsafe {syscall::physmap(address as usize, syscall::PAGE_SIZE, syscall::PhysmapFlags::PHYSMAP_WRITE)}?;
unsafe {
core::ptr::write_bytes(mapped as *mut u8, 69, syscall::PAGE_SIZE);
}
let entry = MemEntry {
address,
length: syscall::PAGE_SIZE as u32,
padding: 0,
};
entries[i]= entry;
}
let attach_request = Dma::new(AttachBacking::new(1, entries.len() as u32))?;
let header = Dma::new(ControlHeader::default())?;
let command = ChainBuilder::new()
.chain(Buffer::new(&attach_request))
.chain(Buffer::new_unsized(&entries))
.chain(Buffer::new(&header).flags(DescriptorFlags::WRITE_ONLY))
.build();
self.control_queue.send(command).await;
assert_eq!(header.ty.get(), CommandTy::RespOkNodata);
let rect = GpuRect::new(0,0,width,height);
let scanout_request = Dma::new(SetScanout::new(0, 1, rect))?;
let header = self.send_request(scanout_request).await?;
assert_eq!(header.ty.get(), CommandTy::RespOkNodata);
let rect = GpuRect::new(0,0,width,height);
let req = Dma::new(XferToHost2d::new(1, rect))?;
let header = self.send_request(req).await?;
assert_eq!(header.ty.get(), CommandTy::RespOkNodata);
let rect = GpuRect::new(0,0,width,height);
self.flush_resource(ResourceFlush::new(1, rect)).await?;
todo!()
}
}
impl<'a> SchemeMut for Display<'a> {
fn open(&mut self, path: &str, flags: usize, uid: u32, gid: u32) -> syscall::Result<usize> {
fn open(&mut self, path: &str, _flags: usize, uid: u32, _gid: u32) -> syscall::Result<usize> {
if path == "input" {
if uid != 0 {
return Err(SysError::new(EPERM));
}
let fd = self.next_id.fetch_add(1, Ordering::SeqCst);
self.handles.insert(fd, Handle::Input(InputHandle {}));
Ok(fd)
unimplemented!("input is only supported via `display/vesa:input`")
} else {
let mut parts = path.split('/');
let screen = parts.next().unwrap_or("").split('.');
dbg!(screen);
let mut screen = parts.next().unwrap_or("").split('.');
todo!();
let vt_index = screen.next().unwrap_or("").parse::<usize>().unwrap_or(1);
let id = screen.next().unwrap_or("").parse::<usize>().unwrap_or(0);
dbg!(&vt_index, &id);
let fd = self.next_id.fetch_add(1, Ordering::SeqCst);
self.handles.insert(fd, Handle::Screen { id });
Ok(fd)
}
}
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> syscall::Result<usize> {
let handle = self.handles.get_mut(&id).ok_or(SysError::new(EINVAL))?;
let bytes_copied = match handle {
Handle::Screen { .. } => {
futures::executor::block_on(self.get_fpath(buf))?
}
};
Ok(bytes_copied)
}
fn fmap(&mut self, id: usize, map: &syscall::Map) -> syscall::Result<usize> {
let handle = self.handles.get_mut(&id).ok_or(SysError::new(EINVAL))?;
if let Handle::Screen { .. } = handle {
futures::executor::block_on(self.map_screen(map.offset, map.size));
}
unreachable!()
}
fn read(&mut self, id: usize, buf: &mut [u8]) -> syscall::Result<usize> {
let handle = self.handles.get_mut(&id).ok_or(SysError::new(EINVAL))?;
let path = match handle {
Handle::Screen { .. } => {
futures::executor::block_on(self.get_fpath(buf))?
}
};
todo!()
}
@@ -94,8 +224,7 @@ impl<'a> SchemeMut for Display<'a> {
let handle = self.handles.get_mut(&id).ok_or(SysError::new(EINVAL))?;
match handle {
Handle::Input(_) => todo!(),
Handle::Screen(_) => {
Handle::Screen { .. } => {
let size = buf.len() / core::mem::size_of::<Event>();
let events =
unsafe { core::slice::from_raw_parts(buf.as_ptr().cast::<Event>(), size) };