From e457f0393502bf9edc880112e29a76abe0873df0 Mon Sep 17 00:00:00 2001 From: Anhad Singh Date: Tue, 18 Jul 2023 18:25:32 +1000 Subject: [PATCH] virtio-gpu: handling GPU resets Signed-off-by: Anhad Singh --- Cargo.lock | 1 + inputd/src/lib.rs | 35 +++++- inputd/src/main.rs | 79 ++++++++---- vesad/src/scheme.rs | 93 +++++--------- virtio-core/src/lib.rs | 2 +- virtio-core/src/probe.rs | 50 +++++--- virtio-core/src/transport.rs | 42 ++++++- virtio-gpud/src/main.rs | 24 +++- virtio-gpud/src/scheme.rs | 235 ++++++++++++++++++++++++----------- virtio-netd/Cargo.toml | 1 + virtio-netd/src/scheme.rs | 2 + 11 files changed, 371 insertions(+), 193 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e17d62f53..a0d8949010 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1908,6 +1908,7 @@ dependencies = [ name = "virtio-netd" version = "0.1.0" dependencies = [ + "common", "futures", "log", "netutils", diff --git a/inputd/src/lib.rs b/inputd/src/lib.rs index 9ad4ae5876..bd9cb6655d 100644 --- a/inputd/src/lib.rs +++ b/inputd/src/lib.rs @@ -1,11 +1,12 @@ use std::fs::File; use std::io::{Error, Read}; +use std::mem; pub struct Handle(File); impl Handle { pub fn new>(device_name: S) -> Result { - let path = format!("input:handle/{}", device_name.into()); + let path = format!("input:handle/display/{}", device_name.into()); Ok(Self(File::open(path)?)) } @@ -16,6 +17,38 @@ impl Handle { } } +#[derive(Debug, Copy, Clone, PartialEq)] +#[repr(u8)] +pub enum CommandTy { + Unknown = 0, + + Activate, + Deactivate, +} + +#[repr(C)] +pub struct Command { + pub ty: CommandTy, + pub value: usize, +} + +impl Command { + pub fn new(ty: CommandTy, value: usize) -> Self { + Self { ty, value } + } + + /// ## Panics + /// This function panics if the buffer is not `sizeof::()` bytes wide. + pub fn parse<'a>(buffer: &'a [u8]) -> &'a Command { + assert_eq!(buffer.len(), core::mem::size_of::()); + unsafe { &*(buffer.as_ptr() as *const Command) } + } + + pub fn into_bytes(self) -> [u8; mem::size_of::()] { + unsafe { core::mem::transmute(self) } + } +} + #[repr(packed)] pub struct Damage { pub x: i32, diff --git a/inputd/src/main.rs b/inputd/src/main.rs index b06a0c247d..0609cb1491 100644 --- a/inputd/src/main.rs +++ b/inputd/src/main.rs @@ -13,10 +13,11 @@ use std::collections::BTreeMap; use std::fs::File; -use std::io::{Read, Write}; +use std::io::{self, Read, Write}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; +use inputd::{Command, CommandTy}; use orbclient::{Event, EventOption}; use syscall::{Error as SysError, EventFlags, Packet, SchemeMut, EINVAL}; @@ -26,6 +27,7 @@ enum Handle { events: EventFlags, pending: Vec, notified: bool, + vt: usize, }, Device(Arc), } @@ -66,17 +68,30 @@ 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 vt = if let Some((_, _, vt)) = self.active_vt { + vt + } else { + // No one is currently set as active, so we assume that the last allocated + // one will be willing to take this consumer. + self.next_vt_id.load(Ordering::SeqCst) - 1 + }; + + dbg!(vt); + Handle::Consumer { + events: EventFlags::empty(), + pending: Vec::new(), + notified: false, + vt, + } + } "handle" => { - let value = path_parts.next().ok_or(SysError::new(EINVAL))?; - Handle::Device(Arc::new(value.to_string())) + let value = path_parts.collect::>().join("/"); + Handle::Device(Arc::new(value)) } _ => unreachable!("inputd: invalid path {path}"), @@ -84,9 +99,7 @@ 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) } @@ -156,36 +169,34 @@ impl SchemeMut for InputScheme { _ => 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); + let deactivate = |file: &mut File, id: usize| -> io::Result<()> { + let command = Command::new(CommandTy::Deactivate, id).into_bytes(); + file.write(&command)?; + Ok(()) }; 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 let Some(vt) = self.vts.get(&new_active).cloned() { + if let Some((_, file, current)) = self.active_vt.as_mut() { + // If the VT is already active, don't do anything. if new_active == *current { continue; } - deactivate(vt, *current); + deactivate(file, *current).unwrap(); } else { let id = self.next_vt_id.load(Ordering::SeqCst) - 1; - let vt = self.vts.get(&id).unwrap(); + let mut vt = File::open(self.vts.get_mut(&id).unwrap().as_ref()).unwrap(); - deactivate(vt, id); + deactivate(&mut vt, id).unwrap(); } - // Result: display/virtio-gpu:3/acvtivate - let activate = format!("display/{vt}:{new_active}/activate"); - log::info!("inputd: switching to VT #{new_active} (`{activate}`)"); + log::info!("inputd: switching to VT #{new_active} (`{vt}`)"); + + let mut file = File::open(vt.as_ref()).unwrap(); + file.write(&Command::new(CommandTy::Activate, new_active).into_bytes()) + .unwrap(); - let file = File::open(&activate).unwrap(); self.active_vt = Some((vt.clone(), file, new_active)); } else { log::warn!("inputd: switch to non-existent VT #{new_active} was requested"); @@ -269,12 +280,26 @@ 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 should_notify = if let Some((_, _, active_vt)) = scheme.active_vt { + active_vt == *vt + } else { + let last_allocated = scheme.next_vt_id.load(Ordering::SeqCst) - 1; + last_allocated == *vt + }; + + // The activate VT is not the same as the VT that the consumer is listening to + // for events. + if !should_notify { + 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; diff --git a/vesad/src/scheme.rs b/vesad/src/scheme.rs index c6a17b9509..d334c79a89 100644 --- a/vesad/src/scheme.rs +++ b/vesad/src/scheme.rs @@ -43,7 +43,7 @@ pub struct DisplayScheme { impl DisplayScheme { pub fn new(mut framebuffers: Vec, spec: &[bool]) -> DisplayScheme { - let mut inputd_handle = inputd::Handle::new("vesa").unwrap(); + let mut inputd_handle = inputd::Handle::new("vesa:handle").unwrap(); let mut onscreens = Vec::new(); for fb in framebuffers.iter_mut() { @@ -146,6 +146,18 @@ impl DisplayScheme { impl SchemeMut for DisplayScheme { fn open(&mut self, path_str: &str, flags: usize, _uid: u32, _gid: u32) -> Result { + 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 +168,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; @@ -303,51 +305,14 @@ 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::()) }; + HandleKind::Input => { + let command = inputd::Command::parse(buf); - 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.ty { + inputd::CommandTy::Activate => { + let vt_i = VtIndex(command.value); - 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], @@ -355,18 +320,20 @@ impl SchemeMut for DisplayScheme { ); } } - } 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); - } - } - } + + self.active = vt_i; + }, + + inputd::CommandTy::Deactivate => { + // Nothing :) + }, + + _ => unreachable!() } - Ok(events.len() * mem::size_of::()) + 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)?; diff --git a/virtio-core/src/lib.rs b/virtio-core/src/lib.rs index 5d1d6d0436..54554679a1 100644 --- a/virtio-core/src/lib.rs +++ b/virtio-core/src/lib.rs @@ -6,4 +6,4 @@ pub mod utils; mod probe; -pub use probe::{probe_device, Device, MSIX_PRIMARY_VECTOR}; +pub use probe::{probe_device, Device, MSIX_PRIMARY_VECTOR, reinit}; diff --git a/virtio-core/src/probe.rs b/virtio-core/src/probe.rs index c9fad8427e..cdb029a60c 100644 --- a/virtio-core/src/probe.rs +++ b/virtio-core/src/probe.rs @@ -11,7 +11,7 @@ use pcid_interface::*; use syscall::Io; use crate::spec::*; -use crate::transport::{Error, StandardTransport}; +use crate::transport::{Error, StandardTransport, Queue}; use crate::utils::{align_down, VolatileCell}; pub struct Device<'a> { @@ -21,6 +21,9 @@ pub struct Device<'a> { pub isr: &'a VolatileCell, } +unsafe impl Send for Device<'_> {} +unsafe impl Sync for Device<'_> {} + struct MsixInfo { pub virt_table_base: NonNull, pub capability: MsixCapability, @@ -237,21 +240,7 @@ pub fn probe_device<'a>(pcid_handle: &mut PcidServerHandle) -> Result let device_space = unsafe { &mut *(device_addr as *mut u8) }; let isr = unsafe { &*(isr_addr as *mut VolatileCell) }; - // 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 +254,33 @@ pub fn probe_device<'a>(pcid_handle: &mut PcidServerHandle) -> Result 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(()) } diff --git a/virtio-core/src/transport.rs b/virtio-core/src/transport.rs index 6048d14f34..7118adbf9d 100644 --- a/virtio-core/src/transport.rs +++ b/virtio-core/src/transport.rs @@ -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, @@ -145,6 +146,7 @@ impl<'a> Queue<'a> { notification_bell: &'a mut AtomicU16, queue_index: u16, + vector: u16, ) -> Arc { 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) -> PendingRequest<'a> { let mut first_descriptor: Option = None; @@ -363,7 +377,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 +397,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 +493,7 @@ 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 +518,23 @@ impl<'a> StandardTransport<'a> { Ok(queue) } + + /// Re-initializes a queue; usually done after a device reset. + pub fn reinit_queue(&self, queue: Arc) { + 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); + } } diff --git a/virtio-gpud/src/main.rs b/virtio-gpud/src/main.rs index bbe5675688..f22ed521da 100644 --- a/virtio-gpud/src/main.rs +++ b/virtio-gpud/src/main.rs @@ -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::{Queue, self}; 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 = spin::Once::new(); + +fn reinit(control_queue: Arc, cursor_queue: Arc) -> 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. diff --git a/virtio-gpud/src/scheme.rs b/virtio-gpud/src/scheme.rs index 4439f647be..a07b54a8a5 100644 --- a/virtio-gpud/src/scheme.rs +++ b/virtio-gpud/src/scheme.rs @@ -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, EINVAL, EAGAIN}; 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 { 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 { + 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 { 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,28 @@ 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 { + // 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,12 +237,22 @@ impl<'a> Display<'a> { // Go back to legacy mode. self.transport.reset(); + self.is_reseted.store(true, Ordering::SeqCst); + Ok(()) } } +enum Handle { + Vt(usize /* VT index */), + Input, +} + pub struct Scheme<'a> { - handles: BTreeMap>>, + vts: BTreeMap>>, + handles: BTreeMap, + /// Counter used for file descriptor allocation. + next_id: AtomicUsize, inputd_handle: inputd::Handle, displays: Vec>>, } @@ -225,9 +272,16 @@ impl<'a> Scheme<'a> { ) .await?; + let mut inputd_handle = inputd::Handle::new("virtio-gpu:handle").unwrap(); + + let mut vts = BTreeMap::new(); + vts.insert(inputd_handle.register().unwrap(), displays[0].clone()); + Ok(Self { + vts, handles: BTreeMap::new(), - inputd_handle: inputd::Handle::new("virtio-gpu").unwrap(), + next_id: AtomicUsize::new(0), + inputd_handle, displays, }) } @@ -284,56 +338,42 @@ impl<'a> Scheme<'a> { impl<'a> SchemeMut for Scheme<'a> { fn open(&mut self, path: &str, _flags: usize, _uid: u32, _gid: u32) -> syscall::Result { - 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::().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::().unwrap(); let id = screen.next().unwrap_or("").parse::().unwrap_or(0); - dbg!(&id); + dbg!(vt, id); - let display = self.displays.get(id).ok_or(SysError::new(EINVAL))?; + if self.displays.get(id).is_none() { + return Err(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); + // FIXME: The +1 is a hack. vesad smh + self.handles.insert(fd, Handle::Vt(vt + 1)); Ok(fd) } - fn dup(&mut self, _old_id: usize, _buf: &[u8]) -> syscall::Result { - todo!() - } - - fn fevent( - &mut self, - _id: usize, - _flags: syscall::EventFlags, - ) -> syscall::Result { - log::warn!("fevent is a stub!"); - Ok(syscall::EventFlags::empty()) - } - fn fpath(&mut self, id: usize, buf: &mut [u8]) -> syscall::Result { - 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).ok_or(SysError::new(EINVAL))? { + Handle::Vt(id) => { + let handle = self.vts.get_mut(&id).ok_or(SysError::new(EINVAL))?; + let bytes_copied = futures::executor::block_on(handle.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 { @@ -349,14 +389,25 @@ impl<'a> SchemeMut for Scheme<'a> { } fn fmap(&mut self, id: usize, map: &syscall::Map) -> syscall::Result { - 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(id) => { + let handle = self.vts.get_mut(&id).ok_or(SysError::new(EINVAL))?; + Ok(futures::executor::block_on(handle.map_screen(map.offset)).unwrap()) + } + _ => unreachable!(), + } } fn fsync(&mut self, id: usize) -> syscall::Result { - 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(id) => { + let handle = self.vts.get_mut(&id).ok_or(SysError::new(EINVAL))?; + futures::executor::block_on(handle.flush(None)).unwrap(); + Ok(0) + } + + _ => unreachable!(), + } } fn read(&mut self, _id: usize, _buf: &mut [u8]) -> syscall::Result { @@ -366,18 +417,54 @@ impl<'a> SchemeMut for Scheme<'a> { } fn write(&mut self, id: usize, buf: &[u8]) -> syscall::Result { - 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::(), - ) - }; + match self.handles.get(&id).ok_or(SysError::new(EINVAL))? { + Handle::Vt(id) => { + let handle = self.vts.get_mut(&id).ok_or(SysError::new(EINVAL))?; - for damage in damages { - futures::executor::block_on(handle.flush(Some(damage))).unwrap(); + // The VT is not active and the device is reseted. Ask them to try + // again later. + if handle.is_reseted.load(Ordering::SeqCst) { + return Err(SysError::new(EAGAIN)); + } + + let damages = unsafe { + core::slice::from_raw_parts( + buf.as_ptr() as *const Damage, + buf.len() / core::mem::size_of::(), + ) + }; + + for damage in damages { + futures::executor::block_on(handle.flush(Some(damage))).unwrap(); + } + + Ok(buf.len()) + } + + Handle::Input => { + let command = inputd::Command::parse(buf); + + match command.ty { + inputd::CommandTy::Activate => { + let vt = command.value; + + let display = self.vts.get(&vt).unwrap(); + futures::executor::block_on(display.init()).unwrap(); + }, + + inputd::CommandTy::Deactivate => { + let vt = command.value; + + let display = self.vts.get(&vt).ok_or(SysError::new(EINVAL))?; + futures::executor::block_on(display.detach()).unwrap(); + } + + _ => unreachable!(), + } + + Ok(buf.len()) + } } - Ok(buf.len()) } fn seek(&mut self, _id: usize, _pos: isize, _whence: usize) -> syscall::Result { diff --git a/virtio-netd/Cargo.toml b/virtio-netd/Cargo.toml index f4a116f10c..3458d7b046 100644 --- a/virtio-netd/Cargo.toml +++ b/virtio-netd/Cargo.toml @@ -10,6 +10,7 @@ futures = { version = "0.3.28", features = ["executor"] } virtio-core = { path = "../virtio-core" } pcid = { path = "../pcid" } +common = { path = "../common" } redox-daemon = "0.1" redox_syscall = "0.3" diff --git a/virtio-netd/src/scheme.rs b/virtio-netd/src/scheme.rs index 251a501ce0..829a3002e6 100644 --- a/virtio-netd/src/scheme.rs +++ b/virtio-netd/src/scheme.rs @@ -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;