#![feature(macro_metavar_expr)] use std::cmp; use std::collections::HashMap; use std::fmt::Debug; use std::fs::File; use std::io::{self, Write}; use std::os::fd::BorrowedFd; use std::sync::{Arc, Mutex}; use inputd::{DisplayHandle, VtEventKind}; use libredox::Fd; use redox_scheme::scheme::{register_scheme_inner, SchemeState, SchemeSync}; use redox_scheme::{CallerCtx, OpenResult, RequestKind, SignalBehavior, Socket}; use scheme_utils::{FpathWriter, HandleMap}; use syscall::schemev2::NewFdFlags; use syscall::{Error, MapFlags, Result, EACCES, EAGAIN, EINVAL, ENOENT, EOPNOTSUPP}; use crate::kms::connector::{KmsConnectorDriver, KmsConnectorState}; use crate::kms::objects::{ KmsCrtc, KmsCrtcDriver, KmsCrtcState, KmsObjectId, KmsObjects, KmsPlane, KmsPlaneDriver, KmsPlaneState, }; mod ioctl; pub mod kms; #[derive(Debug, Copy, Clone)] #[repr(C, packed)] pub struct Damage { pub x: u32, pub y: u32, pub width: u32, pub height: u32, } impl Damage { fn merge(self, other: Self) -> Self { if self.width == 0 || self.height == 0 { return other; } if other.width == 0 || other.height == 0 { return self; } let x = cmp::min(self.x, other.x); let y = cmp::min(self.y, other.y); let x2 = cmp::max(self.x + self.width, other.x + other.width); let y2 = cmp::max(self.y + self.height, other.y + other.height); Damage { x, y, width: x2 - x, height: y2 - y, } } #[must_use] pub fn clip(mut self, width: u32, height: u32) -> Self { // Clip damage let x2 = self.x + self.width; self.x = cmp::min(self.x, width); if x2 > width { self.width = width - self.x; } let y2 = self.y + self.height; self.y = cmp::min(self.y, height); if y2 > height { self.height = height - self.y; } self } } pub trait GraphicsAdapter: Sized + Debug { type Connector: KmsConnectorDriver; type Crtc: KmsCrtcDriver; type Plane: KmsPlaneDriver; type Buffer: Buffer; type Framebuffer: Framebuffer; fn name(&self) -> &'static [u8]; fn desc(&self) -> &'static [u8]; fn init(&mut self, objects: &mut KmsObjects); fn get_unique(&self) -> String; fn get_cap(&self, cap: u32) -> Result; fn set_client_cap(&self, cap: u32, value: u64) -> Result<()>; fn probe_connector(&mut self, objects: &mut KmsObjects, id: KmsObjectId); fn create_dumb_buffer(&mut self, width: u32, height: u32) -> (Self::Buffer, u32); fn map_dumb_buffer(&mut self, buffer: &Self::Buffer) -> *mut u8; fn create_framebuffer(&mut self, buffer: &Self::Buffer) -> Self::Framebuffer; fn set_crtc( &mut self, objects: &KmsObjects, crtc: &Mutex>, new_state: KmsCrtcState, ) -> syscall::Result<()>; fn set_plane( &mut self, objects: &KmsObjects, plane: &Mutex>, new_plane_state: KmsPlaneState, damage: Damage, ) -> syscall::Result<()>; // FIXME replace this with an actual plane marked as cursor plane once we // support planes internally. fn has_cursor_plane(&self) -> bool; fn handle_cursor(&mut self, cursor: &CursorPlane, dirty_fb: bool); } pub trait Buffer: Debug { fn size(&self) -> usize; } pub trait Framebuffer: Debug {} impl Framebuffer for () {} pub struct CursorPlane { pub x: i32, pub y: i32, pub hot_x: i32, pub hot_y: i32, pub buffer: Option>, } pub struct GraphicsScheme { inner: GraphicsSchemeInner, inputd_handle: DisplayHandle, state: SchemeState, } impl GraphicsScheme { pub fn new(mut adapter: T, scheme_name: String, early: bool) -> Self { assert!(scheme_name.starts_with("display")); let socket = Socket::nonblock().expect("failed to create graphics scheme"); let disable_graphical_debug = Some( File::open("/scheme/debug/disable-graphical-debug") .expect("vesad: Failed to open /scheme/debug/disable-graphical-debug"), ); let mut objects = KmsObjects::new(); adapter.init(&mut objects); for connector_id in objects.connector_ids().to_vec() { adapter.probe_connector(&mut objects, connector_id) } let mut inner = GraphicsSchemeInner { adapter, scheme_name, disable_graphical_debug, socket, objects, handles: HandleMap::new(), active_vt: 0, vts: HashMap::new(), }; let cap_id = inner.scheme_root().expect("failed to get this scheme root"); register_scheme_inner(&inner.socket, &inner.scheme_name, cap_id) .expect("failed to register graphics scheme root"); let display_handle = if early { DisplayHandle::new_early(&inner.scheme_name).unwrap() } else { DisplayHandle::new(&inner.scheme_name).unwrap() }; Self { inner, inputd_handle: display_handle, state: SchemeState::new(), } } pub fn event_handle(&self) -> &Fd { self.inner.socket.inner() } pub fn inputd_event_handle(&self) -> BorrowedFd<'_> { self.inputd_handle.inner() } pub fn adapter(&self) -> &T { &self.inner.adapter } pub fn adapter_mut(&mut self) -> &mut T { &mut self.inner.adapter } pub fn kms_objects(&self) -> &KmsObjects { &self.inner.objects } pub fn kms_objects_mut(&mut self) -> &mut KmsObjects { &mut self.inner.objects } pub fn adapter_and_kms_objects_mut(&mut self) -> (&mut T, &mut KmsObjects) { (&mut self.inner.adapter, &mut self.inner.objects) } pub fn handle_vt_events(&mut self) { while let Some(vt_event) = self .inputd_handle .read_vt_event() .expect("driver-graphics: failed to read display handle") { match vt_event.kind { VtEventKind::Activate => self.inner.activate_vt(vt_event.vt), } } } pub fn notify_displays_changed(&mut self) { // FIXME notify clients } /// Process new scheme requests. /// /// This needs to be called each time there is a new event on the scheme /// file. pub fn tick(&mut self) -> io::Result<()> { loop { let request = match self.inner.socket.next_request(SignalBehavior::Restart) { Ok(Some(request)) => request, Ok(None) => { // Scheme likely got unmounted std::process::exit(0); } Err(err) if err.errno == EAGAIN => break, Err(err) => panic!("driver-graphics: failed to read display scheme: {err}"), }; match request.kind() { RequestKind::Call(call) => { let response = call.handle_sync(&mut self.inner, &mut self.state); self.inner .socket .write_response(response, SignalBehavior::Restart) .expect("driver-graphics: failed to write response"); } RequestKind::OnClose { id } => { self.inner.on_close(id); } _ => (), } } Ok(()) } } struct GraphicsSchemeInner { adapter: T, scheme_name: String, disable_graphical_debug: Option, socket: Socket, objects: KmsObjects, handles: HandleMap>, active_vt: usize, vts: HashMap>, } struct VtState { connector_state: Vec>, crtc_state: Vec>, plane_state: Vec>, cursor_plane: Option>, } enum Handle { V2(DrmHandle), SchemeRoot, } struct DrmHandle { vt: usize, unique: Option, next_id: u32, buffers: HashMap>, } impl GraphicsSchemeInner { fn get_or_create_vt<'a>( adapter: &T, objects: &KmsObjects, vts: &'a mut HashMap>, vt: usize, ) -> &'a mut VtState { vts.entry(vt).or_insert_with(|| VtState { connector_state: objects .connectors() .map(|connector| connector.lock().unwrap().state.clone()) .collect(), crtc_state: objects .crtcs() .map(|crtc| crtc.lock().unwrap().state.clone()) .collect(), plane_state: objects .planes() .map(|plane| plane.lock().unwrap().state.clone()) .collect(), cursor_plane: adapter.has_cursor_plane().then(|| CursorPlane { x: 0, y: 0, hot_x: 0, hot_y: 0, buffer: None, }), }) } fn activate_vt(&mut self, vt: usize) { log::info!("activate {}", vt); // Disable the kernel graphical debug writing once switching vt's for the // first time. This way the kernel graphical debug remains enabled if the // userspace logging infrastructure doesn't start up because for example a // kernel panic happened prior to it starting up or logd crashed. if let Some(mut disable_graphical_debug) = self.disable_graphical_debug.take() { let _ = disable_graphical_debug.write(&[1]); } self.active_vt = vt; let vt_state = GraphicsSchemeInner::get_or_create_vt(&self.adapter, &self.objects, &mut self.vts, vt); for (connector_idx, connector_state) in vt_state.connector_state.iter().enumerate() { let connector_id = self.objects.connector_ids()[connector_idx]; let mut connector = self .objects .get_connector(connector_id) .unwrap() .lock() .unwrap(); connector.state = connector_state.clone(); } for (crtc_idx, crtc_state) in vt_state.crtc_state.iter().enumerate() { let crtc_id = self.objects.crtc_ids()[crtc_idx]; let crtc = self.objects.get_crtc(crtc_id).unwrap(); let connector_id = self.objects.connector_ids()[crtc_idx]; self.adapter .set_crtc(&self.objects, crtc, crtc_state.clone()) .unwrap(); self.objects .get_connector(connector_id) .unwrap() .lock() .unwrap() .state .crtc_id = crtc_id; } for (plane_idx, plane_state) in vt_state.plane_state.iter().enumerate() { let plane_id = self.objects.plane_ids()[plane_idx]; let plane = self.objects.get_plane(plane_id).unwrap(); let fb = plane_state.fb_id.map(|fb_id| { self.objects .get_framebuffer(fb_id) .expect("removed framebuffers should be unset") }); self.adapter .set_plane( &self.objects, plane, plane_state.clone(), Damage { x: 0, y: 0, width: fb.map_or(0, |fb| fb.width), height: fb.map_or(0, |fb| fb.height), }, ) .unwrap(); } if let Some(cursor_plane) = &vt_state.cursor_plane { self.adapter.handle_cursor(cursor_plane, true); } } } const MAP_FAKE_OFFSET_MULTIPLIER: usize = 0x10_000_000; impl SchemeSync for GraphicsSchemeInner { fn scheme_root(&mut self) -> Result { Ok(self.handles.insert(Handle::SchemeRoot)) } fn openat( &mut self, dirfd: usize, path: &str, _flags: usize, _fcntl_flags: u32, _ctx: &CallerCtx, ) -> Result { if !matches!(self.handles.get(dirfd)?, Handle::SchemeRoot) { return Err(Error::new(EACCES)); } if path.is_empty() { return Err(Error::new(EINVAL)); } let handle = if path.starts_with("v") { if !path.starts_with("v2/") { return Err(Error::new(ENOENT)); } let vt = path["v2/".len()..] .parse::() .map_err(|_| Error::new(EINVAL))?; // Ensure the VT exists such that the rest of the methods can freely access it. Self::get_or_create_vt(&self.adapter, &self.objects, &mut self.vts, vt); Handle::V2(DrmHandle { vt, unique: None, next_id: 0, buffers: HashMap::new(), }) } else { return Err(Error::new(EINVAL)); }; let id = self.handles.insert(handle); Ok(OpenResult::ThisScheme { number: id, flags: NewFdFlags::empty(), }) } fn fstat(&mut self, _id: usize, stat: &mut syscall::Stat, _ctx: &CallerCtx) -> Result<()> { stat.st_dev = 226 /*DRM_MAJOR*/ << 8; Ok(()) } fn fpath(&mut self, id: usize, buf: &mut [u8], _ctx: &CallerCtx) -> syscall::Result { FpathWriter::with(buf, &self.scheme_name, |w| { match self.handles.get(id)? { Handle::V2(DrmHandle { vt, unique: _, next_id: _, buffers: _, }) => write!(w, "v2/{vt}").unwrap(), Handle::SchemeRoot => return Err(Error::new(EOPNOTSUPP)), }; Ok(()) }) } fn call( &mut self, id: usize, payload: &mut [u8], metadata: &[u64], _ctx: &CallerCtx, ) -> Result { match self.handles.get_mut(id)? { Handle::SchemeRoot => return Err(Error::new(EOPNOTSUPP)), Handle::V2(handle) => ioctl::call_ioctl( &mut self.adapter, &mut self.objects, self.active_vt, &mut self.vts, handle, metadata[0], payload, ), } } fn mmap_prep( &mut self, id: usize, offset: u64, _size: usize, _flags: MapFlags, _ctx: &CallerCtx, ) -> syscall::Result { // log::trace!("KSMSG MMAP {} {:?} {} {}", id, _flags, _offset, _size); let (framebuffer, offset) = match self.handles.get(id)? { Handle::V2(DrmHandle { vt: _, unique: _, next_id: _, buffers, }) => ( buffers .get(&((offset as usize / MAP_FAKE_OFFSET_MULTIPLIER) as u32)) .ok_or(Error::new(EINVAL)) .unwrap(), offset & (MAP_FAKE_OFFSET_MULTIPLIER as u64 - 1), ), Handle::SchemeRoot => return Err(Error::new(EOPNOTSUPP)), }; let ptr = T::map_dumb_buffer(&mut self.adapter, framebuffer); Ok(unsafe { ptr.add(offset as usize) } as usize) } fn on_close(&mut self, id: usize) { self.handles.remove(id); } }