drm: VIRGL blob resources, hardware cursor, atomic modeset
Implement VIRTGPU_RESOURCE_CREATE_BLOB:
- Define VirtioGpuResourceCreateBlob wire struct (commands.rs)
- Add VIRTIO_GPU_BLOB_MEM_*/FLAG_* constants
- Negotiate VIRTIO_GPU_F_RESOURCE_BLOB feature flag
- Add virgl_resource_create_blob() to GpuDriver trait
- Implement in VirtioDriver with virtio command dispatch
- Wire ioctl handler in scheme.rs (was EOPNOTSUPP stub)
- Add find_by_handle() to ResourceManager
Implement hardware cursor:
- Add VIRTIO_GPU_CMD_UPDATE_CURSOR/MOVE_CURSOR opcodes
- Define VirtioGpuCmdUpdateCursor/MoveCursor/CursorPos structs
- Add update_cursor()/move_cursor() to VirtioGpuDevice
- Override cursor_set/cursor_move on VirtioDriver
- CRTC-to-connector lookup for scanout index mapping
Implement atomic modeset:
- Override atomic_commit on VirtioDriver with full state
validation via atomic_check(), then delegate to
set_crtc + page_flip for each active CRTC
- Support TEST_ONLY flag (returns NoChange)
Mesa recipe: add iris,crocus to gallium-drivers
Config: enable mesa = {} in redbear-full.toml
This commit is contained in:
@@ -285,6 +285,12 @@ pub trait GpuDriver: Send + Sync {
|
||||
) -> Result<()> {
|
||||
Err(DriverError::Unsupported("virgl resource attach backing requires Mesa virgl + redox-drm GEM integration"))
|
||||
}
|
||||
|
||||
fn virgl_resource_create_blob(
|
||||
&self, _blob_mem: u32, _blob_flags: u32, _blob_id: u64, _size: u64,
|
||||
) -> Result<(GemHandle, u32)> {
|
||||
Err(DriverError::Unsupported("virgl blob resource creation not supported by this backend"))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -292,6 +292,84 @@ pub struct VirtioGpuRespMapInfo {
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
// Blob resource mem types (Linux virtio_gpu.h)
|
||||
pub const VIRTIO_GPU_BLOB_MEM_GUEST: u32 = 0x0001;
|
||||
pub const VIRTIO_GPU_BLOB_MEM_HOST3D: u32 = 0x0002;
|
||||
pub const VIRTIO_GPU_BLOB_MEM_HOST3D_GUEST: u32 = 0x0003;
|
||||
|
||||
// Blob resource flags
|
||||
pub const VIRTIO_GPU_BLOB_FLAG_USE_MAPPABLE: u32 = 0x0001;
|
||||
pub const VIRTIO_GPU_BLOB_FLAG_USE_SHAREABLE: u32 = 0x0002;
|
||||
pub const VIRTIO_GPU_BLOB_FLAG_USE_CROSS_DEVICE: u32 = 0x0004;
|
||||
pub const VIRTIO_GPU_BLOB_FLAG_USE_LINEAR: u32 = 0x0008;
|
||||
|
||||
// Cursor commands (virtio_gpu.h 0x03xx)
|
||||
pub const VIRTIO_GPU_CMD_UPDATE_CURSOR: u32 = 0x0300;
|
||||
pub const VIRTIO_GPU_CMD_MOVE_CURSOR: u32 = 0x0301;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuResourceCreateBlob {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub resource_id: u32,
|
||||
pub blob_mem: u32,
|
||||
pub blob_flags: u32,
|
||||
pub nr_entries: u32,
|
||||
pub blob_id: u64,
|
||||
pub size: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuResourceUnmapBlob {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub resource_id: u32,
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuSetScanoutBlob {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub scanout_id: u32,
|
||||
pub resource_id: u32,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub format: u32,
|
||||
pub padding: u32,
|
||||
pub strides: [u32; 4],
|
||||
pub offsets: [u32; 4],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuCmdUpdateCursor {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub pos: VirtioGpuCursorPos,
|
||||
pub resource_id: u32,
|
||||
pub hot_x: u32,
|
||||
pub hot_y: u32,
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuCmdMoveCursor {
|
||||
pub hdr: VirtioGpuCtrlHeader,
|
||||
pub pos: VirtioGpuCursorPos,
|
||||
pub resource_id: u32,
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuCursorPos {
|
||||
pub scanout_id: u32,
|
||||
pub x: u32,
|
||||
pub y: u32,
|
||||
pub padding: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct VirtioGpuCmdGetEdid {
|
||||
|
||||
@@ -17,14 +17,15 @@ use crate::drivers::fence::FenceTimeline;
|
||||
use crate::drivers::interrupt::InterruptHandle;
|
||||
use crate::drivers::syncobj::SyncobjManager;
|
||||
use crate::gem::{GemHandle, GemManager};
|
||||
use crate::kms::atomic::{AtomicCommitResult, AtomicState, atomic_check};
|
||||
use crate::kms::connector::{synthetic_edid, Connector};
|
||||
use crate::kms::crtc::Crtc;
|
||||
use crate::kms::{ConnectorInfo, ConnectorStatus, ConnectorType, ModeInfo};
|
||||
|
||||
use self::commands::{
|
||||
append_bytes, bytes_of, read_struct, validate_response_type, response_type_name,
|
||||
VirtioGpuBox, VirtioGpuCmdGetEdid,
|
||||
VirtioGpuCmdSubmit, VirtioGpuConfig, VirtioGpuCtrlHeader, VirtioGpuCtxCreate,
|
||||
VirtioGpuBox, VirtioGpuCmdGetEdid, VirtioGpuCmdMoveCursor, VirtioGpuCmdUpdateCursor,
|
||||
VirtioGpuCmdSubmit, VirtioGpuConfig, VirtioGpuCtrlHeader, VirtioGpuCursorPos,
|
||||
VirtioGpuCtxCreate,
|
||||
VirtioGpuCtxDestroy, VirtioGpuDisplayOne, VirtioGpuGetCapset, VirtioGpuGetCapsetInfo,
|
||||
VirtioGpuMemEntry, VirtioGpuRect, VirtioGpuResourceAttachBacking, VirtioGpuResourceCreate2d,
|
||||
VirtioGpuResourceCreate3d, VirtioGpuResourceFlush, VirtioGpuResourceUnref, VirtioGpuRespCapset,
|
||||
@@ -32,12 +33,14 @@ use self::commands::{
|
||||
VirtioGpuTransferHost3d, VirtioGpuTransferToHost2d, VIRTIO_F_VERSION_1,
|
||||
VIRTIO_GPU_CMD_CTX_CREATE, VIRTIO_GPU_CMD_CTX_DESTROY, VIRTIO_GPU_CMD_GET_CAPSET,
|
||||
VIRTIO_GPU_CMD_GET_CAPSET_INFO, VIRTIO_GPU_CMD_GET_DISPLAY_INFO, VIRTIO_GPU_CMD_GET_EDID,
|
||||
VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, VIRTIO_GPU_CMD_RESOURCE_CREATE_2D,
|
||||
VIRTIO_GPU_CMD_MOVE_CURSOR, VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING,
|
||||
VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB,
|
||||
VIRTIO_GPU_CMD_RESOURCE_CREATE_3D, VIRTIO_GPU_CMD_RESOURCE_FLUSH,
|
||||
VIRTIO_GPU_CMD_RESOURCE_UNREF, VIRTIO_GPU_CMD_SET_SCANOUT, VIRTIO_GPU_CMD_SUBMIT_3D,
|
||||
VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D, VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D,
|
||||
VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D, VIRTIO_GPU_EVENT_DISPLAY, VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM,
|
||||
VIRTIO_GPU_F_EDID, VIRTIO_GPU_F_VIRGL, VIRTIO_GPU_RESP_OK_CAPSET,
|
||||
VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D, VIRTIO_GPU_CMD_UPDATE_CURSOR,
|
||||
VIRTIO_GPU_EVENT_DISPLAY, VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM,
|
||||
VIRTIO_GPU_F_EDID, VIRTIO_GPU_F_RESOURCE_BLOB, VIRTIO_GPU_F_VIRGL, VIRTIO_GPU_RESP_OK_CAPSET,
|
||||
VIRTIO_GPU_RESP_OK_CAPSET_INFO, VIRTIO_GPU_RESP_OK_DISPLAY_INFO, VIRTIO_GPU_RESP_OK_EDID,
|
||||
VIRTIO_GPU_RESP_OK_MAP_INFO, VIRTIO_GPU_RESP_OK_NODATA,
|
||||
};
|
||||
@@ -74,6 +77,7 @@ struct VirtioGpuDevice {
|
||||
cursorq: Virtqueue,
|
||||
supports_edid: bool,
|
||||
has_virgl_3d: bool,
|
||||
has_resource_blob: bool,
|
||||
}
|
||||
|
||||
impl VirtioDriver {
|
||||
@@ -109,7 +113,7 @@ impl VirtioDriver {
|
||||
|
||||
let mut transport = VirtioModernPciTransport::new(&info, &mut pci)?;
|
||||
let negotiated = transport
|
||||
.initialize_device(VIRTIO_F_VERSION_1 | VIRTIO_GPU_F_EDID | VIRTIO_GPU_F_VIRGL)?;
|
||||
.initialize_device(VIRTIO_F_VERSION_1 | VIRTIO_GPU_F_EDID | VIRTIO_GPU_F_VIRGL | VIRTIO_GPU_F_RESOURCE_BLOB)?;
|
||||
let ctrlq_cfg = transport.prepare_queue(CTRL_QUEUE_INDEX, DEFAULT_QUEUE_SIZE)?;
|
||||
let ctrlq = Virtqueue::new(ctrlq_cfg.index, ctrlq_cfg.size, ctrlq_cfg.notify_off)?;
|
||||
transport.activate_queue(
|
||||
@@ -140,6 +144,7 @@ impl VirtioDriver {
|
||||
cursorq,
|
||||
supports_edid: (negotiated & VIRTIO_GPU_F_EDID) != 0,
|
||||
has_virgl_3d: (negotiated & VIRTIO_GPU_F_VIRGL) != 0,
|
||||
has_resource_blob: (negotiated & VIRTIO_GPU_F_RESOURCE_BLOB) != 0,
|
||||
};
|
||||
let (connectors, crtcs) = load_display_topology(&mut device)?;
|
||||
|
||||
@@ -865,6 +870,27 @@ impl GpuDriver for VirtioDriver {
|
||||
device.resource_attach_backing(resource.resource_id, phys_addr, length)
|
||||
}
|
||||
|
||||
fn virgl_resource_create_blob(
|
||||
&self, blob_mem: u32, blob_flags: u32, blob_id: u64, size: u64,
|
||||
) -> Result<(GemHandle, u32)> {
|
||||
let mut device = self
|
||||
.device
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Initialization("VirtIO device state poisoned".into()))?;
|
||||
if !device.has_resource_blob {
|
||||
return Err(DriverError::Unsupported("virgl blob resource create"));
|
||||
}
|
||||
let gem_handle = self.gem_create(size, 0, 0)?;
|
||||
let resource = self
|
||||
.resources
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Initialization("VirtIO resource state poisoned".into()))?
|
||||
.find_by_handle(gem_handle)
|
||||
.ok_or(DriverError::NotFound(format!("resource for GEM handle {}", gem_handle)))?;
|
||||
device.resource_create_blob(resource.resource_id, blob_mem, blob_flags, blob_id, size)?;
|
||||
Ok((gem_handle, resource.resource_id))
|
||||
}
|
||||
|
||||
fn syncobj_create(&self) -> Result<SyncobjHandle> {
|
||||
self.syncobjs
|
||||
.lock()
|
||||
@@ -905,6 +931,92 @@ impl GpuDriver for VirtioDriver {
|
||||
.map_err(|_| DriverError::Initialization("VirtIO syncobj state poisoned".into()))?
|
||||
.import_sync_file(fd)
|
||||
}
|
||||
|
||||
fn cursor_set(&self, crtc_id: u32, fb_handle: u32, hot_x: u32, hot_y: u32) -> Result<()> {
|
||||
let resource = self
|
||||
.resources
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Initialization("VirtIO resource state poisoned".into()))?
|
||||
.find_by_handle(fb_handle)
|
||||
.ok_or(DriverError::NotFound(format!(
|
||||
"cursor resource for GEM handle {}", fb_handle
|
||||
)))?;
|
||||
let connector_id = self
|
||||
.crtcs
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Initialization("VirtIO CRTC state poisoned".into()))?
|
||||
.iter()
|
||||
.find(|crtc| crtc.id == crtc_id)
|
||||
.and_then(|crtc| crtc.connectors.first().copied())
|
||||
.ok_or(DriverError::NotFound(format!(
|
||||
"no connector for CRTC {}", crtc_id
|
||||
)))?;
|
||||
let scanout_id = self.scanout_for_connector(connector_id)?;
|
||||
let mut device = self
|
||||
.device
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Initialization("VirtIO device state poisoned".into()))?;
|
||||
device.update_cursor(scanout_id, resource.resource_id, hot_x, hot_y)
|
||||
}
|
||||
|
||||
fn cursor_move(&self, crtc_id: u32, x: i32, y: i32) -> Result<()> {
|
||||
let connector_id = self
|
||||
.crtcs
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Initialization("VirtIO CRTC state poisoned".into()))?
|
||||
.iter()
|
||||
.find(|crtc| crtc.id == crtc_id)
|
||||
.and_then(|crtc| crtc.connectors.first().copied())
|
||||
.ok_or(DriverError::NotFound(format!(
|
||||
"no connector for CRTC {}", crtc_id
|
||||
)))?;
|
||||
let scanout_id = self.scanout_for_connector(connector_id)?;
|
||||
let mut device = self
|
||||
.device
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Initialization("VirtIO device state poisoned".into()))?;
|
||||
device.move_cursor(scanout_id, x, y)
|
||||
}
|
||||
|
||||
fn atomic_commit(&self, state: &AtomicState) -> Result<AtomicCommitResult> {
|
||||
use crate::kms::atomic::AtomicCheckResult;
|
||||
let connectors = self.detect_connectors();
|
||||
match atomic_check(state, &connectors) {
|
||||
AtomicCheckResult::Ok => {}
|
||||
AtomicCheckResult::CrtcNotFound { crtc_id } => {
|
||||
return Err(DriverError::NotFound(format!("CRTC {}", crtc_id)));
|
||||
}
|
||||
AtomicCheckResult::InvalidMode { crtc_id, reason } => {
|
||||
return Err(DriverError::InvalidArgument(Box::leak(
|
||||
format!("CRTC {} invalid mode: {}", crtc_id, reason).into_boxed_str(),
|
||||
)));
|
||||
}
|
||||
AtomicCheckResult::ClockTooHigh { crtc_id, .. } => {
|
||||
return Err(DriverError::InvalidArgument(Box::leak(
|
||||
format!("CRTC {} pixel clock exceeds hardware limit", crtc_id).into_boxed_str(),
|
||||
)));
|
||||
}
|
||||
AtomicCheckResult::ConnectorDisconnected { connector_id } => {
|
||||
return Err(DriverError::Initialization(format!(
|
||||
"connector {} disconnected", connector_id
|
||||
)));
|
||||
}
|
||||
}
|
||||
if state.test_only {
|
||||
return Ok(AtomicCommitResult::NoChange);
|
||||
}
|
||||
let mut vblank_count = 0u64;
|
||||
for crtc in &state.crtc_states {
|
||||
if !crtc.active {
|
||||
continue;
|
||||
}
|
||||
if let Some(ref mode) = crtc.mode {
|
||||
self.set_crtc(crtc.crtc_id, crtc.fb_handle, &crtc.connectors, mode)?;
|
||||
vblank_count = self.page_flip(crtc.crtc_id, crtc.fb_handle, 0)?;
|
||||
}
|
||||
}
|
||||
Ok(AtomicCommitResult::Committed { vblank_count })
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtioGpuDevice {
|
||||
@@ -1052,6 +1164,40 @@ impl VirtioGpuDevice {
|
||||
self.submit_nodata(&request)
|
||||
}
|
||||
|
||||
fn update_cursor(&mut self, scanout_id: u32, resource_id: u32, hot_x: u32, hot_y: u32) -> Result<()> {
|
||||
let pos = VirtioGpuCursorPos {
|
||||
scanout_id,
|
||||
x: 0,
|
||||
y: 0,
|
||||
padding: 0,
|
||||
};
|
||||
let request = VirtioGpuCmdUpdateCursor {
|
||||
hdr: VirtioGpuCtrlHeader::command(VIRTIO_GPU_CMD_UPDATE_CURSOR),
|
||||
pos,
|
||||
resource_id,
|
||||
hot_x,
|
||||
hot_y,
|
||||
padding: 0,
|
||||
};
|
||||
self.submit_nodata(&request)
|
||||
}
|
||||
|
||||
fn move_cursor(&mut self, scanout_id: u32, x: i32, y: i32) -> Result<()> {
|
||||
let pos = VirtioGpuCursorPos {
|
||||
scanout_id,
|
||||
x: x.max(0) as u32,
|
||||
y: y.max(0) as u32,
|
||||
padding: 0,
|
||||
};
|
||||
let request = VirtioGpuCmdMoveCursor {
|
||||
hdr: VirtioGpuCtrlHeader::command(VIRTIO_GPU_CMD_MOVE_CURSOR),
|
||||
pos,
|
||||
resource_id: 0,
|
||||
padding: 0,
|
||||
};
|
||||
self.submit_nodata(&request)
|
||||
}
|
||||
|
||||
fn set_scanout(
|
||||
&mut self,
|
||||
scanout_id: u32,
|
||||
@@ -1088,6 +1234,21 @@ impl VirtioGpuDevice {
|
||||
self.submit_nodata(&request)
|
||||
}
|
||||
|
||||
fn resource_create_blob(
|
||||
&mut self, resource_id: u32, blob_mem: u32, blob_flags: u32, blob_id: u64, size: u64,
|
||||
) -> Result<()> {
|
||||
let request = VirtioGpuResourceCreateBlob {
|
||||
hdr: VirtioGpuCtrlHeader::command(VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB),
|
||||
resource_id,
|
||||
blob_mem,
|
||||
blob_flags,
|
||||
nr_entries: 0,
|
||||
blob_id,
|
||||
size,
|
||||
};
|
||||
self.submit_nodata(&request)
|
||||
}
|
||||
|
||||
fn ctx_create(&mut self, ctx_id: u32, debug_name: &str, context_init: u32) -> Result<()> {
|
||||
let mut request = VirtioGpuCtxCreate {
|
||||
hdr: VirtioGpuCtrlHeader::command(VIRTIO_GPU_CMD_CTX_CREATE),
|
||||
|
||||
@@ -80,6 +80,10 @@ impl ResourceManager {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn find_by_handle(&self, handle: GemHandle) -> Option<VirtioResource> {
|
||||
self.resources.get(&handle).copied()
|
||||
}
|
||||
|
||||
pub fn allocate_resource_id(&mut self, handle: GemHandle) -> Result<u32> {
|
||||
if !self.resources.contains_key(&handle) {
|
||||
return Err(DriverError::NotFound(format!(
|
||||
|
||||
@@ -508,6 +508,20 @@ struct VirtgpuMapWire {
|
||||
pad: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
struct VirtgpuResourceCreateBlobWire {
|
||||
blob_mem: u32,
|
||||
blob_flags: u32,
|
||||
bo_handle: u32,
|
||||
res_handle: u32,
|
||||
size: u64,
|
||||
pad: u32,
|
||||
cmd_size: u32,
|
||||
cmd: u64,
|
||||
blob_id: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
struct VirtgpuContextInitWire {
|
||||
@@ -2475,10 +2489,17 @@ impl DrmScheme {
|
||||
}
|
||||
|
||||
DRM_IOCTL_VIRTGPU_RESOURCE_CREATE_BLOB => {
|
||||
warn!(
|
||||
"redox-drm: VIRTGPU_RESOURCE_CREATE_BLOB rejected — virgl blob resources are not implemented"
|
||||
);
|
||||
return Err(Error::new(EOPNOTSUPP));
|
||||
let req = decode_wire::<VirtgpuResourceCreateBlobWire>(payload)?;
|
||||
let (bo_handle, res_handle) = self
|
||||
.driver
|
||||
.virgl_resource_create_blob(
|
||||
req.blob_mem, req.blob_flags, req.blob_id, req.size,
|
||||
)
|
||||
.map_err(driver_to_syscall)?;
|
||||
let mut resp = req;
|
||||
resp.bo_handle = bo_handle;
|
||||
resp.res_handle = res_handle;
|
||||
bytes_of(&resp)
|
||||
}
|
||||
|
||||
DRM_IOCTL_REDOX_AMD_SDMA_SUBMIT => {
|
||||
|
||||
Reference in New Issue
Block a user