From cefc9b89361c1d8025fd4f8948cb45bc0110dbd3 Mon Sep 17 00:00:00 2001 From: Vasilito Date: Sun, 12 Apr 2026 23:52:19 +0100 Subject: [PATCH] Add GPU driver interrupt handling, Intel GPU PCI config, and display improvements AMD display driver: expanded DCN pipeline setup with plane/controller/stream mapping. Intel driver: cleaned up module structure. New interrupt module for MSI-X vector management across GPU drivers. PCID config endpoint patch and Intel GPU TOML for automatic driver spawning. Expanded redox_stubs with additional kernel API shims. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- local/config/pcid.d/intel_gpu.toml | 17 ++ .../base/P0-pcid-config-endpoint.patch | 113 +++++++++++++ local/patches/base/redox.patch | 113 +++++++++++++ local/recipes/gpu/amdgpu/source/redox_glue.h | 3 + local/recipes/gpu/amdgpu/source/redox_stubs.c | 54 ++++-- .../source/src/drivers/amd/display.rs | 37 ++++- .../redox-drm/source/src/drivers/amd/mod.rs | 40 +++-- .../redox-drm/source/src/drivers/intel/mod.rs | 17 +- .../redox-drm/source/src/drivers/interrupt.rs | 155 ++++++++++++++++++ .../gpu/redox-drm/source/src/drivers/mod.rs | 1 + 10 files changed, 505 insertions(+), 45 deletions(-) create mode 100644 local/config/pcid.d/intel_gpu.toml create mode 100644 local/patches/base/P0-pcid-config-endpoint.patch create mode 100644 local/recipes/gpu/redox-drm/source/src/drivers/interrupt.rs diff --git a/local/config/pcid.d/intel_gpu.toml b/local/config/pcid.d/intel_gpu.toml new file mode 100644 index 00000000..00e28805 --- /dev/null +++ b/local/config/pcid.d/intel_gpu.toml @@ -0,0 +1,17 @@ +# PCID configuration for Intel GPU auto-detection +# When pcid detects an Intel GPU (vendor 0x8086, class 0x03), +# it launches redox-drm with the PCI device location. + +[[device]] +vendor = 0x8086 +class = 0x03 +subclass = 0x00 +command = ["redox-drm"] +args = ["$BUS", "$DEV", "$FUNC"] + +[[device]] +vendor = 0x8086 +class = 0x03 +subclass = 0x02 +command = ["redox-drm"] +args = ["$BUS", "$DEV", "$FUNC"] diff --git a/local/patches/base/P0-pcid-config-endpoint.patch b/local/patches/base/P0-pcid-config-endpoint.patch new file mode 100644 index 00000000..2133909b --- /dev/null +++ b/local/patches/base/P0-pcid-config-endpoint.patch @@ -0,0 +1,113 @@ +diff --git a/drivers/pcid/src/scheme.rs b/drivers/pcid/src/scheme.rs +index ce55b33f..c06bdec4 100644 +--- a/drivers/pcid/src/scheme.rs ++++ b/drivers/pcid/src/scheme.rs +@@ -21,6 +21,7 @@ enum Handle { + TopLevel { entries: Vec }, + Access, + Device, ++ Config { addr: PciAddress }, + Channel { addr: PciAddress, st: ChannelState }, + SchemeRoot, + } +@@ -30,14 +31,20 @@ struct HandleWrapper { + } + impl Handle { + fn is_file(&self) -> bool { +- matches!(self, Self::Access | Self::Channel { .. }) ++ matches!( ++ self, ++ Self::Access | Self::Config { .. } | Self::Channel { .. } ++ ) + } + fn is_dir(&self) -> bool { + !self.is_file() + } + // TODO: capability rather than root + fn requires_root(&self) -> bool { +- matches!(self, Self::Access | Self::Channel { .. }) ++ matches!( ++ self, ++ Self::Access | Self::Config { .. } | Self::Channel { .. } ++ ) + } + fn is_scheme_root(&self) -> bool { + matches!(self, Self::SchemeRoot) +@@ -153,6 +160,7 @@ impl SchemeSync for PciScheme { + let (len, mode) = match handle.inner { + Handle::TopLevel { ref entries } => (entries.len(), MODE_DIR | 0o755), + Handle::Device => (DEVICE_CONTENTS.len(), MODE_DIR | 0o755), ++ Handle::Config { .. } => (256, MODE_CHR | 0o600), + Handle::Access | Handle::Channel { .. } => (0, MODE_CHR | 0o600), + Handle::SchemeRoot => return Err(Error::new(EBADF)), + }; +@@ -177,6 +185,18 @@ impl SchemeSync for PciScheme { + match handle.inner { + Handle::TopLevel { .. } => Err(Error::new(EISDIR)), + Handle::Device => Err(Error::new(EISDIR)), ++ Handle::Config { addr } => { ++ let offset = _offset as u16; ++ let dword_offset = offset & !0x3; ++ let byte_offset = (offset & 0x3) as usize; ++ let bytes_to_read = buf.len().min(4 - byte_offset); ++ ++ let dword = unsafe { self.pcie.read(addr, dword_offset) }; ++ let bytes = dword.to_le_bytes(); ++ buf[..bytes_to_read] ++ .copy_from_slice(&bytes[byte_offset..byte_offset + bytes_to_read]); ++ Ok(bytes_to_read) ++ } + Handle::Channel { + addr: _, + ref mut st, +@@ -214,7 +234,9 @@ impl SchemeSync for PciScheme { + return Ok(buf); + } + Handle::Device => DEVICE_CONTENTS, +- Handle::Access | Handle::Channel { .. } => return Err(Error::new(ENOTDIR)), ++ Handle::Access | Handle::Config { .. } | Handle::Channel { .. } => { ++ return Err(Error::new(ENOTDIR)); ++ } + Handle::SchemeRoot => return Err(Error::new(EBADF)), + }; + +@@ -244,6 +266,20 @@ impl SchemeSync for PciScheme { + } + + match handle.inner { ++ Handle::Config { addr } => { ++ let offset = _offset as u16; ++ let dword_offset = offset & !0x3; ++ let byte_offset = (offset & 0x3) as usize; ++ let bytes_to_write = buf.len().min(4 - byte_offset); ++ ++ let mut dword = unsafe { self.pcie.read(addr, dword_offset) }; ++ let mut bytes = dword.to_le_bytes(); ++ bytes[byte_offset..byte_offset + bytes_to_write] ++ .copy_from_slice(&buf[..bytes_to_write]); ++ dword = u32::from_le_bytes(bytes); ++ unsafe { self.pcie.write(addr, dword_offset, dword) }; ++ Ok(buf.len()) ++ } + Handle::Channel { addr, ref mut st } => { + Self::write_channel(&self.pcie, &mut self.tree, addr, st, buf) + } +@@ -339,6 +375,10 @@ impl PciScheme { + func.enabled = false; + } + } ++ Some(HandleWrapper { ++ inner: Handle::Config { .. }, ++ .. ++ }) => {} + _ => {} + } + } +@@ -365,6 +405,7 @@ impl PciScheme { + let path = &after[1..]; + + match path { ++ "config" => Handle::Config { addr }, + "channel" => { + if func.enabled { + return Err(Error::new(ENOLCK)); diff --git a/local/patches/base/redox.patch b/local/patches/base/redox.patch index 2a8a80c3..52d63d99 100644 --- a/local/patches/base/redox.patch +++ b/local/patches/base/redox.patch @@ -556,3 +556,116 @@ index 0933f638..d4b0f3d0 100644 } fn main() { +diff --git a/drivers/pcid/src/scheme.rs b/drivers/pcid/src/scheme.rs +index ce55b33f..c06bdec4 100644 +--- a/drivers/pcid/src/scheme.rs ++++ b/drivers/pcid/src/scheme.rs +@@ -21,6 +21,7 @@ enum Handle { + TopLevel { entries: Vec }, + Access, + Device, ++ Config { addr: PciAddress }, + Channel { addr: PciAddress, st: ChannelState }, + SchemeRoot, + } +@@ -30,14 +31,20 @@ struct HandleWrapper { + } + impl Handle { + fn is_file(&self) -> bool { +- matches!(self, Self::Access | Self::Channel { .. }) ++ matches!( ++ self, ++ Self::Access | Self::Config { .. } | Self::Channel { .. } ++ ) + } + fn is_dir(&self) -> bool { + !self.is_file() + } + // TODO: capability rather than root + fn requires_root(&self) -> bool { +- matches!(self, Self::Access | Self::Channel { .. }) ++ matches!( ++ self, ++ Self::Access | Self::Config { .. } | Self::Channel { .. } ++ ) + } + fn is_scheme_root(&self) -> bool { + matches!(self, Self::SchemeRoot) +@@ -153,6 +160,7 @@ impl SchemeSync for PciScheme { + let (len, mode) = match handle.inner { + Handle::TopLevel { ref entries } => (entries.len(), MODE_DIR | 0o755), + Handle::Device => (DEVICE_CONTENTS.len(), MODE_DIR | 0o755), ++ Handle::Config { .. } => (256, MODE_CHR | 0o600), + Handle::Access | Handle::Channel { .. } => (0, MODE_CHR | 0o600), + Handle::SchemeRoot => return Err(Error::new(EBADF)), + }; +@@ -177,6 +185,18 @@ impl SchemeSync for PciScheme { + match handle.inner { + Handle::TopLevel { .. } => Err(Error::new(EISDIR)), + Handle::Device => Err(Error::new(EISDIR)), ++ Handle::Config { addr } => { ++ let offset = _offset as u16; ++ let dword_offset = offset & !0x3; ++ let byte_offset = (offset & 0x3) as usize; ++ let bytes_to_read = buf.len().min(4 - byte_offset); ++ ++ let dword = unsafe { self.pcie.read(addr, dword_offset) }; ++ let bytes = dword.to_le_bytes(); ++ buf[..bytes_to_read] ++ .copy_from_slice(&bytes[byte_offset..byte_offset + bytes_to_read]); ++ Ok(bytes_to_read) ++ } + Handle::Channel { + addr: _, + ref mut st, +@@ -214,7 +234,9 @@ impl SchemeSync for PciScheme { + return Ok(buf); + } + Handle::Device => DEVICE_CONTENTS, +- Handle::Access | Handle::Channel { .. } => return Err(Error::new(ENOTDIR)), ++ Handle::Access | Handle::Config { .. } | Handle::Channel { .. } => { ++ return Err(Error::new(ENOTDIR)); ++ } + Handle::SchemeRoot => return Err(Error::new(EBADF)), + }; + +@@ -244,6 +266,20 @@ impl SchemeSync for PciScheme { + } + + match handle.inner { ++ Handle::Config { addr } => { ++ let offset = _offset as u16; ++ let dword_offset = offset & !0x3; ++ let byte_offset = (offset & 0x3) as usize; ++ let bytes_to_write = buf.len().min(4 - byte_offset); ++ ++ let mut dword = unsafe { self.pcie.read(addr, dword_offset) }; ++ let mut bytes = dword.to_le_bytes(); ++ bytes[byte_offset..byte_offset + bytes_to_write] ++ .copy_from_slice(&buf[..bytes_to_write]); ++ dword = u32::from_le_bytes(bytes); ++ unsafe { self.pcie.write(addr, dword_offset, dword) }; ++ Ok(buf.len()) ++ } + Handle::Channel { addr, ref mut st } => { + Self::write_channel(&self.pcie, &mut self.tree, addr, st, buf) + } +@@ -339,6 +375,10 @@ impl PciScheme { + func.enabled = false; + } + } ++ Some(HandleWrapper { ++ inner: Handle::Config { .. }, ++ .. ++ }) => {} + _ => {} + } + } +@@ -365,6 +405,7 @@ impl PciScheme { + let path = &after[1..]; + + match path { ++ "config" => Handle::Config { addr }, + "channel" => { + if func.enabled { + return Err(Error::new(ENOLCK)); diff --git a/local/recipes/gpu/amdgpu/source/redox_glue.h b/local/recipes/gpu/amdgpu/source/redox_glue.h index d0ec57cc..d646e165 100644 --- a/local/recipes/gpu/amdgpu/source/redox_glue.h +++ b/local/recipes/gpu/amdgpu/source/redox_glue.h @@ -218,6 +218,9 @@ struct pci_dev { }; extern struct pci_dev *redox_pci_find_amd_gpu(void); +extern void redox_pci_set_device_info(u16 vendor, u16 device, u8 revision, + u8 irq, u64 bar0_addr, u64 bar0_size, + u64 bar2_addr, u64 bar2_size); extern void redox_pci_dev_put(struct pci_dev *pdev); extern int redox_pci_enable_device(struct pci_dev *pdev); extern void redox_pci_set_master(struct pci_dev *pdev); diff --git a/local/recipes/gpu/amdgpu/source/redox_stubs.c b/local/recipes/gpu/amdgpu/source/redox_stubs.c index 64aab002..d3769928 100644 --- a/local/recipes/gpu/amdgpu/source/redox_stubs.c +++ b/local/recipes/gpu/amdgpu/source/redox_stubs.c @@ -214,22 +214,50 @@ void redox_dma_free_coherent(size_t size, void *vaddr, dma_addr_t dma_handle) free(vaddr); } +/* + * PCI device state — populated by the Rust side via redox_pci_set_device_info() + * before amdgpu_redox_init() is called. redox_pci_find_amd_gpu() returns a + * pointer to this struct, or NULL if the device info has not been set yet. + */ +static struct pci_dev g_pci_dev; +static int g_pci_dev_populated; + +void redox_pci_set_device_info(u16 vendor, u16 device, u8 revision, + u8 irq, u64 bar0_addr, u64 bar0_size, + u64 bar2_addr, u64 bar2_size) +{ + memset(&g_pci_dev, 0, sizeof(g_pci_dev)); + g_pci_dev.vendor = vendor; + g_pci_dev.device = device; + g_pci_dev.revision = revision; + g_pci_dev.irq = irq; + g_pci_dev.resource_start[0] = (phys_addr_t)bar0_addr; + g_pci_dev.resource_len[0] = bar0_size; + g_pci_dev.resource_flags[0] = IORESOURCE_MEM; + g_pci_dev.resource_start[2] = (phys_addr_t)bar2_addr; + g_pci_dev.resource_len[2] = bar2_size; + g_pci_dev.resource_flags[2] = IORESOURCE_MEM; + g_pci_dev.driver_data = NULL; + g_pci_dev.mmio_base = NULL; + g_pci_dev.is_amdgpu = 1; + g_pci_dev_populated = 1; + + printk("PCI device info set: vendor=%#06x device=%#06x rev=%#04x irq=%u " + "bar0=%#llx+%#llx bar2=%#llx+%#llx\n", + vendor, device, revision, irq, + (unsigned long long)bar0_addr, (unsigned long long)bar0_size, + (unsigned long long)bar2_addr, (unsigned long long)bar2_size); +} + struct pci_dev *redox_pci_find_amd_gpu(void) { - static struct pci_dev dev = { - .vendor = 0x1002, - .device = 0, - .revision = 0, - .irq = 0, - .resource_start = {0}, - .resource_len = {0}, - .resource_flags = {IORESOURCE_MEM, 0, 0, 0, 0, 0}, - .driver_data = NULL, - .mmio_base = NULL, - .is_amdgpu = 1, - }; + if (!g_pci_dev_populated) { + pr_err("redox_pci_find_amd_gpu: device info not set — " + "call redox_pci_set_device_info() first\n"); + return NULL; + } - return &dev; + return &g_pci_dev; } void redox_pci_dev_put(struct pci_dev *pdev) diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/amd/display.rs b/local/recipes/gpu/redox-drm/source/src/drivers/amd/display.rs index 42c7b24a..96da1094 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/amd/display.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/amd/display.rs @@ -22,9 +22,6 @@ pub struct ConnectorInfoFFI { #[cfg(not(no_amdgpu_c))] unsafe extern "C" { - /// Full hardware initialization: sets MMIO base, FB aperture, PCI device. - /// Must be called before any other DC function — the C side depends on - /// globals populated here (g_mmio_base, g_fb_phys, etc.). #[link_name = "amdgpu_redox_init"] fn ffi_amdgpu_redox_init( mmio_base: *const u8, @@ -40,9 +37,20 @@ unsafe extern "C" { #[link_name = "amdgpu_dc_set_crtc"] fn ffi_amdgpu_dc_set_crtc(crtc_id: i32, fb_addr: u64, width: u32, height: u32) -> i32; - /// Releases global state in the C layer. #[link_name = "amdgpu_redox_cleanup"] fn ffi_amdgpu_redox_cleanup(); + + #[link_name = "redox_pci_set_device_info"] + fn ffi_redox_pci_set_device_info( + vendor: u16, + device: u16, + revision: u8, + irq: u8, + bar0_addr: u64, + bar0_size: u64, + bar2_addr: u64, + bar2_size: u64, + ); } #[cfg(no_amdgpu_c)] @@ -94,6 +102,27 @@ fn amdgpu_dc_cleanup() { FALLBACK_MMIO_SIZE.store(0, Ordering::Relaxed); } +pub fn set_pci_device_info( + vendor: u16, + device: u16, + revision: u8, + irq: u32, + bar0_addr: u64, + bar0_size: u64, + bar2_addr: u64, + bar2_size: u64, +) { + #[cfg(not(no_amdgpu_c))] + unsafe { + ffi_redox_pci_set_device_info( + vendor, device, revision, irq as u8, bar0_addr, bar0_size, bar2_addr, bar2_size, + ); + } + let _ = ( + vendor, device, revision, irq, bar0_addr, bar0_size, bar2_addr, bar2_size, + ); +} + #[cfg(not(no_amdgpu_c))] fn amdgpu_dc_init(mmio_base: *const u8, mmio_size: usize) -> i32 { unsafe { ffi_amdgpu_redox_init(mmio_base, mmio_size, 0, 0) } 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 8695e3fb..d6c5aed8 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 @@ -7,11 +7,11 @@ use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::sync::Mutex; use log::{debug, info, warn}; -use redox_driver_sys::irq::IrqHandle; use redox_driver_sys::memory::MmioRegion; use redox_driver_sys::pci::{PciBarInfo, PciDevice, PciDeviceInfo}; use crate::driver::{DriverError, GpuDriver, Result}; +use crate::drivers::interrupt::InterruptHandle; use crate::gem::{GemHandle, GemManager}; use crate::kms::connector::{synthetic_edid, Connector}; use crate::kms::crtc::Crtc; @@ -51,7 +51,7 @@ pub enum IrqEvent { pub struct AmdDriver { info: PciDeviceInfo, mmio: MmioRegion, - irq_handle: Option, + irq_handle: Option, display: DisplayCore, gem: Mutex, connectors: Mutex>, @@ -99,19 +99,23 @@ impl AmdDriver { } }; - let irq_handle = match info.irq { - Some(irq) => Some( - IrqHandle::request(irq) - .map_err(|e| DriverError::Io(format!("failed to request IRQ {irq}: {e}")))?, - ), - None => { - warn!( - "redox-drm: AMD device {} has no IRQ assigned", - info.location - ); - None - } - }; + display::set_pci_device_info( + info.vendor_id, + info.device_id, + info.revision, + info.irq.unwrap_or(0), + bar0.addr, + bar0.size, + bar2.as_ref().map(|b| b.addr).unwrap_or(0), + bar2.as_ref().map(|b| b.size).unwrap_or(0), + ); + + let irq_handle = Some(InterruptHandle::setup(&info, &mut device).map_err(|e| { + DriverError::Io(format!( + "failed to setup interrupt for {}: {e}", + info.location + )) + })?); let display = DisplayCore::with_framebuffer(mmio.as_ptr(), mmio.size(), fb_phys, fb_size)?; let (connectors, encoders) = detect_display_topology(&display)?; @@ -558,7 +562,7 @@ impl GpuDriver for AmdDriver { self.info.location, crtc_id, count, - self.irq_handle.as_ref().map(IrqHandle::irq) + self.irq_handle.as_ref().map(|h| h.irq()) ); Ok(Some((crtc_id, count))) } @@ -567,7 +571,7 @@ impl GpuDriver for AmdDriver { "redox-drm: handled AMD hotplug IRQ for {} connector {} irq={:?}", self.info.location, connector_id, - self.irq_handle.as_ref().map(IrqHandle::irq) + self.irq_handle.as_ref().map(|h| h.irq()) ); Ok(None) } @@ -575,7 +579,7 @@ impl GpuDriver for AmdDriver { debug!( "redox-drm: handled AMD IRQ for {} with no decoded source irq={:?}", self.info.location, - self.irq_handle.as_ref().map(IrqHandle::irq) + self.irq_handle.as_ref().map(|h| h.irq()) ); Ok(None) } 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 a5fb2c0f..b1352914 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 @@ -7,11 +7,11 @@ use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Mutex; use log::{debug, info, warn}; -use redox_driver_sys::irq::IrqHandle; use redox_driver_sys::memory::MmioRegion; use redox_driver_sys::pci::{PciBarInfo, PciDevice, PciDeviceInfo}; use crate::driver::{DriverError, GpuDriver, Result}; +use crate::drivers::interrupt::InterruptHandle; use crate::gem::{GemHandle, GemManager}; use crate::kms::connector::{synthetic_edid, Connector}; use crate::kms::crtc::Crtc; @@ -37,7 +37,7 @@ const RING_HEAD_OFFSET: usize = 0x34; pub struct IntelDriver { info: PciDeviceInfo, mmio: MmioRegion, - irq_handle: Mutex>, + irq_handle: Mutex>, display: IntelDisplay, gem: Mutex, connectors: Mutex>, @@ -83,14 +83,11 @@ impl IntelDriver { let (connectors, encoders) = detect_display_topology(&display)?; let crtcs = build_crtcs(&display)?; - let irq_handle = match info.irq { - Some(irq) => Some( - IrqHandle::request(irq) - .map_err(|e| DriverError::Io(format!("failed to request IRQ {irq}: {e}")))?, - ), - None => { + let irq_handle = match InterruptHandle::setup(&info, &mut device) { + Ok(handle) => Some(handle), + Err(e) => { warn!( - "redox-drm: Intel device {} has no IRQ assigned", + "redox-drm: Intel device {} interrupt setup failed: {e}", info.location ); None @@ -497,7 +494,7 @@ impl GpuDriver for IntelDriver { } }; - if irq_event.is_none() { + if !irq_event { return Ok(None); } diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/interrupt.rs b/local/recipes/gpu/redox-drm/source/src/drivers/interrupt.rs new file mode 100644 index 00000000..e8a81e1a --- /dev/null +++ b/local/recipes/gpu/redox-drm/source/src/drivers/interrupt.rs @@ -0,0 +1,155 @@ +use std::io::{Read, Write}; + +use log::{info, warn}; +use redox_driver_sys::irq::{IrqHandle, MsixTable, MsixVector}; +use redox_driver_sys::pci::{PciDevice, PciDeviceInfo, PCI_CAP_ID_MSIX}; + +use crate::driver::{DriverError, Result}; + +pub enum InterruptHandle { + Msix { + vector: MsixVector, + table: MsixTable, + cap_offset: u8, + }, + Legacy { + handle: IrqHandle, + irq: u32, + }, +} + +impl InterruptHandle { + pub fn setup(device_info: &PciDeviceInfo, pci_device: &mut PciDevice) -> Result { + if let Ok(Some(handle)) = Self::try_msix(device_info, pci_device) { + return Ok(handle); + } + + Self::try_legacy(device_info) + } + + fn try_msix(device_info: &PciDeviceInfo, pci_device: &mut PciDevice) -> Result> { + let msix_cap = match device_info.find_capability(PCI_CAP_ID_MSIX) { + Some(cap) => cap, + None => return Ok(None), + }; + + let msix_info = match pci_device.parse_msix(msix_cap.offset) { + Ok(info) => info, + Err(e) => { + warn!( + "redox-drm: MSI-X capability parse failed for {}: {e}", + device_info.location + ); + return Ok(None); + } + }; + + let table = match MsixTable::map(device_info, &msix_info) { + Ok(t) => t, + Err(e) => { + warn!( + "redox-drm: MSI-X table map failed for {}: {e}", + device_info.location + ); + return Ok(None); + } + }; + + table.mask_all(); + + if let Err(e) = pci_device.enable_msix(msix_cap.offset) { + warn!( + "redox-drm: MSI-X enable failed for {}: {e}", + device_info.location + ); + return Ok(None); + } + + let vector = match table.request_vector(0) { + Ok(v) => v, + Err(e) => { + warn!( + "redox-drm: MSI-X vector allocation failed for {}: {e}", + device_info.location + ); + let _ = pci_device.disable_msix(msix_cap.offset); + return Ok(None); + } + }; + + info!( + "redox-drm: MSI-X enabled for {} vector {} irq {}", + device_info.location, vector.index, vector.irq + ); + + Ok(Some(InterruptHandle::Msix { + vector, + table, + cap_offset: msix_cap.offset, + })) + } + + fn try_legacy(device_info: &PciDeviceInfo) -> Result { + let irq = device_info + .irq + .ok_or_else(|| DriverError::Io(format!("no IRQ for {}", device_info.location)))?; + + let handle = IrqHandle::request(irq).map_err(|e| DriverError::Io(e.to_string()))?; + info!( + "redox-drm: using legacy IRQ {irq} for {}", + device_info.location + ); + + Ok(InterruptHandle::Legacy { handle, irq }) + } + + pub fn try_wait(&mut self) -> Result { + match self { + InterruptHandle::Msix { vector, .. } => { + let mut buf = [0u8; 8]; + match vector.fd.read(&mut buf) { + Ok(n) if n > 0 => Ok(true), + Ok(_) => Ok(false), + Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => Ok(false), + Err(e) => Err(DriverError::Io(e.to_string())), + } + } + InterruptHandle::Legacy { handle, .. } => handle + .try_wait() + .map(|ev| ev.is_some()) + .map_err(|e| DriverError::Io(e.to_string())), + } + } + + pub fn eoi(&mut self) -> Result<()> { + match self { + InterruptHandle::Msix { vector, .. } => { + let mut buf = [0u8; 8]; + vector + .fd + .read_exact(&mut buf) + .map_err(|e| DriverError::Io(e.to_string()))?; + vector + .fd + .write_all(&buf) + .map_err(|e| DriverError::Io(e.to_string())) + } + InterruptHandle::Legacy { handle, .. } => { + let mut buf = [0u8; 8]; + let _ = handle.wait().map_err(|e| DriverError::Io(e.to_string()))?; + Ok(()) + } + } + } + + pub fn irq(&self) -> u32 { + match self { + InterruptHandle::Msix { vector, .. } => vector.irq, + InterruptHandle::Legacy { irq, .. } => *irq, + } + } + + pub fn is_msix(&self) -> bool { + matches!(self, InterruptHandle::Msix { .. }) + } +} diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/mod.rs b/local/recipes/gpu/redox-drm/source/src/drivers/mod.rs index a1bd60d6..8878d20e 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/mod.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/mod.rs @@ -1,5 +1,6 @@ pub mod amd; pub mod intel; +pub mod interrupt; use std::collections::HashMap; use std::sync::Arc;