diff --git a/local/recipes/gpu/redox-drm/source/src/driver.rs b/local/recipes/gpu/redox-drm/source/src/driver.rs index fe606ad8da..1a23eb2f8c 100644 --- a/local/recipes/gpu/redox-drm/source/src/driver.rs +++ b/local/recipes/gpu/redox-drm/source/src/driver.rs @@ -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)] diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/virtio/commands.rs b/local/recipes/gpu/redox-drm/source/src/drivers/virtio/commands.rs index 275f5f5750..50cb91082c 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/virtio/commands.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/virtio/commands.rs @@ -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 { diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/virtio/mod.rs b/local/recipes/gpu/redox-drm/source/src/drivers/virtio/mod.rs index 7ecf2e74af..3ad0f640bc 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/virtio/mod.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/virtio/mod.rs @@ -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 { 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 { + 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), diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/virtio/resource.rs b/local/recipes/gpu/redox-drm/source/src/drivers/virtio/resource.rs index 38342ca746..e65f1f364d 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/virtio/resource.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/virtio/resource.rs @@ -80,6 +80,10 @@ impl ResourceManager { }) } + pub fn find_by_handle(&self, handle: GemHandle) -> Option { + self.resources.get(&handle).copied() + } + pub fn allocate_resource_id(&mut self, handle: GemHandle) -> Result { if !self.resources.contains_key(&handle) { return Err(DriverError::NotFound(format!( diff --git a/local/recipes/gpu/redox-drm/source/src/scheme.rs b/local/recipes/gpu/redox-drm/source/src/scheme.rs index 6320a068fb..2847713c1e 100644 --- a/local/recipes/gpu/redox-drm/source/src/scheme.rs +++ b/local/recipes/gpu/redox-drm/source/src/scheme.rs @@ -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::(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 => {