Merge branch 'core' into 'master'

virtio-gpu: handling GPU resets

See merge request redox-os/drivers!101
This commit is contained in:
Jeremy Soller
2023-08-02 15:09:19 +00:00
15 changed files with 741 additions and 230 deletions
Generated
+1
View File
@@ -640,6 +640,7 @@ dependencies = [
"redox-daemon",
"redox-log",
"redox_syscall 0.3.5",
"spin",
]
[[package]]
+2 -2
View File
@@ -37,8 +37,8 @@ fn main() {
print!("{}", format!(" - BGA {}x{}\n", bga.width(), bga.height()));
let mut scheme = BgaScheme {
bga: bga,
display: File::open("display/vesa:input").ok()
bga,
display: File::open("input:producer").ok()
};
scheme.update_size();
+1
View File
@@ -11,3 +11,4 @@ redox-daemon = "0.1.0"
redox-log = "0.1.1"
redox_syscall = "0.3.5"
orbclient = "0.3.27"
spin = "0.9.8"
+150 -3
View File
@@ -1,18 +1,165 @@
#![feature(iter_next_chunk)]
use std::fs::File;
use std::io::{Error, Read};
use std::io::{Error, Read, Write};
unsafe fn any_as_u8_slice<T: Sized>(p: &T) -> &[u8] {
::core::slice::from_raw_parts((p as *const T) as *const u8, ::core::mem::size_of::<T>())
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[repr(usize)]
pub enum VtMode {
Text = 0,
Graphic = 1,
Default = 2,
}
#[derive(Debug, Clone)]
#[repr(C)]
pub struct VtActivate {
pub vt: usize,
pub mode: VtMode,
}
pub struct Handle(File);
impl Handle {
pub fn new<S: Into<String>>(device_name: S) -> Result<Self, Error> {
let path = format!("input:handle/{}", device_name.into());
let path = format!("input:handle/display/{}", device_name.into());
Ok(Self(File::open(path)?))
}
// The return value is the display identifier. It will be used to uniquely
// identify the display on activation events.
pub fn register(&mut self) -> Result<usize, Error> {
Ok(dbg!(self.0.read(&mut [])?))
self.0.read(&mut [])
}
pub fn activate(&mut self, vt: usize, mode: VtMode) -> Result<usize, Error> {
let cmd = VtActivate { vt, mode };
self.0.write(unsafe { any_as_u8_slice(&cmd) })
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[repr(u8)]
enum CmdTy {
Unknown = 0,
Activate,
Deactivate,
Resize,
}
impl From<u8> for CmdTy {
fn from(value: u8) -> Self {
match value {
1 => CmdTy::Activate,
2 => CmdTy::Deactivate,
3 => CmdTy::Resize,
_ => CmdTy::Unknown,
}
}
}
#[derive(Debug)]
pub enum Cmd {
// TODO(andypython): #VT should really need to be a `u8`.
Activate {
vt: usize,
mode: VtMode,
},
Deactivate(usize /* #VT */),
Resize {
// TODO(andypython): do we really need to pass the VT here?
vt: usize,
width: u32,
height: u32,
stride: u32,
},
}
impl Cmd {
fn ty(&self) -> CmdTy {
match self {
Cmd::Activate { .. } => CmdTy::Activate,
Cmd::Deactivate(_) => CmdTy::Deactivate,
Cmd::Resize { .. } => CmdTy::Resize,
}
}
}
pub fn send_comand(file: &mut File, command: Cmd) -> Result<(), syscall::Error> {
use std::os::fd::AsRawFd;
let mut result = vec![];
result.push(command.ty() as u8);
match command {
Cmd::Activate { vt, mode } => {
let cmd = VtActivate { vt, mode };
let bytes = unsafe { any_as_u8_slice(&cmd) };
result.extend_from_slice(bytes);
}
Cmd::Deactivate(vt) => result.extend_from_slice(&vt.to_le_bytes()),
Cmd::Resize {
vt,
width,
height,
stride,
} => {
result.extend_from_slice(&vt.to_le_bytes());
result.extend(width.to_le_bytes());
result.extend(height.to_le_bytes());
result.extend(stride.to_le_bytes());
}
};
let written = syscall::write(file.as_raw_fd() as usize, &result)?;
// XXX: Ensure all of the data is written.
assert_eq!(written, result.len());
Ok(())
}
pub fn parse_command(buffer: &[u8]) -> Option<Cmd> {
const U32_SIZE: usize = core::mem::size_of::<u32>();
const USIZE_SIZE: usize = core::mem::size_of::<usize>();
let mut parser = buffer.iter().cloned();
let command = CmdTy::from(parser.next()?);
let vt = usize::from_le_bytes(parser.next_chunk::<USIZE_SIZE>().ok()?);
match command {
CmdTy::Activate => {
let cmd = unsafe { &*buffer.as_ptr().offset(1).cast::<VtActivate>() };
Some(Cmd::Activate {
vt: cmd.vt,
mode: cmd.mode,
})
}
CmdTy::Deactivate => Some(Cmd::Deactivate(vt)),
CmdTy::Resize => {
let width = parser.next_chunk::<U32_SIZE>().ok()?;
let height = parser.next_chunk::<U32_SIZE>().ok()?;
let stride = parser.next_chunk::<U32_SIZE>().ok()?;
Some(Cmd::Resize {
vt,
width: u32::from_le_bytes(width),
height: u32::from_le_bytes(height),
stride: u32::from_le_bytes(stride),
})
}
CmdTy::Unknown => None,
}
}
+251 -45
View File
@@ -14,9 +14,14 @@
use std::collections::BTreeMap;
use std::fs::File;
use std::io::{Read, Write};
use std::os::fd::AsRawFd;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use inputd::{Cmd, VtActivate, VtMode};
use spin::Mutex;
use orbclient::{Event, EventOption};
use syscall::{Error as SysError, EventFlags, Packet, SchemeMut, EINVAL};
@@ -26,8 +31,11 @@ enum Handle {
events: EventFlags,
pending: Vec<u8>,
notified: bool,
vt: usize,
},
Device {
device: String,
},
Device(Arc<String>),
}
impl Handle {
@@ -36,15 +44,55 @@ impl Handle {
}
}
/// VT Inner State
///
/// This is *required* to be lazily initialized since opening the handle to the display
/// requires the system call to return first. Otherwise, it will block indefinitely.
struct VtInner {
handle_file: File,
mode: VtMode,
}
struct Vt {
display: String,
index: usize,
inner: spin::Once<Mutex<VtInner>>,
}
impl Vt {
pub fn new<D>(display: D, index: usize) -> Arc<Self>
where
D: Into<String>,
{
Arc::new(Self {
display: display.into(),
inner: spin::Once::new(),
index,
})
}
pub fn inner(&self) -> &Mutex<VtInner> {
self.inner.call_once(|| {
let handle_file = File::open(format!("{}:handle", self.display)).unwrap();
Mutex::new(VtInner {
handle_file,
mode: VtMode::Default,
})
})
}
}
struct InputScheme {
handles: BTreeMap<usize, Handle>,
next_id: AtomicUsize,
next_vt_id: AtomicUsize,
vts: BTreeMap<usize, Arc<String>>,
vts: BTreeMap<usize, Arc<Vt>>,
super_key: bool,
active_vt: Option<(Arc<String>, File, usize /* VT index */)>,
active_vt: Option<Arc<Vt>>,
todo: Vec<VtActivate>,
}
impl InputScheme {
@@ -57,6 +105,8 @@ impl InputScheme {
vts: BTreeMap::new(),
super_key: false,
active_vt: None,
todo: vec![],
}
}
}
@@ -66,17 +116,23 @@ impl SchemeMut for InputScheme {
let mut path_parts = path.split('/');
let command = path_parts.next().ok_or(SysError::new(EINVAL))?;
let fd = self.next_id.fetch_add(1, Ordering::SeqCst);
let handle_ty = match command {
"producer" => Handle::Producer,
"consumer" => Handle::Consumer {
events: EventFlags::empty(),
pending: Vec::new(),
notified: false,
},
"consumer" => {
let target = path_parts.next().unwrap().parse::<usize>().unwrap();
Handle::Consumer {
events: EventFlags::empty(),
pending: Vec::new(),
notified: false,
vt: target,
}
}
"handle" => {
let value = path_parts.next().ok_or(SysError::new(EINVAL))?;
Handle::Device(Arc::new(value.to_string()))
let display = path_parts.collect::<Vec<_>>().join("/");
Handle::Device { device: display }
}
_ => unreachable!("inputd: invalid path {path}"),
@@ -84,12 +140,26 @@ impl SchemeMut for InputScheme {
log::info!("inputd: {path} channel has been opened");
let fd = self.next_id.fetch_add(1, Ordering::SeqCst);
self.handles.insert(fd, handle_ty);
Ok(fd)
}
fn fpath(&mut self, id: usize, buf: &mut [u8]) -> syscall::Result<usize> {
let handle = self.handles.get(&id).ok_or(SysError::new(EINVAL))?;
if let Handle::Consumer { vt, .. } = handle {
let display = self.vts.get(vt).ok_or(SysError::new(EINVAL))?;
let vt = format!("{}:{vt}", display.display);
let size = core::cmp::min(vt.len(), buf.len());
buf[..size].copy_from_slice(&vt.as_bytes()[..size]);
Ok(size)
} else {
Err(SysError::new(EINVAL))
}
}
fn read(&mut self, id: usize, buf: &mut [u8]) -> syscall::Result<usize> {
let handle = self.handles.get_mut(&id).ok_or(SysError::new(EINVAL))?;
@@ -104,9 +174,11 @@ impl SchemeMut for InputScheme {
Ok(copy)
}
Handle::Device(device) => {
Handle::Device { device } => {
assert!(buf.is_empty());
let vt = self.next_vt_id.fetch_add(1, Ordering::SeqCst);
self.vts.insert(vt, device.clone());
self.vts.insert(vt, Vt::new(device.clone(), vt));
Ok(vt)
}
@@ -115,6 +187,25 @@ impl SchemeMut for InputScheme {
}
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::Device { device } => {
assert!(buf.len() == core::mem::size_of::<VtActivate>());
// SAFETY: We have verified the size of the buffer above.
let cmd = unsafe { &*buf.as_ptr().cast::<VtActivate>() };
self.vts.insert(cmd.vt, Vt::new(device.clone(), cmd.vt));
self.todo.push(cmd.clone());
return Ok(buf.len());
}
Handle::Consumer { .. } => unreachable!(),
_ => {}
}
if buf.len() == 1 && buf[0] > 0xf4 {
return Ok(1);
}
@@ -126,7 +217,7 @@ impl SchemeMut for InputScheme {
)
};
for event in events.iter() {
'out: for event in events.iter() {
let mut new_active_opt = None;
match event.to_option() {
EventOption::Key(key_event) => match key_event.scancode {
@@ -153,56 +244,73 @@ impl SchemeMut for InputScheme {
_ => (),
},
EventOption::Resize(resize_event) => {
let active_vt = self.active_vt.as_ref().unwrap();
let mut vt_inner = active_vt.inner().lock();
inputd::send_comand(
&mut vt_inner.handle_file,
Cmd::Resize {
vt: active_vt.index,
width: resize_event.width,
height: resize_event.height,
// TODO(andypython): Figure out how to get the stride.
stride: resize_event.width,
},
)?;
}
_ => continue,
}
let deactivate = |vt, current| {
// Drop the old active VT first.
//
// TODO(andypython): This can be drastically improved by introducting something
// like ioctl() to the display scheme.
let deactivate = format!("display/{}:{current}/deactivate", vt);
let _ = File::open(&deactivate);
};
if let Some(new_active) = new_active_opt {
if let Some(vt) = self.vts.get(&new_active) {
// If the VT is already active, don't do anything.
if let Some((vt, _, current)) = self.active_vt.as_ref() {
if new_active == *current {
continue;
}
if new_active == self.active_vt.as_ref().unwrap().index {
continue 'out;
}
deactivate(vt, *current);
} else {
let id = self.next_vt_id.load(Ordering::SeqCst) - 1;
let vt = self.vts.get(&id).unwrap();
if let Some(new_active) = self.vts.get(&new_active).cloned() {
{
let active_vt = self.active_vt.as_ref().unwrap();
let mut vt_inner = active_vt.inner().lock();
deactivate(vt, id);
inputd::send_comand(
&mut vt_inner.handle_file,
Cmd::Deactivate(active_vt.index),
)?;
}
// Result: display/virtio-gpu:3/acvtivate
let activate = format!("display/{vt}:{new_active}/activate");
log::info!("inputd: switching to VT #{new_active} (`{activate}`)");
let mut vt_inner = new_active.inner().lock();
let file = File::open(&activate).unwrap();
self.active_vt = Some((vt.clone(), file, new_active));
inputd::send_comand(
&mut vt_inner.handle_file,
Cmd::Activate {
vt: new_active.index,
mode: VtMode::Default,
},
)?;
self.active_vt = Some(new_active.clone());
} else {
log::warn!("inputd: switch to non-existent VT #{new_active} was requested");
}
}
}
let handle = self.handles.get_mut(&id).ok_or(SysError::new(EINVAL))?;
assert!(handle.is_producer());
let active_vt = self.active_vt.as_ref().unwrap();
for handle in self.handles.values_mut() {
match handle {
Handle::Consumer {
ref mut pending,
ref mut notified,
pending,
notified,
vt,
..
} => {
if *vt != active_vt.index {
continue;
}
pending.extend_from_slice(buf);
*notified = false;
}
@@ -236,7 +344,7 @@ impl SchemeMut for InputScheme {
}
fn close(&mut self, _id: usize) -> syscall::Result<usize> {
todo!()
Ok(0)
}
}
@@ -260,6 +368,25 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
scheme.handle(&mut packet);
socket_file.write(&packet)?;
while let Some(cmd) = scheme.todo.pop() {
let vt = scheme.vts.get_mut(&cmd.vt).unwrap();
let mut vt_inner = vt.inner().lock();
// Failing to activate a VT is not a fatal error.
if let Err(err) = inputd::send_comand(
&mut vt_inner.handle_file,
Cmd::Activate {
vt: vt.index,
mode: cmd.mode,
},
) {
log::error!("inputd: failed to activate VT #{}: {err}", vt.index)
}
vt_inner.mode = cmd.mode;
scheme.active_vt = Some(vt.clone());
}
if !should_handle {
continue;
}
@@ -269,12 +396,21 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
events,
pending,
ref mut notified,
vt,
} = handle
{
if pending.is_empty() || *notified || !events.contains(EventFlags::EVENT_READ) {
continue;
}
let active_vt = scheme.active_vt.as_ref().unwrap();
// The activate VT is not the same as the VT that the consumer is listening to
// for events.
if active_vt.index != *vt {
continue;
}
// Notify the consumer that we have some events to read. Yum yum.
let mut event_packet = Packet::default();
event_packet.a = syscall::SYS_FEVENT;
@@ -334,5 +470,75 @@ pub fn setup_logging(level: log::LevelFilter, name: &str) {
pub fn main() {
#[cfg(target_os = "redox")]
setup_logging(log::LevelFilter::Trace, "inputd");
redox_daemon::Daemon::new(daemon_runner).expect("virtio-core: failed to daemonize");
let mut args = std::env::args().skip(1);
if let Some(val) = args.next() {
match val.as_ref() {
// Sets the VT mode of the specified VT to [`VtMode::Graphic`].
"-G" => {
let vt = args.next().unwrap().parse::<usize>().unwrap();
// On startup, the VESA display driver is started which basically makes use of the framebuffer
// provided by the firmware. The GPU devices 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("inputd: failed to convert path to string");
if path_str.contains("display") {
println!("inputd: 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() {
"vesa"
} else {
// TODO: What should we do when there are multiple display devices?
device[0].split('/').nth(2).unwrap()
};
let mut handle =
inputd::Handle::new(device).expect("inputd: failed to open display handle");
handle
.activate(vt, VtMode::Graphic)
.expect("inputd: failed to activate VT in graphic mode");
}
// Activates a VT.
"-A" => {
let vt = args.next().unwrap().parse::<usize>().unwrap();
let handle = File::open(format!("input:consumer/{vt}")).expect("inputd: failed to open consumer handle");
let mut display_path = [0; 4096];
let written = syscall::fpath(handle.as_raw_fd() as usize, &mut display_path).expect("inputd: fpath() failed");
assert!(written <= display_path.len());
drop(handle);
let display_path = std::str::from_utf8(&display_path[..written]).expect("inputd: display path UTF-8 validation failed");
let display_name = display_path.split('/').skip(1).next().expect("inputd: invalid display path");
let display_scheme = display_name.split(':').next().expect("inputd: invalid display path");
let mut handle = inputd::Handle::new(display_scheme).expect("inputd: failed to open display handle");
handle.activate(vt, VtMode::Default).expect("inputd: failed to activate VT");
}
_ => panic!("inputd: invalid argument: {}", val),
}
} else {
redox_daemon::Daemon::new(daemon_runner).expect("virtio-core: failed to daemonize");
}
}
+1 -1
View File
@@ -208,7 +208,7 @@ fn main() {
let mut width = 0;
let mut height = 0;
let mut display_opt = File::open("display/vesa:input").ok();
let mut display_opt = File::open("inputd:producer").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) {
+2
View File
@@ -98,6 +98,8 @@ fn inner(daemon: redox_daemon::Daemon, framebuffers: Vec<FrameBuffer>, spec: &[b
daemon.ready().expect("failed to notify parent");
scheme.inputd_handle.activate(1, inputd::VtMode::Default).unwrap();
let mut blocked = Vec::new();
loop {
let mut packet = Packet::default();
+50 -72
View File
@@ -1,8 +1,7 @@
use std::collections::BTreeMap;
use std::{mem, ptr, slice, str};
use orbclient::{Event, EventOption};
use syscall::{Error, EventFlags, EACCES, EBADF, EINVAL, ENOENT, Map, O_NONBLOCK, Result, SchemeMut, PAGE_SIZE};
use syscall::{Error, EventFlags, EBADF, EINVAL, ENOENT, Map, O_NONBLOCK, Result, SchemeMut, PAGE_SIZE};
use crate::{
display::Display,
@@ -37,8 +36,8 @@ pub struct DisplayScheme {
pub vts: BTreeMap<VtIndex, BTreeMap<ScreenIndex, Box<dyn Screen>>>,
next_id: usize,
pub handles: BTreeMap<usize, Handle>,
super_key: bool,
inputd_handle: inputd::Handle,
pub inputd_handle: inputd::Handle,
}
impl DisplayScheme {
@@ -74,8 +73,7 @@ impl DisplayScheme {
vts,
next_id: 0,
handles: BTreeMap::new(),
super_key: false,
inputd_handle
inputd_handle,
}
}
@@ -146,6 +144,18 @@ impl DisplayScheme {
impl SchemeMut for DisplayScheme {
fn open(&mut self, path_str: &str, flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
if path_str == "handle" {
let id = self.next_id;
self.next_id += 1;
self.handles.insert(id, Handle {
kind: HandleKind::Input,
flags,
events: EventFlags::empty(),
notified_read: false
});
return Ok(id);
}
let mut parts = path_str.split('/');
let mut vt_screen = parts.next().unwrap_or("").split('.');
let vt_i = VtIndex(
@@ -156,16 +166,6 @@ impl SchemeMut for DisplayScheme {
);
if let Some(screens) = self.vts.get_mut(&vt_i) {
if let Some(screen) = screens.get_mut(&screen_i) {
for cmd in parts {
if cmd == "activate" {
self.active = vt_i;
screen.redraw(
self.onscreens[screen_i.0],
self.framebuffers[screen_i.0].stride
);
}
}
let id = self.next_id;
self.next_id += 1;
@@ -186,7 +186,7 @@ impl SchemeMut for DisplayScheme {
}
fn dup(&mut self, id: usize, buf: &[u8]) -> Result<usize> {
if ! buf.is_empty() {
if !buf.is_empty() {
return Err(Error::new(EINVAL));
}
@@ -303,70 +303,48 @@ impl SchemeMut for DisplayScheme {
let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?;
match handle.kind {
HandleKind::Input => if buf.len() == 1 && buf[0] >= 0xF4 {
let new_active = VtIndex((buf[0] - 0xF4) as usize + 1);
if let Some(screens) = self.vts.get_mut(&new_active) {
self.active = new_active;
for (screen_i, screen) in screens.iter_mut() {
screen.redraw(
self.onscreens[screen_i.0],
self.framebuffers[screen_i.0].stride
);
}
}
Ok(1)
} else {
let events = unsafe { slice::from_raw_parts(buf.as_ptr() as *const Event, buf.len()/mem::size_of::<Event>()) };
HandleKind::Input => {
use inputd::{Cmd as DisplayCommand, VtMode};
let command = inputd::parse_command(buf).unwrap();
for event in events.iter() {
let mut new_active_opt = None;
match event.to_option() {
EventOption::Key(key_event) => match key_event.scancode {
f @ 0x3B ..= 0x44 if self.super_key => { // F1 through F10
new_active_opt = Some(VtIndex((f - 0x3A) as usize));
},
0x57 if self.super_key => { // F11
new_active_opt = Some(VtIndex(11));
},
0x58 if self.super_key => { // F12
new_active_opt = Some(VtIndex(12));
},
0x5B => { // Super
self.super_key = key_event.pressed;
},
_ => ()
},
EventOption::Resize(resize_event) => {
let width = resize_event.width as usize;
let height = resize_event.height as usize;
let stride = width; //TODO: get stride somehow
self.resize(width, height, stride);
},
_ => ()
};
match command {
DisplayCommand::Activate { vt, mode } => {
let vt_i = VtIndex(vt);
if let Some(new_active) = new_active_opt {
if let Some(screens) = self.vts.get_mut(&new_active) {
self.active = new_active;
if let Some(screens) = self.vts.get_mut(&vt_i) {
for (screen_i, screen) in screens.iter_mut() {
screen.redraw(
self.onscreens[screen_i.0],
self.framebuffers[screen_i.0].stride
);
}
}
} else {
if let Some(screens) = self.vts.get_mut(&self.active) {
//TODO: send input to other screens? Don't want extra events
if let Some(screen) = screens.get_mut(&ScreenIndex(0)) {
screen.input(event);
match mode {
VtMode::Graphic => {
*screen = Box::new(GraphicScreen::new(Display::new(screen.width(), screen.height())));
}
VtMode::Default => {
screen.redraw(
self.onscreens[screen_i.0],
self.framebuffers[screen_i.0].stride
);
}
VtMode::Text => todo!()
}
}
}
self.active = vt_i;
},
DisplayCommand::Resize { width, height, stride, .. } => {
self.resize(width as usize, height as usize, stride as usize);
}
// Nothing to do for deactivate :)
DisplayCommand::Deactivate(_) => {},
}
Ok(events.len() * mem::size_of::<Event>())
Ok(buf.len())
},
HandleKind::Screen(vt_i, screen_i) => if let Some(screens) = self.vts.get_mut(&vt_i) {
if let Some(screen) = screens.get_mut(&screen_i) {
let count = screen.write(buf)?;
+1 -1
View File
@@ -27,7 +27,7 @@ pub struct GraphicScreen {
impl GraphicScreen {
pub fn new(display: Display) -> GraphicScreen {
GraphicScreen {
display: display,
display,
input: VecDeque::new(),
sync_rects: Vec::new(),
}
+1 -1
View File
@@ -6,4 +6,4 @@ pub mod utils;
mod probe;
pub use probe::{probe_device, Device, MSIX_PRIMARY_VECTOR};
pub use probe::{probe_device, reinit, Device, MSIX_PRIMARY_VECTOR};
+35 -20
View File
@@ -21,6 +21,11 @@ pub struct Device<'a> {
pub isr: &'a VolatileCell<u32>,
}
// FIXME(andypython): `device_space` should not be `Send` nor `Sync`. Take
// it out of `Device`.
unsafe impl Send for Device<'_> {}
unsafe impl Sync for Device<'_> {}
struct MsixInfo {
pub virt_table_base: NonNull<MsixTableEntry>,
pub capability: MsixCapability,
@@ -187,7 +192,12 @@ pub fn probe_device<'a>(pcid_handle: &mut PcidServerHandle) -> Result<Device<'a>
let size = offset + capability.length as usize;
let addr = common::physmap(aligned_addr, size, common::Prot::RW, common::MemoryType::Uncacheable)? as usize;
let addr = common::physmap(
aligned_addr,
size,
common::Prot::RW,
common::MemoryType::Uncacheable,
)? as usize;
addr + offset
};
@@ -237,21 +247,7 @@ pub fn probe_device<'a>(pcid_handle: &mut PcidServerHandle) -> Result<Device<'a>
let device_space = unsafe { &mut *(device_addr as *mut u8) };
let isr = unsafe { &*(isr_addr as *mut VolatileCell<u32>) };
// Reset the device.
common.device_status.set(DeviceStatusFlags::empty());
// Upon reset, the device must initialize device status to 0.
assert_eq!(common.device_status.get(), DeviceStatusFlags::empty());
log::info!("virtio: successfully reseted the device");
// XXX: According to the virtio specification v1.2, setting the ACKNOWLEDGE and DRIVER bits
// in `device_status` is required to be done in two steps.
common
.device_status
.set(common.device_status.get() | DeviceStatusFlags::ACKNOWLEDGE);
common
.device_status
.set(common.device_status.get() | DeviceStatusFlags::DRIVER);
let transport = StandardTransport::new(common, notify_addr as *const u8, notify_multiplier);
// Setup interrupts.
let all_pci_features = pcid_handle.fetch_all_features()?;
@@ -265,12 +261,31 @@ pub fn probe_device<'a>(pcid_handle: &mut PcidServerHandle) -> Result<Device<'a>
log::info!("virtio: using standard PCI transport");
let transport = StandardTransport::new(common, notify_addr as *const u8, notify_multiplier);
Ok(Device {
let device = Device {
transport,
device_space,
irq_handle,
isr,
})
};
device.transport.reset();
reinit(&device)?;
Ok(device)
}
pub fn reinit<'a>(device: &Device<'a>) -> Result<(), Error> {
// XXX: According to the virtio specification v1.2, setting the ACKNOWLEDGE and DRIVER bits
// in `device_status` is required to be done in two steps.
let mut common = device.transport.common.lock().unwrap();
let old = common.device_status.get();
common
.device_status
.set(old | DeviceStatusFlags::ACKNOWLEDGE);
let old = common.device_status.get();
common.device_status.set(old | DeviceStatusFlags::DRIVER);
Ok(())
}
+52 -6
View File
@@ -131,6 +131,7 @@ pub struct Queue<'a> {
pub descriptor: Dma<[Descriptor]>,
pub available: Available<'a>,
pub used_head: AtomicU16,
vector: u16,
notification_bell: &'a mut AtomicU16,
descriptor_stack: crossbeam_queue::SegQueue<u16>,
@@ -145,6 +146,7 @@ impl<'a> Queue<'a> {
notification_bell: &'a mut AtomicU16,
queue_index: u16,
vector: u16,
) -> Arc<Self> {
let descriptor_stack = crossbeam_queue::SegQueue::new();
(0..descriptor.len() as u16).for_each(|i| descriptor_stack.push(i));
@@ -159,9 +161,21 @@ impl<'a> Queue<'a> {
descriptor_stack,
used_head: AtomicU16::new(0),
sref: sref.clone(),
vector,
})
}
fn reinit(&self) {
self.used_head.store(0, Ordering::SeqCst);
self.available.set_head_idx(0);
// Drain all of the available descriptors.
while let Some(_) = self.descriptor_stack.pop() {}
// Refill the descriptor stack.
(0..self.descriptor.len() as u16).for_each(|i| self.descriptor_stack.push(i));
}
#[must_use = "The function returns a future that must be awaited to ensure the sent request is completed."]
pub fn send(&self, chain: Vec<Buffer>) -> PendingRequest<'a> {
let mut first_descriptor: Option<usize> = None;
@@ -230,9 +244,9 @@ impl<'a> Available<'a> {
let size = size.next_multiple_of(syscall::PAGE_SIZE); // align to page size
let addr = unsafe { syscall::physalloc(size) }.map_err(Error::SyscallError)?;
let virt = unsafe {
common::physmap(addr, size, common::Prot::RW, common::MemoryType::default())
}.map_err(Error::SyscallError)?;
let virt =
unsafe { common::physmap(addr, size, common::Prot::RW, common::MemoryType::default()) }
.map_err(Error::SyscallError)?;
let ring = unsafe { &mut *(virt as *mut AvailableRing) };
@@ -298,7 +312,8 @@ impl<'a> Used<'a> {
let addr = unsafe { syscall::physalloc(size) }.map_err(Error::SyscallError)?;
let virt =
unsafe { common::physmap(addr, size, common::Prot::RW, common::MemoryType::default()) }.map_err(Error::SyscallError)?;
unsafe { common::physmap(addr, size, common::Prot::RW, common::MemoryType::default()) }
.map_err(Error::SyscallError)?;
let ring = unsafe { &mut *(virt as *mut UsedRing) };
@@ -363,7 +378,7 @@ impl Drop for Used<'_> {
}
pub struct StandardTransport<'a> {
common: Mutex<&'a mut CommonCfg>,
pub(crate) common: Mutex<&'a mut CommonCfg>,
notify: *const u8,
notify_mul: u32,
@@ -383,7 +398,12 @@ impl<'a> StandardTransport<'a> {
pub fn reset(&self) {
let mut common = self.common.lock().unwrap();
common.device_status.set(DeviceStatusFlags::empty());
// Upon reset, the device must initialize device status to 0.
assert_eq!(common.device_status.get(), DeviceStatusFlags::empty());
log::debug!("virtio: successfully reseted device");
}
pub fn check_device_feature(&self, feature: u32) -> bool {
@@ -474,7 +494,14 @@ impl<'a> StandardTransport<'a> {
log::info!("virtio-core: enabled queue #{queue_index} (size={queue_size})");
let queue = Queue::new(descriptor, avail, used, notification_bell, queue_index);
let queue = Queue::new(
descriptor,
avail,
used,
notification_bell,
queue_index,
vector,
);
let queue_copy = queue.clone();
let irq_fd = irq_handle.as_raw_fd();
@@ -499,4 +526,23 @@ impl<'a> StandardTransport<'a> {
Ok(queue)
}
/// Re-initializes a queue; usually done after a device reset.
pub fn reinit_queue(&self, queue: Arc<Queue>) {
let mut common = self.common.lock().unwrap();
queue.reinit();
common.queue_select.set(queue.queue_index);
common.queue_desc.set(queue.descriptor.physical() as u64);
common.queue_driver.set(queue.available.phys_addr() as u64);
common.queue_device.set(queue.used.phys_addr() as u64);
// Set the MSI-X vector.
common.queue_msix_vector.set(queue.vector);
assert!(common.queue_msix_vector.get() == queue.vector);
// Enable the queue.
common.queue_enable.set(1);
}
}
+21 -5
View File
@@ -25,10 +25,12 @@
use std::cell::UnsafeCell;
use std::fs::File;
use std::io::{Read, Write};
use std::sync::Arc;
use pcid_interface::PcidServerHandle;
use syscall::{Packet, SchemeMut};
use virtio_core::transport::{self, Queue};
use virtio_core::utils::VolatileCell;
use virtio_core::MSIX_PRIMARY_VECTOR;
@@ -356,10 +358,7 @@ pub struct SetScanout {
impl SetScanout {
pub fn new(scanout_id: u32, resource_id: u32, rect: GpuRect) -> Self {
Self {
header: ControlHeader {
ty: VolatileCell::new(CommandTy::SetScanout),
..Default::default()
},
header: ControlHeader::with_ty(CommandTy::SetScanout),
rect,
scanout_id,
@@ -394,6 +393,21 @@ impl XferToHost2d {
}
}
static DEVICE: spin::Once<virtio_core::Device> = spin::Once::new();
fn reinit(control_queue: Arc<Queue>, cursor_queue: Arc<Queue>) -> Result<(), transport::Error> {
let device = DEVICE.get().unwrap();
virtio_core::reinit(device)?;
device.transport.finalize_features();
device.transport.reinit_queue(control_queue.clone());
device.transport.reinit_queue(cursor_queue.clone());
device.transport.run_device();
Ok(())
}
fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
let mut pcid_handle = PcidServerHandle::connect_default()?;
@@ -405,7 +419,7 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
assert_eq!(pci_config.func.devid, 0x1050);
log::info!("virtio-gpu: initiating startup sequence :^)");
let device = virtio_core::probe_device(&mut pcid_handle)?;
let device = DEVICE.try_call_once(|| virtio_core::probe_device(&mut pcid_handle))?;
let config = unsafe { &mut *(device.device_space as *mut GpuConfig) };
// Negotiate features.
@@ -432,6 +446,8 @@ fn deamon(deamon: redox_daemon::Daemon) -> anyhow::Result<()> {
device.transport.clone(),
))?;
// scheme.inputd_handle.activate(scheme.main_vt)?;
loop {
let mut packet = Packet::default();
socket_file
+171 -74
View File
@@ -1,11 +1,12 @@
use std::collections::BTreeMap;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
use std::sync::Arc;
use common::dma::Dma;
use inputd::Damage;
use syscall::{Error as SysError, SchemeMut, EINVAL};
use common::dma::Dma;
use syscall::{Error as SysError, SchemeMut, EAGAIN, EINVAL};
use virtio_core::spec::{Buffer, ChainBuilder, DescriptorFlags};
use virtio_core::transport::{Error, Queue, StandardTransport};
@@ -40,6 +41,8 @@ pub struct Display<'a> {
resource_id: u32,
id: usize,
is_reseted: AtomicBool,
}
impl<'a> Display<'a> {
@@ -61,9 +64,27 @@ impl<'a> Display<'a> {
id,
resource_id: RESOURCE_ALLOC.fetch_add(1, Ordering::SeqCst),
is_reseted: AtomicBool::new(false),
}
}
async fn init(&self) -> Result<(), Error> {
if !self.is_reseted.load(Ordering::SeqCst) {
// The device is already initialized.
return Ok(());
}
self.is_reseted.store(false, Ordering::SeqCst);
log::info!("virtio-gpu: initializing GPU after a reset");
crate::reinit(self.control_queue.clone(), self.cursor_queue.clone())?;
self.remap_screen().await?;
Ok(())
}
async fn get_fpath(&self, buffer: &mut [u8]) -> Result<usize, Error> {
let path = format!("display/virtio-gpu:3.0/{}/{}", self.width, self.height);
@@ -90,29 +111,26 @@ impl<'a> Display<'a> {
Ok(())
}
async fn remap_screen(&self) -> Result<usize, Error> {
let bpp = 32;
let fb_size = (self.width as usize * self.height as usize * bpp / 8)
.next_multiple_of(syscall::PAGE_SIZE);
let mapped = *self.mapped.get().unwrap();
let address = unsafe { syscall::virttophys(mapped) }?;
self.map_screen_with(0, address, fb_size, mapped).await
}
async fn map_screen(&self, offset: usize) -> Result<usize, Error> {
if let Some(mapped) = self.mapped.get() {
return Ok(mapped + offset);
}
// Create a host resource using `VIRTIO_GPU_CMD_RESOURCE_CREATE_2D`.
let mut request = Dma::new(ResourceCreate2d::default())?;
request.set_width(self.width);
request.set_height(self.height);
request.set_format(ResourceFormat::Bgrx);
request.set_resource_id(self.resource_id);
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 = (self.width as usize * self.height as usize * bpp / 8)
.next_multiple_of(syscall::PAGE_SIZE);
let address = unsafe { syscall::physalloc(fb_size) }? as u64;
let address = unsafe { syscall::physalloc(fb_size) }?;
let mapped = unsafe {
common::physmap(
address as usize,
@@ -126,9 +144,34 @@ impl<'a> Display<'a> {
core::ptr::write_bytes(mapped as *mut u8, 255, fb_size);
}
self.map_screen_with(offset, address, fb_size, mapped).await
}
async fn map_screen_with(
&self,
offset: usize,
address: usize,
size: usize,
mapped: usize,
) -> Result<usize, Error> {
// Create a host resource using `VIRTIO_GPU_CMD_RESOURCE_CREATE_2D`.
let mut request = Dma::new(ResourceCreate2d::default())?;
request.set_width(self.width);
request.set_height(self.height);
request.set_format(ResourceFormat::Bgrx);
request.set_resource_id(self.resource_id);
self.send_request(request).await?;
// Use the allocated framebuffer from tthe guest ram, and attach it as backing
// storage to the resource just created, using `VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING`.
//
// TODO(andypython): Scatter lists are supported, so the framebuffer doesnt need to be
// contignous in guest physical memory.
let entry = Dma::new(MemEntry {
address,
length: fb_size as u32,
address: address as u64,
length: size as u32,
padding: 0,
})?;
@@ -200,13 +243,21 @@ impl<'a> Display<'a> {
// Go back to legacy mode.
self.transport.reset();
self.is_reseted.store(true, Ordering::SeqCst);
Ok(())
}
}
enum Handle<'a> {
Vt { display: Arc<Display<'a>>, vt: usize },
Input,
}
pub struct Scheme<'a> {
handles: BTreeMap<usize /* file descriptor */, Arc<Display<'a>>>,
inputd_handle: inputd::Handle,
handles: BTreeMap<usize /* file descriptor */, Handle<'a>>,
/// Counter used for file descriptor allocation.
next_id: AtomicUsize,
displays: Vec<Arc<Display<'a>>>,
}
@@ -227,7 +278,7 @@ impl<'a> Scheme<'a> {
Ok(Self {
handles: BTreeMap::new(),
inputd_handle: inputd::Handle::new("virtio-gpu").unwrap(),
next_id: AtomicUsize::new(0),
displays,
})
}
@@ -284,56 +335,37 @@ impl<'a> Scheme<'a> {
impl<'a> SchemeMut for Scheme<'a> {
fn open(&mut self, path: &str, _flags: usize, _uid: u32, _gid: u32) -> syscall::Result<usize> {
dbg!(&path);
if path == "handle" {
let fd = self.next_id.fetch_add(1, Ordering::SeqCst);
self.handles.insert(fd, Handle::Input);
return Ok(fd);
}
let mut parts = path.split('/');
let mut screen = parts.next().unwrap_or("").split('.');
static YES: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(true);
if YES.load(Ordering::SeqCst) {
YES.store(false, Ordering::SeqCst);
}
let fd = screen.next().unwrap_or("").parse::<usize>().unwrap();
if path.contains("deactivate") {
log::info!("virtio-gpu: deactivating display at file #{}", fd);
let handle = self.handles.get(&fd).unwrap();
futures::executor::block_on(handle.detach()).unwrap();
return Ok(0);
}
let vt = screen.next().unwrap_or("").parse::<usize>().unwrap();
let id = screen.next().unwrap_or("").parse::<usize>().unwrap_or(0);
dbg!(&id);
dbg!(vt, id);
let display = self.displays.get(id).ok_or(SysError::new(EINVAL))?;
let fd = self.inputd_handle.register().unwrap();
self.handles.insert(fd, display.clone());
let fd = self.next_id.fetch_add(1, Ordering::SeqCst);
self.handles.insert(fd, Handle::Vt {display: display.clone(), vt });
Ok(fd)
}
fn dup(&mut self, _old_id: usize, _buf: &[u8]) -> syscall::Result<usize> {
todo!()
}
fn fevent(
&mut self,
_id: usize,
_flags: syscall::EventFlags,
) -> syscall::Result<syscall::EventFlags> {
log::warn!("fevent is a stub!");
Ok(syscall::EventFlags::empty())
}
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 = futures::executor::block_on(handle.get_fpath(buf)).unwrap();
match self.handles.get(&id).unwrap() {
Handle::Vt { display, .. } => {
let bytes_copied = futures::executor::block_on(display.get_fpath(buf)).unwrap();
Ok(bytes_copied)
}
Ok(bytes_copied)
Handle::Input => unreachable!(),
}
}
fn fmap_old(&mut self, id: usize, map: &syscall::OldMap) -> syscall::Result<usize> {
@@ -349,14 +381,23 @@ impl<'a> SchemeMut for Scheme<'a> {
}
fn fmap(&mut self, id: usize, map: &syscall::Map) -> syscall::Result<usize> {
let handle = self.handles.get_mut(&id).ok_or(SysError::new(EINVAL))?;
Ok(futures::executor::block_on(handle.map_screen(map.offset)).unwrap())
match self.handles.get(&id).ok_or(SysError::new(EINVAL))? {
Handle::Vt { display, .. } => {
Ok(futures::executor::block_on(display.map_screen(map.offset)).unwrap())
}
_ => unreachable!(),
}
}
fn fsync(&mut self, id: usize) -> syscall::Result<usize> {
let handle = self.handles.get_mut(&id).ok_or(SysError::new(EINVAL))?;
futures::executor::block_on(handle.flush(None)).unwrap();
Ok(0)
match self.handles.get(&id).ok_or(SysError::new(EINVAL))? {
Handle::Vt { display, .. } => {
futures::executor::block_on(display.flush(None)).unwrap();
Ok(0)
}
_ => unreachable!(),
}
}
fn read(&mut self, _id: usize, _buf: &mut [u8]) -> syscall::Result<usize> {
@@ -366,18 +407,74 @@ impl<'a> SchemeMut for Scheme<'a> {
}
fn write(&mut self, id: usize, buf: &[u8]) -> syscall::Result<usize> {
let handle = self.handles.get_mut(&id).ok_or(SysError::new(EINVAL))?;
let damages = unsafe {
core::slice::from_raw_parts(
buf.as_ptr() as *const Damage,
buf.len() / core::mem::size_of::<Damage>(),
)
};
match self.handles.get(&id).ok_or(SysError::new(EINVAL))? {
Handle::Vt { display, .. } => {
// The VT is not active and the device is reseted. Ask them to try
// again later.
if display.is_reseted.load(Ordering::SeqCst) {
return Err(SysError::new(EAGAIN));
}
for damage in damages {
futures::executor::block_on(handle.flush(Some(damage))).unwrap();
let damages = unsafe {
core::slice::from_raw_parts(
buf.as_ptr() as *const Damage,
buf.len() / core::mem::size_of::<Damage>(),
)
};
for damage in damages {
futures::executor::block_on(display.flush(Some(damage))).unwrap();
}
Ok(buf.len())
}
Handle::Input => {
use inputd::{Cmd as DisplayCommand, VtMode};
let command = inputd::parse_command(buf).unwrap();
match command {
DisplayCommand::Activate { mode, vt } => {
assert!(mode == VtMode::Graphic || mode == VtMode::Default);
let target_vt = vt;
for handle in self.handles.values() {
if let Handle::Vt { display , vt } = handle {
if *vt != target_vt {
continue;
}
futures::executor::block_on(display.init()).unwrap();
}
}
}
DisplayCommand::Deactivate(target_vt) => {
for handle in self.handles.values() {
if let Handle::Vt { display , vt } = handle {
if *vt != target_vt {
continue;
}
futures::executor::block_on(display.detach()).unwrap();
break;
}
}
// for display in self.displays.iter() {
// futures::executor::block_on(display.detach()).unwrap();
// }
}
DisplayCommand::Resize { .. } => {
log::warn!("virtio-gpu: resize is not implemented yet")
}
}
Ok(buf.len())
}
}
Ok(buf.len())
}
fn seek(&mut self, _id: usize, _pos: isize, _whence: usize) -> syscall::Result<isize> {
+2
View File
@@ -5,6 +5,8 @@ use std::sync::Arc;
use syscall::Error as SysError;
use syscall::*;
use common::dma::Dma;
use virtio_core::spec::{Buffer, ChainBuilder, DescriptorFlags};
use virtio_core::transport::Queue;