Merge branch 'core' into 'master'
virtio-gpu: handling GPU resets See merge request redox-os/drivers!101
This commit is contained in:
Generated
+1
@@ -640,6 +640,7 @@ dependencies = [
|
||||
"redox-daemon",
|
||||
"redox-log",
|
||||
"redox_syscall 0.3.5",
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
+2
-2
@@ -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();
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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)?;
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
|
||||
@@ -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
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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 doesn’t 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 doesn’t 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> {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user