virtio-gpu: start working on the scheme
Signed-off-by: Anhad Singh <andypython@protonmail.com>
This commit is contained in:
Generated
+7
@@ -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",
|
||||
|
||||
@@ -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
@@ -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
@@ -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 doesn’t 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) };
|
||||
|
||||
Reference in New Issue
Block a user