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 <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-12 23:52:19 +01:00
parent 59d4ba5dcf
commit cefc9b8936
10 changed files with 505 additions and 45 deletions
@@ -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) }
@@ -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<IrqHandle>,
irq_handle: Option<InterruptHandle>,
display: DisplayCore,
gem: Mutex<GemManager>,
connectors: Mutex<Vec<Connector>>,
@@ -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)
}
@@ -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<Option<IrqHandle>>,
irq_handle: Mutex<Option<InterruptHandle>>,
display: IntelDisplay,
gem: Mutex<GemManager>,
connectors: Mutex<Vec<Connector>>,
@@ -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);
}
@@ -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<Self> {
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<Option<Self>> {
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<Self> {
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<bool> {
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 { .. })
}
}
@@ -1,5 +1,6 @@
pub mod amd;
pub mod intel;
pub mod interrupt;
use std::collections::HashMap;
use std::sync::Arc;