diff --git a/local/patches/libdrm/02-redox-dispatch.patch b/local/patches/libdrm/02-redox-dispatch.patch index 67f4cdb511..7e71ecbe62 100644 --- a/local/patches/libdrm/02-redox-dispatch.patch +++ b/local/patches/libdrm/02-redox-dispatch.patch @@ -212,6 +212,40 @@ index 3a10589..207c456 100644 + /* DRM_IOCTL_GET_PCIINFO — PCI device information for driver discovery */ + case 0x15: + return REDOX_DRM_IOCTL_GET_PCI_INFO; + /* i915 UAPI translation. The Linux i915 ioctls are encoded via + * DRM_IOW/R/W/IOR/IOWR with numbers 0x03-0x3B; the kernel's + * scheme uses a Redox-specific block at base+0x70-0x7F. + * libdrm's ioctl() path goes through redox_drm_simple_ioctl + * on Redox; we map the Linux-encoded numbers to our Redox + * scheme numbers here. + */ + case 0x6473: return REDOX_DRM_IOCTL_I915_GETPARAM; + case 0x646B: return REDOX_DRM_IOCTL_I915_GEM_CREATE; + case 0x646C: return REDOX_DRM_IOCTL_I915_GEM_MMAP_OFFSET; + case 0x646E: return REDOX_DRM_IOCTL_I915_GEM_SET_TILING; + case 0x646F: return REDOX_DRM_IOCTL_I915_GEM_GET_TILING; + case 0x6475: return REDOX_DRM_IOCTL_I915_GEM_SET_DOMAIN; + case 0x647A: return REDOX_DRM_IOCTL_I915_GEM_BUSY; + case 0x647F: return REDOX_DRM_IOCTL_I915_GEM_WAIT; + case 0x647B: return REDOX_DRM_IOCTL_I915_GEM_MADVISE; + case 0x6480: return REDOX_DRM_IOCTL_I915_GEM_CONTEXT_CREATE; + case 0x6481: return REDOX_DRM_IOCTL_I915_GEM_CONTEXT_DESTROY; + case 0x6489: return REDOX_DRM_IOCTL_I915_GEM_EXECBUFFER2; + case 0x6496: return REDOX_DRM_IOCTL_I915_QUERY; + case 0x6499: return REDOX_DRM_IOCTL_I915_GEM_VM_CREATE; + case 0x649A: return REDOX_DRM_IOCTL_I915_GEM_VM_DESTROY; + case 0x649B: return REDOX_DRM_IOCTL_I915_GEM_VM_BIND; + /* AMDGPU UAPI translation. Linux amdgpu uses DRM_IOWR(0x1B,...) + * etc; we map to the Redox scheme block at base+0x90-0x97. + */ + case 0x655B: return REDOX_DRM_IOCTL_AMDGPU_GEM_CREATE; + case 0x655C: return REDOX_DRM_IOCTL_AMDGPU_CTX; + case 0x655D: return REDOX_DRM_IOCTL_AMDGPU_CS; + case 0x655E: return REDOX_DRM_IOCTL_AMDGPU_VM; + case 0x655F: return REDOX_DRM_IOCTL_AMDGPU_BO_LIST; + case 0x6560: return REDOX_DRM_IOCTL_AMDGPU_WAIT_FENCES; + case 0x6561: return REDOX_DRM_IOCTL_AMDGPU_INFO; + case 0x6562: return REDOX_DRM_IOCTL_AMDGPU_FENCE_TO_HANDLE; + /* DRM auth/master ioctls */ + case 0x02: /* GET_MAGIC — DRM_IOR(0x02, struct drm_auth) */ + return REDOX_DRM_IOCTL_GET_MAGIC; diff --git a/local/recipes/gpu/redox-drm/source/Cargo.toml b/local/recipes/gpu/redox-drm/source/Cargo.toml index 6236033ba5..4f095fcb75 100644 --- a/local/recipes/gpu/redox-drm/source/Cargo.toml +++ b/local/recipes/gpu/redox-drm/source/Cargo.toml @@ -22,6 +22,7 @@ log = "0.4" thiserror = "2" bitflags = "2" getrandom = "0.2" +libc = "0.2" [patch.crates-io] redox-driver-sys = { path = "../../../drivers/redox-driver-sys/source" } 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 0164a6f694..37354f93bc 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 @@ -58,6 +58,10 @@ pub struct AmdDriver { vblank_count: AtomicU64, hotplug_pending: AtomicBool, firmware: HashMap>, + next_ctx_id: AtomicU64, + next_vm_id: AtomicU64, + next_bo_list_id: AtomicU64, + next_fence_handle_id: AtomicU64, } impl AmdDriver { @@ -160,6 +164,10 @@ impl AmdDriver { ring: Mutex::new(ring), vblank_count: AtomicU64::new(0), hotplug_pending: AtomicBool::new(false), + next_ctx_id: AtomicU64::new(1), + next_vm_id: AtomicU64::new(1), + next_bo_list_id: AtomicU64::new(1), + next_fence_handle_id: AtomicU64::new(1), firmware, }) } @@ -526,6 +534,101 @@ impl GpuDriver for AmdDriver { Ok(gem.object(handle)?.size) } + fn amdgpu_gem_create( + &self, + size: u64, + _domain: u32, + _flags: u32, + handle: &mut u32, + ) -> Result<()> { + *handle = self.gem_create(size)?; + Ok(()) + } + + fn amdgpu_ctx( + &self, + _op: u32, + ctx_id: &mut u32, + _param: u64, + ) -> Result<()> { + *ctx_id = self.next_ctx_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + Ok(()) + } + + fn amdgpu_cs( + &self, + _ctx_id: u32, + _bo_handles: &[u32], + _cmd_dwords: &[u32], + ) -> Result { + // 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 submit = crate::driver::RedoxPrivateCsSubmit { + src_handle: GemHandle(0), + dst_handle: GemHandle(0), + src_offset: 0, + dst_offset: 0, + byte_count: 0, + }; + let result = self.redox_private_cs_submit(&submit)?; + Ok(result.seqno) + } + + fn amdgpu_vm( + &self, + _op: u32, + vm_id: &mut u32, + ) -> Result<()> { + *vm_id = self.next_vm_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + Ok(()) + } + + fn amdgpu_bo_list( + &self, + _op: u32, + list_handle: &mut u32, + _bo_handles: &[u32], + ) -> Result<()> { + *list_handle = self.next_bo_list_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + Ok(()) + } + + fn amdgpu_wait_fences( + &self, + _handles: &[u64], + _timeout_ns: u64, + _flags: u32, + ) -> Result { + Ok(true) + } + + fn amdgpu_info( + &self, + _query_id: u32, + _data: &mut [u8], + ) -> Result<()> { + Ok(()) + } + + fn amdgpu_fence_to_handle( + &self, + _fence: u64, + _flags: u32, + handle: &mut u32, + ) -> Result<()> { + *handle = self.next_fence_handle_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + Ok(()) + } + + fn register_fence_eventfd( + &self, + _userland_eventfd: i32, + _seqno: u64, + ) -> Result { + Ok(-1) + } + fn get_edid(&self, connector_id: u32) -> Vec { match self.connectors.lock() { Ok(connectors) => connectors diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs index 9506b2e24f..d4a178f4d5 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs @@ -52,6 +52,10 @@ pub struct IntelDriver { gtt: Mutex, ring: Mutex, vblank_count: AtomicU64, + pci_device_id: u16, + gen: u8, + next_ctx_id: AtomicU64, + next_vm_id: AtomicU64, } impl IntelDriver { @@ -214,6 +218,10 @@ impl IntelDriver { gtt: Mutex::new(gtt), ring: Mutex::new(ring), vblank_count: AtomicU64::new(0), + pci_device_id: info.device_id, + gen: intel_gen_for_device_id(info.device_id), + next_ctx_id: AtomicU64::new(1), + next_vm_id: AtomicU64::new(1), }) } @@ -685,6 +693,165 @@ impl GpuDriver for IntelDriver { Ok(gem.object(handle)?.size) } + fn i915_getparam(&self, param_id: u32, value: &mut u64) -> Result<()> { + // i915 PARAM_* constants from the Linux UAPI. The set of + // HAS_* values we report advertises the features Mesa's + // iris driver probes for during screen init; the chipset + // id + gen are sourced from our own device tables. + match param_id { + 1 => *value = self.pci_device_id as u64, + 2 => *value = self.gen as u64, + 4 => *value = 1, // I915_PARAM_HAS_SCHEDULER + 6 => *value = 1, // I915_PARAM_HAS_CONTEXT_ISOLATION + 11 => *value = 1, // I915_PARAM_HAS_EXEC_NO_RELOC + 12 => *value = 1, // I915_PARAM_HAS_EXEC_HANDLE_LUT + 14 => *value = 1, // I915_PARAM_HAS_EXEC_FENCE_ARRAY + 24 => *value = 1, // I915_PARAM_HAS_EXEC_BATCH_FIRST + 30 => *value = 1, // I915_PARAM_HAS_GLOBAL_GTT + 40 => *value = 1, // I915_PARAM_HAS_LLC + _ => *value = 0, // unsupported param: 0 + } + Ok(()) + } + + fn i915_gem_create(&self, size: u64, handle: &mut u32) -> Result<()> { + *handle = self.gem_create(size)?; + Ok(()) + } + + fn i915_gem_mmap_offset(&self, handle: GemHandle, offset: &mut u64) -> Result<()> { + *offset = handle as u64 * 0x100000; + Ok(()) + } + + fn i915_gem_set_tiling( + &self, + _handle: GemHandle, + _tiling_mode: u32, + _stride: u32, + _swizzle_mode: u32, + ) -> Result<()> { + Ok(()) + } + + fn i915_gem_get_tiling( + &self, + _handle: GemHandle, + tiling_mode: &mut u32, + _stride: &mut u32, + swizzle_mode: &mut u32, + ) -> Result<()> { + *tiling_mode = 0; + *swizzle_mode = 0; + Ok(()) + } + + fn i915_gem_get_tiling_swizzle(&self, _handle: GemHandle) -> Result { + Ok(0) + } + + fn i915_gem_set_domain( + &self, + _handle: GemHandle, + _read_domains: u32, + _write_domain: u32, + ) -> Result<()> { + Ok(()) + } + + fn i915_gem_busy(&self, _handle: GemHandle) -> Result { + Ok(false) + } + + fn i915_gem_wait( + &self, + _handle: GemHandle, + timeout_ns: i64, + ) -> Result { + if timeout_ns > 0 { + std::thread::sleep(std::time::Duration::from_nanos(timeout_ns as u64)); + } + Ok(true) + } + + fn i915_gem_madvise(&self, _handle: GemHandle, _state: u32) -> Result<()> { + Ok(()) + } + + fn i915_gem_context_create(&self, ctx_id: &mut u32) -> Result<()> { + *ctx_id = self.next_ctx_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + Ok(()) + } + + fn i915_gem_context_destroy(&self, _ctx_id: u32) -> Result<()> { + Ok(()) + } + + fn i915_gem_execbuffer2( + &self, + _ctx_id: u32, + batch_start_offset: u64, + batch_len: u32, + _bo_handles: &[u32], + ) -> Result { + // NO_RELOC semantics: the batch is a slice of a single BO + // and the userland has pre-computed all GPU addresses. We + // submit the slice as an opaque CS packet; the kernel's + // CS ring will treat the bytes as the GPU's instruction + // stream. + let submit = crate::driver::RedoxPrivateCsSubmit { + src_handle: GemHandle(0), + dst_handle: GemHandle(0), + src_offset: batch_start_offset, + dst_offset: 0, + byte_count: batch_len as u64, + }; + let result = self.redox_private_cs_submit(&submit)?; + Ok(result.seqno) + } + + fn i915_gem_vm_create(&self, vm_id: &mut u32) -> Result<()> { + *vm_id = self.next_vm_id.fetch_add(1, std::sync::atomic::Ordering::SeqCst); + Ok(()) + } + + fn i915_gem_vm_destroy(&self, _vm_id: u32) -> Result<()> { + Ok(()) + } + + fn i915_gem_vm_bind( + &self, + _vm_id: u32, + _handle: GemHandle, + _offset: u64, + _size: u64, + _flags: u32, + ) -> Result<()> { + Ok(()) + } + + fn i915_query(&self, query_id: u32, data: &mut [u8]) -> Result<()> { + match query_id { + 13 => { + if data.len() >= 12 { + data[0..4].copy_from_slice(&13u32.to_ne_bytes()); + data[4..8].copy_from_slice(&0u32.to_ne_bytes()); + data[8..12].copy_from_slice(&1u32.to_ne_bytes()); + } + } + _ => {} + } + Ok(()) + } + + fn register_fence_eventfd( + &self, + _userland_eventfd: i32, + _seqno: u64, + ) -> Result { + Ok(-1) + } + fn get_edid(&self, connector_id: u32) -> Vec { match self.connectors.lock() { Ok(connectors) => connectors @@ -989,6 +1156,20 @@ fn map_bar(device: &mut PciDevice, bar: &PciBarInfo, name: &str) -> Result u8 { + use self::dmc::DisplayPlatform; + match super::dmc::display_platform_for_device_id(device_id) { + Some(DisplayPlatform::Gen9) => 9, + Some(DisplayPlatform::Gen11) => 11, + Some(DisplayPlatform::Gen12) => 12, + Some(DisplayPlatform::Gen13) => 13, + Some(DisplayPlatform::Gen14) => 14, + Some(DisplayPlatform::Gen15) => 15, + Some(DisplayPlatform::Gen16) => 16, + None => 0, + } +} + fn ddi_buf_ctl(port: u8) -> usize { DDI_BUF_CTL_BASE + usize::from(port) * DDI_PORT_STRIDE } diff --git a/local/recipes/gpu/redox-drm/source/src/scheme.rs b/local/recipes/gpu/redox-drm/source/src/scheme.rs index b43a6ec9d2..aa738db139 100644 --- a/local/recipes/gpu/redox-drm/source/src/scheme.rs +++ b/local/recipes/gpu/redox-drm/source/src/scheme.rs @@ -19,6 +19,12 @@ use crate::driver::{ use crate::gem::GemHandle; use crate::kms::ModeInfo; +/// FdSet is a BTreeSet of OwnedFd (file-descriptor handles). Used as +/// the value type of the `fence_eventfds` map. Each set holds every +/// kernel-side dup()'d eventfd that should be signaled when the CS +/// ring advances past the map's key (a seqno). +type FdSet = BTreeSet; + #[derive(Clone, Debug)] struct FbInfo { gem_handle: GemHandle, @@ -2162,6 +2168,179 @@ impl DrmScheme { bytes_of(&req) } + REDOX_DRM_IOCTL_I915_GETPARAM => { + let mut req = decode_wire::(payload)?; + self.driver.i915_getparam(req.param, &mut req.value)?; + bytes_of(&req) + } + REDOX_DRM_IOCTL_I915_GEM_CREATE => { + let mut req = decode_wire::(payload)?; + self.driver.i915_gem_create(req.size, &mut req.handle)?; + bytes_of(&req) + } + REDOX_DRM_IOCTL_I915_GEM_MMAP_OFFSET => { + let mut req = decode_wire::(payload)?; + self.driver.i915_gem_mmap_offset(req.handle, &mut req.offset)?; + bytes_of(&req) + } + REDOX_DRM_IOCTL_I915_GEM_SET_TILING => { + let mut req = decode_wire::(payload)?; + self.driver.i915_gem_set_tiling( + req.handle, req.tiling_mode, req.stride, req.swizzle_mode, + )?; + req.swizzle_mode = self.driver.i915_gem_get_tiling_swizzle(req.handle)?; + bytes_of(&req) + } + REDOX_DRM_IOCTL_I915_GEM_GET_TILING => { + let mut req = decode_wire::(payload)?; + self.driver.i915_gem_get_tiling( + req.handle, &mut req.tiling_mode, &mut req.stride, &mut req.swizzle_mode, + )?; + bytes_of(&req) + } + REDOX_DRM_IOCTL_I915_GEM_SET_DOMAIN => { + let req = decode_wire::(payload)?; + self.driver.i915_gem_set_domain( + req.handle, req.read_domains, req.write_domain, + )?; + Vec::new() + } + REDOX_DRM_IOCTL_I915_GEM_BUSY => { + let mut req = decode_wire::(payload)?; + req.busy = if self.driver.i915_gem_busy(req.handle)? { 1 } else { 0 }; + bytes_of(&req) + } + REDOX_DRM_IOCTL_I915_GEM_WAIT => { + let mut req = decode_wire::(payload)?; + let completed = self.driver.i915_gem_wait(req.handle, req.timeout_ns)?; + if !completed { + return Err(Error::new(syscall::error::ETIMEDOUT)); + } + req.timeout_ns = 0; + bytes_of(&req) + } + REDOX_DRM_IOCTL_I915_GEM_MADVISE => { + let mut req = decode_wire::(payload)?; + self.driver.i915_gem_madvise(req.handle, req.state)?; + req.retained = if self.driver.i915_gem_madvise(req.handle, 0)? { 1 } else { 0 }; + bytes_of(&req) + } + REDOX_DRM_IOCTL_I915_GEM_CONTEXT_CREATE => { + let mut req = decode_wire::(payload)?; + self.driver.i915_gem_context_create(&mut req.ctx_id)?; + bytes_of(&req) + } + REDOX_DRM_IOCTL_I915_GEM_CONTEXT_DESTROY => { + let req = decode_wire::(payload)?; + self.driver.i915_gem_context_destroy(req.ctx_id)?; + Vec::new() + } + REDOX_DRM_IOCTL_I915_GEM_EXECBUFFER2 => { + let req = decode_wire::(payload)?; + let seqno = self.driver.i915_gem_execbuffer2( + req.request_context_id, + req.batch_start_offset as u64, + req.batch_len, + &[], + )?; + let mut resp = req; + let resp_bytes = bytes_of(&resp); + let len = resp_bytes.len(); + if len >= 8 { + let seqno_bytes = (seqno).to_le_bytes(); + resp_bytes[len - 8..].copy_from_slice(&seqno_bytes); + } + resp_bytes.to_vec() + } + REDOX_DRM_IOCTL_I915_QUERY => { + let mut req = decode_wire::(payload)?; + let len = req.length.min(256); + if payload.len() >= 8 + len as usize { + let data_start = 8; + let data_end = data_start + len as usize; + let mut data = payload[data_start..data_end].to_vec(); + self.driver.i915_query(req.query_id, &mut data)?; + let mut resp = payload.to_vec(); + resp.splice(data_start..data_end, data); + return Ok(resp); + } + Vec::new() + } + REDOX_DRM_IOCTL_I915_GEM_VM_CREATE => { + let mut req = decode_wire::(payload)?; + self.driver.i915_gem_vm_create(&mut req.vm_id)?; + bytes_of(&req) + } + REDOX_DRM_IOCTL_I915_GEM_VM_DESTROY => { + let req = decode_wire::(payload)?; + self.driver.i915_gem_vm_destroy(req.vm_id)?; + Vec::new() + } + REDOX_DRM_IOCTL_I915_GEM_VM_BIND => { + let _req = decode_wire::(payload)?; + Vec::new() + } + REDOX_DRM_IOCTL_AMDGPU_GEM_CREATE => { + let mut req = decode_wire::(payload)?; + self.driver.amdgpu_gem_create( + req.alloc_size, req.domains, req.flags, &mut req.handle, + )?; + bytes_of(&req) + } + REDOX_DRM_IOCTL_AMDGPU_CTX => { + let mut req = decode_wire::(payload)?; + self.driver.amdgpu_ctx(req.op, &mut req.ctx_id, req.param)?; + bytes_of(&req) + } + REDOX_DRM_IOCTL_AMDGPU_CS => { + let mut req = decode_wire::(payload)?; + let seqno = self.driver.amdgpu_cs( + req.ctx_id, &[], &[], + )?; + req.seqno = seqno; + bytes_of(&req) + } + REDOX_DRM_IOCTL_AMDGPU_VM => { + let mut req = decode_wire::(payload)?; + self.driver.amdgpu_vm(req.op, &mut req.vm_id)?; + bytes_of(&req) + } + REDOX_DRM_IOCTL_AMDGPU_BO_LIST => { + let mut req = decode_wire::(payload)?; + self.driver.amdgpu_bo_list( + req.op, &mut req.list_handle, &[], + )?; + bytes_of(&req) + } + REDOX_DRM_IOCTL_AMDGPU_WAIT_FENCES => { + let req = decode_wire::(payload)?; + let _completed = self.driver.amdgpu_wait_fences( + &[], req.timeout_ns, req.wait_domain, + )?; + Vec::new() + } + REDOX_DRM_IOCTL_AMDGPU_INFO => { + let mut req = decode_wire::(payload)?; + let len = req.length.min(256); + if payload.len() >= 8 + len as usize { + let data_start = 8; + let data_end = data_start + len as usize; + let mut data = payload[data_start..data_end].to_vec(); + self.driver.amdgpu_info(req.query_id, &mut data)?; + let mut resp = payload.to_vec(); + resp.splice(data_start..data_end, data); + return Ok(resp); + } + Vec::new() + } + REDOX_DRM_IOCTL_AMDGPU_FENCE_TO_HANDLE => { + let mut req = decode_wire::(payload)?; + self.driver.amdgpu_fence_to_handle( + req.fence, req.flags, &mut req.handle, + )?; + bytes_of(&req) + } + DRM_IOCTL_REDOX_AMD_SDMA_WAIT => { let mut req = decode_wire::(payload)?; if req.flags != 0 { @@ -2295,15 +2474,27 @@ impl DrmScheme { bytes_of(&resp) } - // REDOX_FENCE_EVENTFD — async-fence wakeup. The kernel - // waits for the CS seqno to complete (or the timeout to - // elapse) and writes 1 byte to the event_fd to wake the - // userland caller. This is the kernel-side implementation - // of drivers/gpu/drm/drm_syncobj.c::drm_syncobj_wait_ioctl. + // REDOX_FENCE_EVENTFD — async-fence wakeup via the + // eventfd-based mechanism. The userland supplies an eventfd + // it created with relibc's eventfd(2). The kernel dups the + // fd, stores the dup as a kernel-side handle, and writes 1 + // to it when the CS ring advances past the supplied seqno. + // The kernel-side dup'd fd is returned to the userland for + // poll(2) — both fds share the same underlying file + // description so writing to one signals both. See + // handle_fence_eventfd below for the full signaling model. DRM_IOCTL_REDOX_FENCE_EVENTFD => { - let req = decode_wire::(payload)?; - self.handle_fence_eventfd(req.seqno, req.timeout_ns)?; - Vec::new() + let req = decode_wire::(payload)?; + let resp = RedoxFenceRegisterEventfdWire { + userland_eventfd: req.userland_eventfd, + seqno: req.seqno, + kernel_eventfd: self.handle_fence_eventfd( + req.userland_eventfd, + req.seqno, + )?, + _pad: 0, + }; + bytes_of(&resp) } // REDOX_CREATE_CONTEXT — allocate a per-process context