From 510d3576dfd74c92ebc90403f4b0cc56402e0ed0 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 21 Dec 2025 21:54:44 +0100 Subject: [PATCH] driver/graphics: Add support for blob properties And support fetching the EDID in virtio-gpud. --- drivers/graphics/driver-graphics/src/lib.rs | 36 ++++++++++--- .../graphics/driver-graphics/src/objects.rs | 53 ++++++++++++++---- drivers/graphics/ihdgd/src/device/scheme.rs | 11 +++- drivers/graphics/vesad/src/scheme.rs | 10 +++- drivers/graphics/virtio-gpud/src/main.rs | 54 ++++++++++++++++++- drivers/graphics/virtio-gpud/src/scheme.rs | 45 ++++++++++++++-- 6 files changed, 183 insertions(+), 26 deletions(-) diff --git a/drivers/graphics/driver-graphics/src/lib.rs b/drivers/graphics/driver-graphics/src/lib.rs index 1c89d48c90..b77af3e4d9 100644 --- a/drivers/graphics/driver-graphics/src/lib.rs +++ b/drivers/graphics/driver-graphics/src/lib.rs @@ -24,12 +24,13 @@ use redox_scheme::{CallerCtx, OpenResult, RequestKind, SignalBehavior, Socket}; use syscall::schemev2::NewFdFlags; use syscall::{Error, MapFlags, Result, EAGAIN, EBADF, EINVAL, ENOENT, EOPNOTSUPP}; -use crate::objects::{DrmConnector, DrmObjectId, DrmObjects, DrmPropertyKind}; +use crate::objects::{DrmObjectId, DrmObjects, DrmPropertyKind}; pub mod objects; #[derive(Debug, Copy, Clone)] pub struct StandardProperties { + pub edid: DrmObjectId, pub dpms: DrmObjectId, } @@ -47,7 +48,12 @@ pub trait GraphicsAdapter: Sized + Debug { fn get_cap(&self, cap: u32) -> Result; fn set_client_cap(&self, cap: u32, value: u64) -> Result<()>; - fn probe_connector(&mut self, connector: &mut DrmConnector); + fn probe_connector( + &mut self, + objects: &mut DrmObjects, + standard_properties: &StandardProperties, + id: DrmObjectId, + ); /// The maximum amount of displays that could be attached. /// @@ -125,6 +131,7 @@ impl GraphicsScheme { let mut objects = DrmObjects::new(); + let edid = objects.add_property("EDID", true, false, DrmPropertyKind::Blob); let dpms = objects.add_property( "DPMS", false, @@ -136,10 +143,12 @@ impl GraphicsScheme { ("Off", DRM_MODE_DPMS_OFF.into()), ]), ); - let standard_properties = StandardProperties { dpms }; + let standard_properties = StandardProperties { edid, dpms }; adapter.init(&mut objects, &standard_properties); - objects.for_each_connector_mut(|connector| adapter.probe_connector(connector)); + for connector_id in objects.connector_ids().to_vec() { + adapter.probe_connector(&mut objects, &standard_properties, connector_id) + } GraphicsScheme { adapter, @@ -179,6 +188,10 @@ impl GraphicsScheme { (&mut self.adapter, &mut self.objects) } + pub fn standard_properties(&self) -> StandardProperties { + self.standard_properties + } + pub fn handle_vt_event(&mut self, vt_event: VtEvent) { match vt_event.kind { VtEventKind::Activate => { @@ -601,12 +614,16 @@ impl SchemeSync for GraphicsScheme { Ok(0) }), ipc::MODE_GET_CONNECTOR => ipc::DrmModeGetConnector::with(payload, |mut data| { + if data.count_modes() == 0 { + self.adapter.probe_connector( + &mut self.objects, + &self.standard_properties, + DrmObjectId(data.connector_id()), + ); + } let connector = self .objects .get_connector_mut(DrmObjectId(data.connector_id()))?; - if data.count_modes() == 0 { - self.adapter.probe_connector(connector); - } data.set_connection(connector.connection as u32); data.set_modes_ptr(&connector.modes); data.set_mm_width(connector.mm_width); @@ -705,6 +722,11 @@ impl SchemeSync for GraphicsScheme { } Ok(0) }), + ipc::MODE_GET_PROP_BLOB => ipc::DrmModeGetBlob::with(payload, |mut data| { + let blob = self.objects.get_blob(DrmObjectId(data.blob_id()))?; + data.set_data(&blob); + Ok(0) + }), ipc::MODE_GET_FB => ipc::DrmModeFbCmd::with(payload, |mut data| { let i = id_index(data.fb_id()); let (width, height) = self.adapter.display_size(i as usize); diff --git a/drivers/graphics/driver-graphics/src/objects.rs b/drivers/graphics/driver-graphics/src/objects.rs index cac576231d..3cd6ea3730 100644 --- a/drivers/graphics/driver-graphics/src/objects.rs +++ b/drivers/graphics/driver-graphics/src/objects.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::ffi::c_char; use drm_sys::{ - drm_mode_modeinfo, DRM_MODE_OBJECT_CONNECTOR, DRM_MODE_OBJECT_ENCODER, + drm_mode_modeinfo, DRM_MODE_OBJECT_BLOB, DRM_MODE_OBJECT_CONNECTOR, DRM_MODE_OBJECT_ENCODER, DRM_MODE_OBJECT_PROPERTY, DRM_PROP_NAME_LEN, }; use syscall::{Error, Result, EINVAL}; @@ -31,6 +31,7 @@ impl DrmObjects { let object = self.objects.get(&id).ok_or(Error::new(EINVAL))?; Ok(match object.kind { DrmObjectKind::Property(_) => DRM_MODE_OBJECT_PROPERTY, + DrmObjectKind::Blob(_) => DRM_MODE_OBJECT_BLOB, DrmObjectKind::Connector(_) => DRM_MODE_OBJECT_CONNECTOR, DrmObjectKind::Encoder(_) => DRM_MODE_OBJECT_ENCODER, }) @@ -103,15 +104,47 @@ impl DrmObjects { pub fn add_object_property(&mut self, object: DrmObjectId, property: DrmObjectId, value: u64) { let object = self.objects.get_mut(&object).unwrap(); - // FIXME validate property + // FIXME validate property uniqueness and value object.properties.push((property, value)); } + pub fn set_object_property(&mut self, object: DrmObjectId, property: DrmObjectId, value: u64) { + let object = self.objects.get_mut(&object).unwrap(); + // FIXME validate property existence and value + for (prop, val) in object.properties.iter_mut() { + if *prop == property { + *val = value; + } + } + } + pub fn get_object_properties(&self, id: DrmObjectId) -> Result<&[(DrmObjectId, u64)]> { let object = self.objects.get(&id).ok_or(Error::new(EINVAL))?; Ok(&object.properties) } + pub fn add_blob(&mut self, data: Vec) -> DrmObjectId { + let id = self.next_id; + self.objects.insert( + id, + DrmObject { + kind: DrmObjectKind::Blob(data), + properties: vec![], + }, + ); + self.next_id.0 += 1; + + id + } + + pub fn get_blob(&self, id: DrmObjectId) -> Result<&[u8]> { + let object = self.objects.get(&id).ok_or(Error::new(EINVAL))?; + match &object.kind { + DrmObjectKind::Blob(data) => Ok(data), + _ => Err(Error::new(EINVAL)), + } + } + pub fn add_connector(&mut self, driver_data: T::Connector) -> DrmObjectId { let connector_id = self.next_id; let encoder_id = DrmObjectId(self.next_id.0 + 1); @@ -164,15 +197,6 @@ impl DrmObjects { }) } - pub fn for_each_connector_mut<'a>(&mut self, mut f: impl FnMut(&mut DrmConnector)) { - for id in &self.connectors { - match &mut self.objects.get_mut(&id).unwrap().kind { - DrmObjectKind::Connector(connector) => f(connector), - _ => unreachable!(), - } - } - } - pub fn get_connector(&self, id: DrmObjectId) -> Result<&DrmConnector> { let object = self.objects.get(&id).ok_or(Error::new(EINVAL))?; match &object.kind { @@ -217,6 +241,12 @@ impl DrmObjectId { pub const INVALID: DrmObjectId = DrmObjectId(0); } +impl From for u64 { + fn from(value: DrmObjectId) -> Self { + value.0.into() + } +} + #[derive(Debug)] struct DrmObject { kind: DrmObjectKind, @@ -226,6 +256,7 @@ struct DrmObject { #[derive(Debug)] enum DrmObjectKind { Property(DrmProperty), + Blob(Vec), Connector(DrmConnector), Encoder(DrmEncoder), } diff --git a/drivers/graphics/ihdgd/src/device/scheme.rs b/drivers/graphics/ihdgd/src/device/scheme.rs index 68475a0ef8..8d275c8aeb 100644 --- a/drivers/graphics/ihdgd/src/device/scheme.rs +++ b/drivers/graphics/ihdgd/src/device/scheme.rs @@ -4,7 +4,7 @@ use std::alloc::{self, Layout}; use std::convert::TryInto; use std::ptr::{self, NonNull}; -use driver_graphics::objects::{DrmConnector, DrmConnectorStatus, DrmObjects}; +use driver_graphics::objects::{DrmConnectorStatus, DrmObjectId, DrmObjects}; use driver_graphics::{ modeinfo_for_size, CursorFramebuffer, CursorPlane, Framebuffer, GraphicsAdapter, StandardProperties, @@ -67,13 +67,20 @@ impl GraphicsAdapter for Device { } } - fn probe_connector(&mut self, connector: &mut DrmConnector) { + fn probe_connector( + &mut self, + objects: &mut DrmObjects, + _standard_properties: &StandardProperties, + id: DrmObjectId, + ) { + let connector = objects.get_connector_mut(id).unwrap(); let framebuffer = &self.framebuffers[connector.driver_data.framebuffer_id]; connector.connection = DrmConnectorStatus::Connected; connector.modes = vec![modeinfo_for_size( framebuffer.width as u32, framebuffer.height as u32, )]; + // FIXME fetch EDID } fn display_count(&self) -> usize { diff --git a/drivers/graphics/vesad/src/scheme.rs b/drivers/graphics/vesad/src/scheme.rs index fd478a2f45..31d18ba54f 100644 --- a/drivers/graphics/vesad/src/scheme.rs +++ b/drivers/graphics/vesad/src/scheme.rs @@ -2,7 +2,7 @@ use std::alloc::{self, Layout}; use std::convert::TryInto; use std::ptr::{self, NonNull}; -use driver_graphics::objects::{DrmConnector, DrmConnectorStatus, DrmObjects}; +use driver_graphics::objects::{DrmConnectorStatus, DrmObjectId, DrmObjects}; use driver_graphics::{ modeinfo_for_size, CursorFramebuffer, CursorPlane, Framebuffer, GraphicsAdapter, StandardProperties, @@ -69,7 +69,13 @@ impl GraphicsAdapter for FbAdapter { } } - fn probe_connector(&mut self, connector: &mut DrmConnector) { + fn probe_connector( + &mut self, + objects: &mut DrmObjects, + _standard_properties: &StandardProperties, + id: DrmObjectId, + ) { + let connector = objects.get_connector_mut(id).unwrap(); connector.modes = vec![modeinfo_for_size( connector.driver_data.width, connector.driver_data.height, diff --git a/drivers/graphics/virtio-gpud/src/main.rs b/drivers/graphics/virtio-gpud/src/main.rs index 16c69d119e..b34af2c60c 100644 --- a/drivers/graphics/virtio-gpud/src/main.rs +++ b/drivers/graphics/virtio-gpud/src/main.rs @@ -32,6 +32,12 @@ use virtio_core::MSIX_PRIMARY_VECTOR; mod scheme; +//const VIRTIO_GPU_F_VIRGL: u32 = 0; +const VIRTIO_GPU_F_EDID: u32 = 1; +//const VIRTIO_GPU_F_RESOURCE_UUID: u32 = 2; +//const VIRTIO_GPU_F_RESOURCE_BLOB: u32 = 3; +//const VIRTIO_GPU_F_CONTEXT_INIT: u32 = 4; + const VIRTIO_GPU_EVENT_DISPLAY: u32 = 1 << 0; const VIRTIO_GPU_MAX_SCANOUTS: usize = 16; @@ -362,6 +368,44 @@ impl XferToHost2d { } } +#[derive(Debug)] +#[repr(C)] +pub struct GetEdid { + pub header: ControlHeader, + pub scanout: u32, + pub padding: u32, +} + +impl GetEdid { + pub fn new(scanout_id: u32) -> Self { + Self { + header: ControlHeader::with_ty(CommandTy::GetEdid), + scanout: scanout_id, + padding: 0, + } + } +} + +#[derive(Debug)] +#[repr(C)] +pub struct GetEdidResp { + pub header: ControlHeader, + pub size: u32, + pub padding: u32, + pub edid: [u8; 1024], +} + +impl GetEdidResp { + pub fn new() -> Self { + Self { + header: ControlHeader::with_ty(CommandTy::GetEdid), + size: 0, + padding: 0, + edid: [0; 1024], + } + } +} + #[derive(Debug)] #[repr(C)] pub struct CursorPos { @@ -461,6 +505,10 @@ fn deamon(deamon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> anyhow: let config = unsafe { &mut *(device.device_space as *mut GpuConfig) }; // Negotiate features. + let has_edid = device.transport.check_device_feature(VIRTIO_GPU_F_EDID); + if has_edid { + device.transport.ack_driver_feature(VIRTIO_GPU_F_EDID); + } device.transport.finalize_features(); // Queue for sending control commands. @@ -483,6 +531,7 @@ fn deamon(deamon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> anyhow: control_queue.clone(), cursor_queue.clone(), device.transport.clone(), + has_edid, )?; user_data! { @@ -542,9 +591,12 @@ fn deamon(deamon: daemon::Daemon, mut pcid_handle: PciFunctionHandle) -> anyhow: let events = scheme.adapter().config.events_read.get(); if events & VIRTIO_GPU_EVENT_DISPLAY != 0 { + let standard_properties = scheme.standard_properties(); let (adapter, objects) = scheme.adapter_and_objects_mut(); futures::executor::block_on(async { adapter.update_displays().await.unwrap() }); - objects.for_each_connector_mut(|connector| adapter.probe_connector(connector)); + for connector_id in objects.connector_ids().to_vec() { + adapter.probe_connector(objects, &standard_properties, connector_id); + } scheme.notify_displays_changed(); scheme .adapter_mut() diff --git a/drivers/graphics/virtio-gpud/src/scheme.rs b/drivers/graphics/virtio-gpud/src/scheme.rs index 1086682877..c9efd130b9 100644 --- a/drivers/graphics/virtio-gpud/src/scheme.rs +++ b/drivers/graphics/virtio-gpud/src/scheme.rs @@ -2,7 +2,7 @@ use std::fmt; use std::sync::Arc; use common::{dma::Dma, sgl}; -use driver_graphics::objects::{DrmConnector, DrmConnectorStatus, DrmObjects}; +use driver_graphics::objects::{DrmConnectorStatus, DrmObjectId, DrmObjects}; use driver_graphics::{ modeinfo_for_size, CursorFramebuffer, CursorPlane, Framebuffer, GraphicsAdapter, GraphicsScheme, StandardProperties, @@ -76,11 +76,12 @@ pub struct VirtGpuCursor { impl CursorFramebuffer for VirtGpuCursor {} -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone)] pub struct Display { enabled: bool, width: u32, height: u32, + edid: Vec, active_resource: Option, } @@ -89,6 +90,7 @@ pub struct VirtGpuAdapter<'a> { control_queue: Arc>, cursor_queue: Arc>, transport: Arc, + has_edid: bool, displays: Vec, } @@ -111,6 +113,7 @@ impl VirtGpuAdapter<'_> { enabled: false, width: 0, height: 0, + edid: vec![], active_resource: None, }, ); @@ -133,6 +136,11 @@ impl VirtGpuAdapter<'_> { self.displays[i].width = info.rect.width; self.displays[i].height = info.rect.height; } + + if self.has_edid { + let edid = self.get_edid(i as u32).await?; + self.displays[i].edid = edid.edid[..edid.size as usize].to_vec(); + } } Ok(()) @@ -176,6 +184,21 @@ impl VirtGpuAdapter<'_> { Ok(response) } + async fn get_edid(&self, scanout_id: u32) -> Result, Error> { + let header = Dma::new(GetEdid::new(scanout_id))?; + + let response = Dma::new(GetEdidResp::new())?; + let command = ChainBuilder::new() + .chain(Buffer::new(&header)) + .chain(Buffer::new(&response).flags(DescriptorFlags::WRITE_ONLY)) + .build(); + + self.control_queue.send(command).await; + assert!(response.header.ty == CommandTy::RespOkEdid); + + Ok(response) + } + fn update_cursor(&mut self, cursor: &VirtGpuCursor, x: i32, y: i32, hot_x: i32, hot_y: i32) { //Transfering cursor resource to host futures::executor::block_on(async { @@ -240,6 +263,9 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { for display_id in 0..self.config.num_scanouts.get() { let connector = objects.add_connector(VirtGpuConnector { display_id }); + if self.has_edid { + objects.add_object_property(connector, standard_properties.edid, 0); + } objects.add_object_property( connector, standard_properties.dpms, @@ -263,8 +289,14 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { } } - fn probe_connector(&mut self, connector: &mut DrmConnector) { + fn probe_connector( + &mut self, + objects: &mut DrmObjects, + standard_properties: &StandardProperties, + id: DrmObjectId, + ) { futures::executor::block_on(async { + let connector = objects.get_connector_mut(id).unwrap(); let display = &self.displays[connector.driver_data.display_id as usize]; connector.modes = vec![modeinfo_for_size(display.width, display.height)]; @@ -273,6 +305,11 @@ impl<'a> GraphicsAdapter for VirtGpuAdapter<'a> { } else { DrmConnectorStatus::Disconnected }; + + if self.has_edid { + let blob = objects.add_blob(display.edid.clone()); + objects.set_object_property(id, standard_properties.edid, blob.into()); + } }); } @@ -482,12 +519,14 @@ impl<'a> GpuScheme { control_queue: Arc>, cursor_queue: Arc>, transport: Arc, + has_edid: bool, ) -> Result<(GraphicsScheme>, DisplayHandle), Error> { let adapter = VirtGpuAdapter { config, control_queue, cursor_queue, transport, + has_edid, displays: vec![], };