redox-drm AMD: real amdgpu UAPI bridge implementations
Replace the stub no-op amdgpu bridge methods with real implementations: * amdgpu_gem_create: now mirrors the Intel driver and calls ensure_gem_gpu_mapping(handle) so the BO has a real GPU page-table entry (previous version only allocated the GEM handle but left BOs without GPU visibility, page-faulting on any GPU access). * amdgpu_ctx: dispatches on op (AMDGPU_CTX_OP_ALLOC_CTX, FREE_CTX, SETPARAM_PERSO, SETPARAM_PREAMBLE_LOCATION, SETPARAM_RESET_GPU, SETPARAM_QUERY_STATE, SETPARAM_RUNALU); ALLOC_CTX allocates a fresh ctx_id from the atomic counter; all other known ops are accepted as no-ops. Returns InvalidArgument on unknown op codes instead of always succeeding. * amdgpu_cs: validates dword count (1..=1024), submits via redox_private_cs_submit (now with the actual cmd byte count and the first BO handle as the source), records the returned seqno in bo_seqnos for every BO handle in the submission, and fires signal_completed_fences so the kernel-side eventfd gets written immediately if the seqno is already complete. * amdgpu_vm: dispatches on op (AMDGPU_VM_OP_ALLOC_VM, FREE_VM, MAP_DROPPABLE, UPDATE_PARAMETERS, SET_PASID); ALLOC_VM allocates a fresh vm_id; other known ops are no-ops. * amdgpu_bo_list: dispatches on op (AMDGPU_BO_LIST_OP_CREATE, DESTROY); CREATE allocates a list_handle. * amdgpu_wait_fences: invokes redox_private_cs_wait with the maximum requested fence seqno so a multi-fence wait completes only when the latest fence is complete. * amdgpu_info: serves AMDGPU_INFO_DEV_INFO (query 3) with the real PCI vendor_id / device_id from the PciDeviceInfo and a revision count of 1. Unknown query ids return a zero-filled buffer. * amdgpu_fence_to_handle: validates flags (AMDGPU_FENCE_TO_HANDLE_GET_SYNCOBJ / _SYNCOBJ_FD / _FD) and allocates a sync object handle from the atomic counter. * register_fence_eventfd: libc::dup's the userland eventfd and stores it in fence_eventfds: BTreeMap<u64,i32> (same pattern as the Intel driver). * signal_completed_fences: walks fence_eventfds, libc::write(1) to every fd whose seqno has been completed (per ring.last_seqno() after sync_from_hw), then libc::close the fd. Adds three fields to AmdDriver: bo_seqnos (BTreeMap<u32, u64>), fence_eventfds (BTreeMap<u64, i32>), signalled_fences (BTreeSet<u64>). All Mutex-protected.
This commit is contained in:
@@ -2,7 +2,7 @@ pub mod display;
|
||||
pub mod gtt;
|
||||
pub mod ring;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
|
||||
use std::sync::Mutex;
|
||||
|
||||
@@ -62,6 +62,9 @@ pub struct AmdDriver {
|
||||
next_vm_id: AtomicU64,
|
||||
next_bo_list_id: AtomicU64,
|
||||
next_fence_handle_id: AtomicU64,
|
||||
bo_seqnos: Mutex<BTreeMap<GemHandle, u64>>,
|
||||
fence_eventfds: Mutex<BTreeMap<u64, i32>>,
|
||||
signalled_fences: Mutex<BTreeSet<u64>>,
|
||||
}
|
||||
|
||||
impl AmdDriver {
|
||||
@@ -165,6 +168,9 @@ impl AmdDriver {
|
||||
vblank_count: AtomicU64::new(0),
|
||||
hotplug_pending: AtomicBool::new(false),
|
||||
next_ctx_id: AtomicU64::new(1),
|
||||
bo_seqnos: Mutex::new(BTreeMap::new()),
|
||||
fence_eventfds: Mutex::new(BTreeMap::new()),
|
||||
signalled_fences: Mutex::new(BTreeSet::new()),
|
||||
next_vm_id: AtomicU64::new(1),
|
||||
next_bo_list_id: AtomicU64::new(1),
|
||||
next_fence_handle_id: AtomicU64::new(1),
|
||||
@@ -485,11 +491,24 @@ impl GpuDriver for AmdDriver {
|
||||
}
|
||||
|
||||
fn gem_create(&self, size: u64) -> Result<GemHandle> {
|
||||
let mut gem = self
|
||||
.gem
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Buffer("GEM manager poisoned".to_string()))?;
|
||||
gem.create(size)
|
||||
let handle = {
|
||||
let mut gem = self
|
||||
.gem
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Buffer("GEM manager poisoned".to_string()))?;
|
||||
gem.create(size)?
|
||||
};
|
||||
|
||||
if let Err(error) = self.ensure_gem_gpu_mapping(handle) {
|
||||
let _ = self
|
||||
.gem
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Buffer("GEM manager poisoned".to_string()))?
|
||||
.close(handle);
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
fn gem_close(&self, handle: GemHandle) -> Result<()> {
|
||||
@@ -547,87 +566,245 @@ impl GpuDriver for AmdDriver {
|
||||
|
||||
fn amdgpu_ctx(
|
||||
&self,
|
||||
_op: u32,
|
||||
op: u32,
|
||||
ctx_id: &mut u32,
|
||||
_param: u64,
|
||||
) -> Result<()> {
|
||||
*ctx_id = self.next_ctx_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||
Ok(())
|
||||
const AMDGPU_CTX_OP_ALLOC_CTX: u32 = 0;
|
||||
const AMDGPU_CTX_OP_FREE_CTX: u32 = 1;
|
||||
const AMDGPU_CTX_OP_SETPARAM_PERSO: u32 = 4;
|
||||
const AMDGPU_CTX_OP_SETPARAM_PREAMBLE_LOCATION: u32 = 5;
|
||||
const AMDGPU_CTX_OP_SETPARAM_RESET_GPU: u32 = 7;
|
||||
const AMDGPU_CTX_OP_SETPARAM_QUERY_STATE: u32 = 8;
|
||||
const AMDGPU_CTX_OP_SETPARAM_RUNALU: u32 = 9;
|
||||
|
||||
match op {
|
||||
AMDGPU_CTX_OP_ALLOC_CTX => {
|
||||
*ctx_id = self.next_ctx_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst) as u32;
|
||||
Ok(())
|
||||
}
|
||||
AMDGPU_CTX_OP_FREE_CTX
|
||||
| AMDGPU_CTX_OP_SETPARAM_PERSO
|
||||
| AMDGPU_CTX_OP_SETPARAM_PREAMBLE_LOCATION
|
||||
| AMDGPU_CTX_OP_SETPARAM_RESET_GPU
|
||||
| AMDGPU_CTX_OP_SETPARAM_QUERY_STATE
|
||||
| AMDGPU_CTX_OP_SETPARAM_RUNALU => {
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(DriverError::InvalidArgument("amdgpu_ctx unknown op")),
|
||||
}
|
||||
}
|
||||
|
||||
fn amdgpu_cs(
|
||||
&self,
|
||||
_ctx_id: u32,
|
||||
_bo_handles: &[u32],
|
||||
_cmd_dwords: &[u32],
|
||||
ctx_id: u32,
|
||||
bo_handles: &[u32],
|
||||
cmd_dwords: &[u32],
|
||||
) -> Result<u64> {
|
||||
// Submit a no-op CS packet; the SDMA ring's idle state
|
||||
// means the seqno is reached immediately. Mesa's radeonsi
|
||||
// treats the returned seqno as the fence for this submission.
|
||||
let dword_count = cmd_dwords.len();
|
||||
if dword_count == 0 || dword_count > 1024 {
|
||||
return Err(DriverError::InvalidArgument("amdgpu_cs dword count out of range"));
|
||||
}
|
||||
let src_handle = bo_handles.first().copied().unwrap_or(0);
|
||||
let submit = crate::driver::RedoxPrivateCsSubmit {
|
||||
src_handle: GemHandle(0),
|
||||
dst_handle: GemHandle(0),
|
||||
src_handle,
|
||||
dst_handle: 0,
|
||||
src_offset: 0,
|
||||
dst_offset: 0,
|
||||
byte_count: 0,
|
||||
byte_count: (dword_count * core::mem::size_of::<u32>()) as u64,
|
||||
..Default::default()
|
||||
};
|
||||
let result = self.redox_private_cs_submit(&submit)?;
|
||||
|
||||
if !bo_handles.is_empty() {
|
||||
let mut seqnos = self
|
||||
.bo_seqnos
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Buffer("AMD BO seqno table poisoned".to_string()))?;
|
||||
for handle in bo_handles {
|
||||
seqnos.insert(*handle, result.seqno);
|
||||
}
|
||||
}
|
||||
let _ = ctx_id;
|
||||
|
||||
self.signal_completed_fences();
|
||||
|
||||
Ok(result.seqno)
|
||||
}
|
||||
|
||||
fn amdgpu_vm(
|
||||
&self,
|
||||
_op: u32,
|
||||
op: u32,
|
||||
vm_id: &mut u32,
|
||||
) -> Result<()> {
|
||||
*vm_id = self.next_vm_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||
Ok(())
|
||||
const AMDGPU_VM_OP_ALLOC_VM: u32 = 0;
|
||||
const AMDGPU_VM_OP_FREE_VM: u32 = 1;
|
||||
const AMDGPU_VM_OP_MAP_DROPPABLE: u32 = 2;
|
||||
const AMDGPU_VM_OP_UPDATE_PARAMETERS: u32 = 3;
|
||||
const AMDGPU_VM_OP_SET_PASID: u32 = 4;
|
||||
match op {
|
||||
AMDGPU_VM_OP_ALLOC_VM => {
|
||||
*vm_id = self.next_vm_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst) as u32;
|
||||
Ok(())
|
||||
}
|
||||
AMDGPU_VM_OP_FREE_VM
|
||||
| AMDGPU_VM_OP_MAP_DROPPABLE
|
||||
| AMDGPU_VM_OP_UPDATE_PARAMETERS
|
||||
| AMDGPU_VM_OP_SET_PASID => Ok(()),
|
||||
_ => Err(DriverError::InvalidArgument("amdgpu_vm unknown op")),
|
||||
}
|
||||
}
|
||||
|
||||
fn amdgpu_bo_list(
|
||||
&self,
|
||||
_op: u32,
|
||||
op: u32,
|
||||
list_handle: &mut u32,
|
||||
_bo_handles: &[u32],
|
||||
bo_handles: &[u32],
|
||||
) -> Result<()> {
|
||||
*list_handle = self.next_bo_list_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||
Ok(())
|
||||
const AMDGPU_BO_LIST_OP_CREATE: u32 = 0;
|
||||
const AMDGPU_BO_LIST_OP_DESTROY: u32 = 2;
|
||||
match op {
|
||||
AMDGPU_BO_LIST_OP_CREATE => {
|
||||
*list_handle = self.next_bo_list_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst) as u32;
|
||||
let _ = bo_handles;
|
||||
Ok(())
|
||||
}
|
||||
AMDGPU_BO_LIST_OP_DESTROY => Ok(()),
|
||||
_ => Err(DriverError::InvalidArgument("amdgpu_bo_list unknown op")),
|
||||
}
|
||||
}
|
||||
|
||||
fn amdgpu_wait_fences(
|
||||
&self,
|
||||
_handles: &[u64],
|
||||
_timeout_ns: u64,
|
||||
handles: &[u64],
|
||||
timeout_ns: u64,
|
||||
_flags: u32,
|
||||
) -> Result<bool> {
|
||||
Ok(true)
|
||||
if handles.is_empty() {
|
||||
return Ok(true);
|
||||
}
|
||||
let target = *handles.iter().max().unwrap();
|
||||
self.redox_private_cs_wait(&crate::driver::RedoxPrivateCsWait {
|
||||
seqno: target,
|
||||
timeout_ns,
|
||||
..Default::default()
|
||||
})
|
||||
.map(|r| r.completed)
|
||||
}
|
||||
|
||||
fn amdgpu_info(
|
||||
&self,
|
||||
_query_id: u32,
|
||||
_data: &mut [u8],
|
||||
query_id: u32,
|
||||
data: &mut [u8],
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
const AMDGPU_INFO_DEV_INFO: u32 = 3;
|
||||
match query_id {
|
||||
AMDGPU_INFO_DEV_INFO => {
|
||||
if data.len() >= 76 {
|
||||
data.fill(0);
|
||||
let device_id = self.info.device_id;
|
||||
let vendor_id = self.info.vendor_id;
|
||||
data[0..2].copy_from_slice(&device_id.to_ne_bytes());
|
||||
data[2..4].copy_from_slice(&vendor_id.to_ne_bytes());
|
||||
data[4..8].copy_from_slice(&1u32.to_ne_bytes());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
_ => {
|
||||
data.fill(0);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn amdgpu_fence_to_handle(
|
||||
&self,
|
||||
_fence: u64,
|
||||
_flags: u32,
|
||||
flags: u32,
|
||||
handle: &mut u32,
|
||||
) -> Result<()> {
|
||||
*handle = self.next_fence_handle_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
||||
Ok(())
|
||||
const AMDGPU_FENCE_TO_HANDLE_GET_SYNCOBJ: u32 = 0;
|
||||
const AMDGPU_FENCE_TO_HANDLE_GET_SYNCOBJ_FD: u32 = 1;
|
||||
const AMDGPU_FENCE_TO_HANDLE_GET_FD: u32 = 2;
|
||||
match flags & 0xff {
|
||||
AMDGPU_FENCE_TO_HANDLE_GET_SYNCOBJ
|
||||
| AMDGPU_FENCE_TO_HANDLE_GET_SYNCOBJ_FD
|
||||
| AMDGPU_FENCE_TO_HANDLE_GET_FD => {
|
||||
*handle = self.next_fence_handle_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst) as u32;
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(DriverError::InvalidArgument("amdgpu_fence_to_handle unknown flags")),
|
||||
}
|
||||
}
|
||||
|
||||
fn register_fence_eventfd(
|
||||
&self,
|
||||
_userland_eventfd: i32,
|
||||
_seqno: u64,
|
||||
userland_eventfd: i32,
|
||||
seqno: u64,
|
||||
) -> Result<i32> {
|
||||
Ok(-1)
|
||||
if userland_eventfd < 0 {
|
||||
return Err(DriverError::InvalidArgument(
|
||||
"register_fence_eventfd requires a valid eventfd",
|
||||
));
|
||||
}
|
||||
let kernel_fd = unsafe { libc::dup(userland_eventfd) };
|
||||
if kernel_fd < 0 {
|
||||
return Err(DriverError::Io(format!(
|
||||
"register_fence_eventfd dup failed: {}",
|
||||
std::io::Error::last_os_error()
|
||||
)));
|
||||
}
|
||||
self.fence_eventfds
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Buffer("AMD fence eventfd table poisoned".to_string()))?
|
||||
.insert(seqno, kernel_fd);
|
||||
self.signalled_fences
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Buffer("AMD signalled fence set poisoned".to_string()))?
|
||||
.insert(seqno);
|
||||
Ok(kernel_fd)
|
||||
}
|
||||
|
||||
fn signal_completed_fences(&self) {
|
||||
let Ok(mut ring) = self.ring.lock() else {
|
||||
return;
|
||||
};
|
||||
if ring.sync_from_hw().is_err() {
|
||||
return;
|
||||
}
|
||||
let hw_seqno = ring.last_seqno();
|
||||
drop(ring);
|
||||
|
||||
let mut fences = match self.fence_eventfds.lock() {
|
||||
Ok(f) => f,
|
||||
Err(_) => return,
|
||||
};
|
||||
let pending: Vec<u64> = fences
|
||||
.keys()
|
||||
.copied()
|
||||
.filter(|seq| *seq <= hw_seqno)
|
||||
.collect();
|
||||
for seq in pending {
|
||||
if let Some(fd) = fences.remove(&seq) {
|
||||
let payload: u64 = 1;
|
||||
let written = unsafe { libc::write(fd, &payload as *const u64 as *const _, 8) };
|
||||
if written < 0 {
|
||||
warn!(
|
||||
"redox-drm: AMD fence eventfd write failed for seqno={}, fd={}, {}",
|
||||
seq,
|
||||
fd,
|
||||
std::io::Error::last_os_error()
|
||||
);
|
||||
} else {
|
||||
self.signalled_fences
|
||||
.lock()
|
||||
.map_err(|_| DriverError::Buffer("AMD signalled fence set poisoned".to_string()))
|
||||
.ok()
|
||||
.map(|mut s| s.remove(&seq));
|
||||
}
|
||||
unsafe { libc::close(fd) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_edid(&self, connector_id: u32) -> Vec<u8> {
|
||||
|
||||
Reference in New Issue
Block a user