virtio-gpu: start working on the scheme

Signed-off-by: Anhad Singh <andypython@protonmail.com>
This commit is contained in:
Anhad Singh
2023-07-03 17:12:59 +10:00
parent 4bbda21f0b
commit 7cc2f4eff7
15 changed files with 394 additions and 25 deletions
Generated
+9 -7
View File
@@ -773,12 +773,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.17"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if 1.0.0",
]
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
[[package]]
name = "matches"
@@ -1131,9 +1128,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.56"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
dependencies = [
"unicode-ident",
]
@@ -1952,9 +1949,14 @@ dependencies = [
name = "virtio-gpud"
version = "0.1.0"
dependencies = [
"anyhow",
"futures",
"log",
"orbclient",
"pcid",
"redox-daemon",
"redox_syscall 0.3.5",
"static_assertions",
"virtio-core",
]
+1 -1
View File
@@ -38,7 +38,7 @@ fn main() {
let mut scheme = BgaScheme {
bga: bga,
display: File::open("display:input").ok()
display: File::open("display/vesa:input").ok()
};
scheme.update_size();
+4
View File
@@ -11,3 +11,7 @@ popd
pushd virtio-blkd
cargo fmt
popd
pushd virtio-gpud
cargo fmt
popd
+1 -1
View File
@@ -45,7 +45,7 @@ fn daemon(daemon: redox_daemon::Daemon) -> ! {
let input = OpenOptions::new()
.write(true)
.open("display:input")
.open("display/vesa:input")
.expect("ps2d: failed to open display:input");
let mut event_file = OpenOptions::new()
+1 -1
View File
@@ -382,7 +382,7 @@ fn main() {
let report_ty = ReportTy::Input;
let report_id = 0;
let mut display = File::open("display:input").expect("Failed to open orbital input socket");
let mut display = File::open("display/vesa:input").expect("Failed to open orbital input socket");
//TODO: get dynamically
let mut display_width = 0;
+1 -1
View File
@@ -206,7 +206,7 @@ fn main() {
let mut width = 0;
let mut height = 0;
let mut display_opt = File::open("display:input").ok();
let mut display_opt = File::open("display/vesa:input").ok();
if let Some(ref display) = display_opt {
let mut buf: [u8; 4096] = [0; 4096];
if let Ok(count) = syscall::fpath(display.as_raw_fd() as usize, &mut buf) {
+1 -1
View File
@@ -90,7 +90,7 @@ fn main() {
redox_daemon::Daemon::new(|daemon| inner(daemon, framebuffers, &spec)).expect("failed to create daemon");
}
fn inner(daemon: redox_daemon::Daemon, framebuffers: Vec<FrameBuffer>, spec: &[bool]) -> ! {
let mut socket = File::create(":display").expect("vesad: failed to create display scheme");
let mut socket = File::create(":display/vesa").expect("vesad: failed to create display scheme");
let mut scheme = DisplayScheme::new(framebuffers, &spec);
+2 -6
View File
@@ -12,7 +12,7 @@ use syscall::{Io, PHYSMAP_NO_CACHE, PHYSMAP_WRITE};
use crate::spec::*;
use crate::transport::{Error, StandardTransport};
use crate::utils::{VolatileCell, align_down};
use crate::utils::{align_down, VolatileCell};
pub struct Device<'a> {
pub transport: Arc<StandardTransport<'a>>,
@@ -186,11 +186,7 @@ pub fn probe_device<'a>(pcid_handle: &mut PcidServerHandle) -> Result<Device<'a>
let size = offset + capability.length as usize;
let addr = syscall::physmap(
aligned_addr,
size,
PHYSMAP_WRITE | PHYSMAP_NO_CACHE,
)?;
let addr = syscall::physmap(aligned_addr, size, PHYSMAP_WRITE | PHYSMAP_NO_CACHE)?;
addr + offset
};
+11 -2
View File
@@ -86,6 +86,12 @@ impl<'a> Future for PendingRequest<'a> {
.insert(self.first_descriptor, cx.waker().clone());
let used_head = self.queue.used.head_index();
if used_head == self.queue.used_head.load(Ordering::SeqCst) {
// No new requests have been completed.
return Poll::Pending;
}
let used_element = self.queue.used.get_element_at((used_head - 1) as usize);
let written = used_element.written.get();
@@ -109,6 +115,8 @@ impl<'a> Future for PendingRequest<'a> {
.lock()
.unwrap()
.remove(&self.first_descriptor);
self.queue.used_head.store(used_head, Ordering::SeqCst);
return Poll::Ready(written);
} else {
return Poll::Pending;
@@ -122,6 +130,7 @@ pub struct Queue<'a> {
pub used: Used<'a>,
pub descriptor: Dma<[Descriptor]>,
pub available: Available<'a>,
pub used_head: AtomicU16,
notification_bell: &'a mut AtomicU16,
descriptor_stack: crossbeam_queue::SegQueue<u16>,
@@ -148,6 +157,7 @@ impl<'a> Queue<'a> {
waker: Mutex::new(std::collections::HashMap::new()),
queue_index,
descriptor_stack,
used_head: AtomicU16::new(0),
sref: sref.clone(),
})
}
@@ -190,8 +200,6 @@ impl<'a> Queue<'a> {
self.notification_bell
.store(self.queue_index, Ordering::SeqCst);
assert_eq!(self.used.flags(), 0);
PendingRequest {
queue: self.sref.upgrade().unwrap(),
first_descriptor: first_descriptor as u32,
@@ -470,6 +478,7 @@ impl<'a> StandardTransport<'a> {
event_queue
.add(irq_fd, move |_| -> Result<Option<usize>, std::io::Error> {
// Wake up the tasks waiting on the queue.
for (_, task) in queue_copy.waker.lock().unwrap().iter() {
task.wake_by_ref();
}
+12 -1
View File
@@ -1,7 +1,7 @@
use core::cell::UnsafeCell;
use core::fmt::Debug;
use core::marker::PhantomData;
#[derive(Debug)]
#[repr(C)]
pub struct VolatileCell<T> {
value: UnsafeCell<T>,
@@ -28,6 +28,17 @@ impl<T: Copy> VolatileCell<T> {
}
}
impl<T> Debug for VolatileCell<T>
where
T: Debug + Copy,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VolatileCell")
.field("value", &self.get())
.finish()
}
}
unsafe impl<T> Sync for VolatileCell<T> {}
#[repr(C)]
+5
View File
@@ -6,8 +6,13 @@ authors = ["Anhad Singh <andypython@protonmail.com>"]
[dependencies]
log = "0.4"
static_assertions = "1.1.0"
futures = { version = "0.3.28", features = ["executor"] }
anyhow = "1.0.71"
virtio-core = { path = "../virtio-core" }
pcid = { path = "../pcid" }
redox-daemon = "0.1"
redox_syscall = "0.3"
orbclient = "0.3.27"
+45
View File
@@ -0,0 +1,45 @@
diff --git a/src/main.rs b/src/main.rs
index a99488a..ec97fdd 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -78,6 +78,40 @@ pub fn run(file: &Path) -> Result<()> {
println!("init: failed to run: no argument");
},
"run.d" => if let Some(new_dir) = args.next() {
+ // On startup, the VESA display driver is started which basically makes use of the framebuffer
+ // provided by the firmware. The GPU device are latter started by `pcid` (such as `virtio-gpu`).
+ let mut devices = vec![];
+ let schemes = std::fs::read_dir(":").unwrap();
+
+ for entry in schemes {
+ let path = entry.unwrap().path();
+ let path_str = path
+ .into_os_string()
+ .into_string()
+ .expect("init: failed to convert path to string");
+
+ if path_str.contains("display") {
+ println!("init: found display scheme {}", path_str);
+ devices.push(path_str);
+ }
+ }
+
+ let device = devices.iter().filter(|d| !d.contains("vesa")).collect::<Vec<_>>();
+ let device = if device.is_empty() {
+ // No GPU available, fallback to VESA display which *should* always be accessible via `display/vesa:`.
+ "vesa"
+ } else {
+ // Parts:
+ // :/display/virtio-gpu
+ //
+ // 1st: ":"
+ // 2nd: "display"
+ // 3rd: "virtio-gpu" (the one we want!)
+ device[0].split("/").nth(2).unwrap()
+ };
+
+ std::env::set_var("DISPLAY", &format!("display/{}:3/activate", device));
+
let mut entries = vec![];
match read_dir(&new_dir) {
Ok(list) => for entry_res in list {
+13
View File
@@ -0,0 +1,13 @@
diff --git a/src/main.rs b/src/main.rs
index 1cdf9cf..3859ac0 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -63,7 +63,7 @@ fn orbital(daemon: Daemon) -> Result<(), String> {
.enable();
let mut args = env::args().skip(1);
- let display_path = args.next().ok_or("no display argument")?;
+ let display_path = env::var("DISPLAY").unwrap();
let login_cmd = args.next().ok_or("no login manager argument")?;
core::fix_env(&display_path).map_err(|_| "error setting env vars")?;
+180 -4
View File
@@ -1,7 +1,154 @@
use pcid_interface::PcidServerHandle;
use virtio_core::transport::Error;
//! `virtio-gpu` is a virtio based graphics adapter. It can operate in 2D mode and in 3D mode.
//!
//! XXX: 3D mode will offload rendering ops to the host gpu and therefore requires a GPU with 3D support
//! on the host machine.
fn deamon(deamon: redox_daemon::Daemon) -> Result<(), Error> {
#![feature(async_closure)]
use std::fs::File;
use std::io::{Read, Write};
use pcid_interface::PcidServerHandle;
use syscall::{Packet, SchemeMut};
use virtio_core::utils::VolatileCell;
use virtio_core::MSIX_PRIMARY_VECTOR;
mod scheme;
// const VIRTIO_GPU_EVENT_DISPLAY: u32 = 1 << 0;
const VIRTIO_GPU_MAX_SCANOUTS: usize = 16;
#[repr(C)]
pub struct GpuConfig {
/// Signals pending events to the driver.
pub events_read: VolatileCell<u32>, // read-only
/// Clears pending events in the device (write-to-clear).
pub events_clear: VolatileCell<u32>, // write-only
pub min_scanouts: VolatileCell<u32>,
pub num_capsets: VolatileCell<u32>,
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[repr(u32)]
pub enum CommandTy {
Undefined = 0,
// 2D commands
GetDisplayInfo = 0x0100,
ResourceCreate2d,
ResourceUnref,
SetScanout,
ResourceFlush,
TransferToHost2d,
ResourceAttachBacking,
ResourceDetachBacking,
GetCapsetInfo,
GetCapset,
GetEdid,
ResourceAssignUuid,
ResourceCreateBlob,
SetScanoutBlob,
// 3D commands
CtxCreate = 0x0200,
CtxDestroy,
CtxAttachResource,
CtxDetachResource,
ResourceCreate3d,
TransferToHost3d,
TransferFromHost3d,
Submit3d,
ResourceMapBlob,
ResourceUnmapBlob,
// cursor commands
UpdateCursor = 0x0300,
MoveCursor,
// success responses
RespOkNodata = 0x1100,
RespOkDisplayInfo,
RespOkCapsetInfo,
RespOkCapset,
RespOkEdid,
RespOkResourceUuid,
RespOkMapInfo,
// error responses
RespErrUnspec = 0x1200,
RespErrOutOfMemory,
RespErrInvalidScanoutId,
RespErrInvalidResourceId,
RespErrInvalidContextId,
RespErrInvalidParameter,
}
static_assertions::const_assert_eq!(core::mem::size_of::<CommandTy>(), 4);
#[derive(Debug)]
#[repr(C)]
pub struct ControlHeader {
pub ty: VolatileCell<CommandTy>,
pub flags: VolatileCell<u32>,
pub fence_id: VolatileCell<u64>,
pub ctx_id: VolatileCell<u32>,
pub ring_index: VolatileCell<u8>,
padding: [u8; 3],
}
impl Default for ControlHeader {
fn default() -> Self {
Self {
ty: VolatileCell::new(CommandTy::Undefined),
flags: VolatileCell::new(0),
fence_id: VolatileCell::new(0),
ctx_id: VolatileCell::new(0),
ring_index: VolatileCell::new(0),
padding: [0; 3],
}
}
}
#[derive(Debug)]
#[repr(C)]
pub struct GpuRect {
pub x: VolatileCell<u32>,
pub y: VolatileCell<u32>,
pub width: VolatileCell<u32>,
pub height: VolatileCell<u32>,
}
#[derive(Debug)]
#[repr(C)]
pub struct DisplayInfo {
pub rect: GpuRect,
pub enabled: VolatileCell<u32>,
pub flags: VolatileCell<u32>,
}
#[derive(Debug)]
#[repr(C)]
pub struct GetDisplayInfo {
pub header: ControlHeader,
pub display_info: [DisplayInfo; VIRTIO_GPU_MAX_SCANOUTS],
}
impl Default for GetDisplayInfo {
fn default() -> Self {
Self {
header: ControlHeader {
ty: VolatileCell::new(CommandTy::GetDisplayInfo),
..Default::default()
},
display_info: unsafe { core::mem::zeroed() },
}
}
}
fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
let mut pcid_handle = PcidServerHandle::connect_default()?;
// Double check that we have the right device.
@@ -13,7 +160,36 @@ fn deamon(deamon: redox_daemon::Daemon) -> Result<(), Error> {
log::info!("virtio-gpu: initiating startup sequence :^)");
let device = virtio_core::probe_device(&mut pcid_handle)?;
loop {}
// Negotiate features.
device.transport.finalize_features();
// Queue for sending control commands.
let control_queue = device
.transport
.setup_queue(MSIX_PRIMARY_VECTOR, &device.irq_handle)?;
// Queue for sending cursor updates.
let cursor_queue = device
.transport
.setup_queue(MSIX_PRIMARY_VECTOR, &device.irq_handle)?;
device.transport.run_device();
deamon.ready().unwrap();
let mut socket_file = File::create(":display/virtio-gpu")?;
let mut scheme = scheme::Display::new(control_queue, cursor_queue);
loop {
let mut packet = Packet::default();
socket_file
.read(&mut packet)
.expect("virtio-gpud: failed to read disk scheme");
scheme.handle(&mut packet);
socket_file
.write(&packet)
.expect("virtio-gpud: failed to read disk scheme");
}
}
fn daemon_runner(redox_daemon: redox_daemon::Daemon) -> ! {
+108
View File
@@ -0,0 +1,108 @@
use std::{
collections::BTreeMap,
sync::{
atomic::{AtomicUsize, Ordering},
Arc,
},
};
use orbclient::Event;
use syscall::{Dma, Error as SysError, SchemeMut, EINVAL, EPERM};
use virtio_core::{
spec::{Buffer, ChainBuilder, DescriptorFlags},
transport::{Error, Queue},
utils::VolatileCell,
};
use crate::{CommandTy, ControlHeader, GetDisplayInfo};
pub enum Handle {
Input(InputHandle),
Screen(ScreenHandle),
}
pub struct InputHandle {}
pub struct ScreenHandle {
id: usize,
}
pub struct Display<'a> {
control_queue: Arc<Queue<'a>>,
cursor_queue: Arc<Queue<'a>>,
display_id: usize,
handles: BTreeMap<usize, Handle>,
next_id: AtomicUsize,
}
impl<'a> Display<'a> {
pub fn new(control_queue: Arc<Queue<'a>>, cursor_queue: Arc<Queue<'a>>) -> Self {
Self {
control_queue,
cursor_queue,
display_id: 0,
handles: BTreeMap::new(),
next_id: AtomicUsize::new(0),
}
}
async fn get_display_info(&self) -> Result<Dma<GetDisplayInfo>, Error> {
let header = Dma::new(ControlHeader {
ty: VolatileCell::new(CommandTy::GetDisplayInfo),
..Default::default()
})?;
let response = Dma::new(GetDisplayInfo::default())?;
let command = ChainBuilder::new()
.chain(Buffer::new(&header))
.chain(Buffer::new(&response).flags(DescriptorFlags::WRITE_ONLY))
.build();
self.control_queue.send(command).await;
assert!(response.header.ty.get() == CommandTy::RespOkDisplayInfo);
Ok(response)
}
}
impl<'a> SchemeMut for Display<'a> {
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)
} else {
let mut parts = path.split('/');
let screen = parts.next().unwrap_or("").split('.');
dbg!(screen);
todo!();
}
}
fn read(&mut self, id: usize, buf: &mut [u8]) -> syscall::Result<usize> {
todo!()
}
fn write(&mut self, id: usize, buf: &[u8]) -> syscall::Result<usize> {
let handle = self.handles.get_mut(&id).ok_or(SysError::new(EINVAL))?;
match handle {
Handle::Input(_) => todo!(),
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) };
dbg!(events);
todo!()
}
}
}
}