driver/graphics: Add support for blob properties

And support fetching the EDID in virtio-gpud.
This commit is contained in:
bjorn3
2025-12-21 21:54:44 +01:00
parent 13f8b090cf
commit 510d3576df
6 changed files with 183 additions and 26 deletions
+29 -7
View File
@@ -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<u64>;
fn set_client_cap(&self, cap: u32, value: u64) -> Result<()>;
fn probe_connector(&mut self, connector: &mut DrmConnector<Self>);
fn probe_connector(
&mut self,
objects: &mut DrmObjects<Self>,
standard_properties: &StandardProperties,
id: DrmObjectId,
);
/// The maximum amount of displays that could be attached.
///
@@ -125,6 +131,7 @@ impl<T: GraphicsAdapter> GraphicsScheme<T> {
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<T: GraphicsAdapter> GraphicsScheme<T> {
("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<T: GraphicsAdapter> GraphicsScheme<T> {
(&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<T: GraphicsAdapter> SchemeSync for GraphicsScheme<T> {
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<T: GraphicsAdapter> SchemeSync for GraphicsScheme<T> {
}
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);
+42 -11
View File
@@ -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<T: GraphicsAdapter> DrmObjects<T> {
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<T: GraphicsAdapter> DrmObjects<T> {
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<u8>) -> 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<T: GraphicsAdapter> DrmObjects<T> {
})
}
pub fn for_each_connector_mut<'a>(&mut self, mut f: impl FnMut(&mut DrmConnector<T>)) {
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<T>> {
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<DrmObjectId> for u64 {
fn from(value: DrmObjectId) -> Self {
value.0.into()
}
}
#[derive(Debug)]
struct DrmObject<T: GraphicsAdapter> {
kind: DrmObjectKind<T>,
@@ -226,6 +256,7 @@ struct DrmObject<T: GraphicsAdapter> {
#[derive(Debug)]
enum DrmObjectKind<T: GraphicsAdapter> {
Property(DrmProperty),
Blob(Vec<u8>),
Connector(DrmConnector<T>),
Encoder(DrmEncoder),
}
+9 -2
View File
@@ -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<Self>) {
fn probe_connector(
&mut self,
objects: &mut DrmObjects<Self>,
_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 {
+8 -2
View File
@@ -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<Self>) {
fn probe_connector(
&mut self,
objects: &mut DrmObjects<Self>,
_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,
+53 -1
View File
@@ -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()
+42 -3
View File
@@ -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<u8>,
active_resource: Option<ResourceId>,
}
@@ -89,6 +90,7 @@ pub struct VirtGpuAdapter<'a> {
control_queue: Arc<Queue<'a>>,
cursor_queue: Arc<Queue<'a>>,
transport: Arc<dyn Transport>,
has_edid: bool,
displays: Vec<Display>,
}
@@ -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<Dma<GetEdidResp>, 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<Self>) {
fn probe_connector(
&mut self,
objects: &mut DrmObjects<Self>,
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<Queue<'a>>,
cursor_queue: Arc<Queue<'a>>,
transport: Arc<dyn Transport>,
has_edid: bool,
) -> Result<(GraphicsScheme<VirtGpuAdapter<'a>>, DisplayHandle), Error> {
let adapter = VirtGpuAdapter {
config,
control_queue,
cursor_queue,
transport,
has_edid,
displays: vec![],
};