intel: Phase 2 — memory management modernization

- mod.rs: identity PPGTT with 2MB-pages, PDP register programming in cs_submit
- lmem.rs: free-list page allocator replacing simple bump allocator
- ring.rs: expose write_reg as pub(crate) for PDP register access
This commit is contained in:
2026-06-01 21:11:05 +03:00
parent 9088f5930a
commit 10eeebdc37
3 changed files with 97 additions and 7 deletions
@@ -1,4 +1,4 @@
use log::info;
use log::{debug, info};
use redox_driver_sys::memory::MmioRegion;
@@ -25,6 +25,8 @@ pub struct IntelLmem {
bar_addr: u64,
bar_size: u64,
next_offset: u64,
free_list: Vec<(u64, u64)>,
total_allocated: u64,
}
impl IntelLmem {
@@ -43,29 +45,73 @@ impl IntelLmem {
bar_addr,
bar_size,
next_offset: 0,
free_list: Vec::new(),
total_allocated: 0,
})
}
pub fn alloc(&mut self, size: u64, alignment: u64) -> Result<u64> {
let aligned_size = align_up(size, alignment);
if let Some(idx) = self
.free_list
.iter()
.position(|&(start, free_size)| {
let aligned_start = align_up(start, alignment);
aligned_start + aligned_size <= start + free_size
})
{
let (free_start, free_size) = self.free_list.remove(idx);
let aligned = align_up(free_start, alignment);
let remainder = (aligned + aligned_size).saturating_sub(free_start);
if remainder < free_size {
self.free_list
.push((free_start + remainder, free_size - remainder));
}
self.total_allocated += aligned_size;
let vram_addr = self.bar_addr + aligned;
debug!(
"redox-drm-intel: LMEM reused {:#x} bytes at offset {:#x} (phys {:#010x}) from free list",
aligned_size, aligned, vram_addr
);
return Ok(vram_addr);
}
let aligned = align_up(self.next_offset, alignment);
let end = aligned
.checked_add(size)
.checked_add(aligned_size)
.ok_or_else(|| DriverError::Buffer("Intel LMEM allocation overflow".into()))?;
if end > self.bar_size {
return Err(DriverError::Buffer(format!(
"Intel LMEM exhausted: need {:#x} at offset {:#x}, have {:#x}",
size, aligned, self.bar_size
aligned_size, aligned, self.bar_size
)));
}
self.next_offset = end;
self.total_allocated += aligned_size;
let vram_addr = self.bar_addr + aligned;
info!(
"redox-drm-intel: LMEM allocated {:#x} bytes at offset {:#x} (phys {:#010x})",
size, aligned, vram_addr
aligned_size, aligned, vram_addr
);
Ok(vram_addr)
}
pub fn free(&mut self, offset: u64, size: u64) {
let aligned_size = align_up(size, 4096);
self.free_list.push((offset, aligned_size));
self.free_list.sort_by_key(|&(start, _)| start);
self.total_allocated = self.total_allocated.saturating_sub(aligned_size);
debug!(
"redox-drm-intel: LMEM freed {:#x} bytes at offset {:#x}, {} regions in free list",
aligned_size, offset, self.free_list.len()
);
}
pub fn allocated_bytes(&self) -> u64 {
self.total_allocated
}
pub fn vram_mmio(&self) -> &MmioRegion {
&self.mmio
}
@@ -89,7 +89,6 @@ const RING_HEAD_OFFSET: usize = 0x34;
const GEN8_PDP0_UDW: usize = 0x270;
const GEN8_PDP0_LDW: usize = 0x274;
const GEN8_PDP_STRIDE: usize = 8;
pub struct IntelDriver {
info: PciDeviceInfo,
@@ -290,7 +289,11 @@ impl IntelDriver {
let mut ring = IntelRing::create(mmio_arc.clone(), RingType::Render)?;
ring.bind_gtt(&mut gtt)?;
let (default_pd_base, has_ppgtt) = setup_identity_ppgtt(&mut gtt, &device_info)?;
let (default_pd_base, has_ppgtt) = if device_info.is_gen9_or_later() {
setup_identity_ppgtt(&mut gtt)?
} else {
(0, false)
};
let mut guc = GucFirmware::new(mmio_arc.clone());
if let Some(guc_key) = device_info.guc_fw_key {
@@ -1018,6 +1021,14 @@ impl GpuDriver for IntelDriver {
let mut ring = self.ring.lock()
.map_err(|_| DriverError::Initialization("Intel ring state poisoned".into()))?;
if self.has_ppgtt && self.default_pd_base != 0 {
let pd_lo = (self.default_pd_base & 0xFFFF_FFFF) as u32;
let pd_hi = ((self.default_pd_base >> 32) & 0xFFFF_FFFF) as u32;
ring.write_reg(GEN8_PDP0_LDW, pd_lo)?;
ring.write_reg(GEN8_PDP0_UDW, pd_hi)?;
}
ring.submit_batch(cmds)?;
let seqno = ring.last_seqno();
drop(ring);
@@ -1261,3 +1272,36 @@ fn discover_vbt(mmio: &MmioRegion) -> Option<vbt::VbtInfo> {
debug!("redox-drm-intel: no VBT found in MMIO, using port heuristic");
None
}
fn setup_identity_ppgtt(gtt: &mut IntelGtt) -> Result<(u64, bool)> {
use redox_driver_sys::dma::DmaBuffer;
let pd_bytes = 4096usize;
let mut pd_buf = DmaBuffer::allocate(pd_bytes, 4096)
.map_err(|e| DriverError::Buffer(format!("PPGTT PD DMA alloc failed: {e}")))?;
let pd_phys = pd_buf.physical_address() as u64;
let pd_slice = unsafe {
std::slice::from_raw_parts_mut(pd_buf.as_mut_ptr(), pd_bytes)
};
for entry in 0..128u64 {
let page_addr = entry * 0x200000;
let pte_val = page_addr
| (1u64 << 0)
| (1u64 << 1)
| (1u64 << 7);
let offset = (entry as usize) * 8;
pd_slice[offset..offset + 8].copy_from_slice(&pte_val.to_le_bytes());
}
let pd_gpu_addr = gtt.alloc_range(pd_bytes as u64)?;
gtt.map_range(pd_gpu_addr, pd_phys, pd_bytes as u64, (1u64 << 0) | (1u64 << 1))?;
info!(
"redox-drm-intel: identity PPGTT PD at {:#010x} (phys {:#010x}, 128 × 2MB entries, covers {}MB)",
pd_gpu_addr, pd_phys, 128 * 2
);
Ok((pd_gpu_addr, true))
}
@@ -239,7 +239,7 @@ impl IntelRing {
Ok(self.mmio.read32(offset))
}
fn write_reg(&self, reg: usize, value: u32) -> Result<()> {
pub(crate) fn write_reg(&self, reg: usize, value: u32) -> Result<()> {
let offset = self
.base
.checked_add(reg)