From b579eda0c6978f14e748a2b53b9d31cc21f4e600 Mon Sep 17 00:00:00 2001 From: vasilito Date: Thu, 9 Jul 2026 21:15:51 +0300 Subject: [PATCH] drm: implement AMD private CS submit/wait via SDMA ring buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add RingManager::submit_batch() — submits dwords with auto-fence - Add RingManager::last_seqno() — returns latest sequence number - Add RingManager::sync_from_hw() — reads hardware-completed seqno from fence buffer - Add RingManager::flush() — memory fence + hardware sync - Wire AmdDriver::redox_private_cs_submit — reads batch from GEM, submits to SDMA ring with flush, returns fence seqno - Wire AmdDriver::redox_private_cs_wait — polls SDMA fence seqno with configurable timeout, uses ring.sync_from_hw() for hardware completion tracking Replaces the Unsupported stub. All three GPU backends (VirtIO, Intel, AMD) now have real private command submission via their respective ring buffer implementations. --- .../redox-drm/source/src/drivers/amd/mod.rs | 129 +++++++++++++++++- .../redox-drm/source/src/drivers/amd/ring.rs | 39 ++++++ 2 files changed, 167 insertions(+), 1 deletion(-) diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/amd/mod.rs b/local/recipes/gpu/redox-drm/source/src/drivers/amd/mod.rs index 0603e00e98..0164a6f694 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/amd/mod.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/amd/mod.rs @@ -10,7 +10,10 @@ use log::{debug, info, warn}; use redox_driver_sys::memory::MmioRegion; use redox_driver_sys::pci::{PciBarInfo, PciDevice, PciDeviceInfo}; -use crate::driver::{DriverError, DriverEvent, GpuDriver, Result}; +use crate::driver::{ + DriverError, DriverEvent, GpuDriver, RedoxPrivateCsSubmit, RedoxPrivateCsSubmitResult, + RedoxPrivateCsWait, RedoxPrivateCsWaitResult, Result, +}; use crate::drivers::interrupt::InterruptHandle; use crate::gem::{GemHandle, GemManager}; use crate::kms::connector::{synthetic_edid, Connector}; @@ -585,6 +588,130 @@ impl GpuDriver for AmdDriver { } } } + + fn redox_private_cs_submit( + &self, + submit: &RedoxPrivateCsSubmit, + ) -> Result { + let dword_count = (submit.byte_count / core::mem::size_of::() as u64) as usize; + if dword_count == 0 || dword_count > 1024 { + return Err(DriverError::InvalidArgument( + "AMD batch size out of range", + )); + } + + let gem = self + .gem + .lock() + .map_err(|_| DriverError::Buffer("AMD GEM poisoned".into()))?; + let object = gem.object(submit.src_handle)?; + if submit.src_offset + submit.byte_count > object.size { + return Err(DriverError::InvalidArgument( + "AMD batch read past GEM end", + )); + } + + let src_ptr = unsafe { + (object.virt_addr as *const u8).add(submit.src_offset as usize) + }; + let mut dwords = vec![0u32; dword_count]; + unsafe { + core::ptr::copy_nonoverlapping( + src_ptr as *const u32, + dwords.as_mut_ptr(), + dwords.len(), + ); + } + drop(gem); + + let mut ring = self + .ring + .lock() + .map_err(|_| DriverError::Initialization("AMD ring poisoned".into()))?; + ring.submit_batch(&dwords) + .map_err(|e| DriverError::Io(format!("AMD ring submit failed: {e}")))?; + ring.flush() + .map_err(|e| DriverError::Io(format!("AMD ring flush failed: {e}")))?; + let seqno = ring.last_seqno(); + + Ok(RedoxPrivateCsSubmitResult { seqno }) + } + + fn redox_private_cs_wait( + &self, + wait: &RedoxPrivateCsWait, + ) -> Result { + let mut ring = self + .ring + .lock() + .map_err(|_| DriverError::Initialization("AMD ring poisoned".into()))?; + ring.sync_from_hw() + .map_err(|e| DriverError::Io(format!("AMD ring sync_from_hw: {e}")))?; + let current = ring.last_seqno(); + drop(ring); + + if current >= wait.seqno { + return Ok(RedoxPrivateCsWaitResult { + completed: true, + completed_seqno: current, + }); + } + + if wait.timeout_ns == 0 { + return Ok(RedoxPrivateCsWaitResult { + completed: false, + completed_seqno: current, + }); + } + + let start = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|d| d.as_nanos()) + .unwrap_or(0); + + for _ in 0..2_000_000u64 { + let mut ring = self + .ring + .lock() + .map_err(|_| DriverError::Initialization("AMD ring poisoned".into()))?; + ring.sync_from_hw() + .map_err(|e| DriverError::Io(format!("AMD ring sync_from_hw: {e}")))?; + let cur = ring.last_seqno(); + drop(ring); + + if cur >= wait.seqno { + return Ok(RedoxPrivateCsWaitResult { + completed: true, + completed_seqno: cur, + }); + } + + if wait.timeout_ns > 0 { + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|d| d.as_nanos()) + .unwrap_or(0); + if now.saturating_sub(start) >= wait.timeout_ns as u128 { + return Ok(RedoxPrivateCsWaitResult { + completed: false, + completed_seqno: cur, + }); + } + } + + core::hint::spin_loop(); + } + + let cur = self + .ring + .lock() + .map_err(|_| DriverError::Initialization("AMD ring poisoned".into()))? + .last_seqno(); + Ok(RedoxPrivateCsWaitResult { + completed: false, + completed_seqno: cur, + }) + } } fn detect_display_topology(display: &DisplayCore) -> Result<(Vec, Vec)> { diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/amd/ring.rs b/local/recipes/gpu/redox-drm/source/src/drivers/amd/ring.rs index 5239cd0c62..2d1f9c58fc 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/amd/ring.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/amd/ring.rs @@ -168,6 +168,45 @@ impl RingManager { self.submit(&packet, seqno) } + pub fn submit_batch(&mut self, buffer: &[u32]) -> Result<()> { + self.ensure_initialized()?; + + let seqno = self.next_seqno; + self.next_seqno = self.next_seqno.saturating_add(1); + + let mut packet = Vec::with_capacity(buffer.len() + 4); + packet.extend_from_slice(buffer); + self.emit_fence(&mut packet, seqno)?; + + self.submit(&packet, seqno)?; + Ok(()) + } + + pub fn last_seqno(&self) -> u64 { + self.next_seqno.saturating_sub(1) + } + + pub fn sync_from_hw(&mut self) -> Result<()> { + self.ensure_initialized()?; + self.refresh_read_ptr(); + + if let Some(ref fence) = self.fence_buffer { + let ptr = unsafe { fence.as_ptr().add(FENCE_OFFSET_BYTES) as *const u64 }; + let completed = unsafe { core::ptr::read_volatile(ptr) }; + if completed > self.last_signaled_seqno { + self.last_signaled_seqno = completed; + } + } + + Ok(()) + } + + pub fn flush(&mut self) -> Result<()> { + self.ensure_initialized()?; + fence(Ordering::SeqCst); + self.sync_from_hw() + } + pub(crate) fn bind_mmio(mmio: &MmioRegion) { MMIO_BASE.store(mmio.as_ptr() as *mut u8, Ordering::Release); MMIO_SIZE.store(mmio.size(), Ordering::Release);