intel: Phase 2c - free-list VRAM allocator + PAT init + regs_gt PAT constants

- gem_lmem: Replace bump allocator with best-fit free-list (BTreeMap) that
  tracks individual allocations and coalesces freed blocks on both sides
- mocs: Add init_pat() - programs PAT index 0-7 with WB/WC/WT/UC for Gen9+,
  and WB-only for Gen12+; called after init_mocs() in IntelDriver init
- regs_gt: Add PAT register constants (GEN8_PRIVATE_PAT_*, GEN12_PAT_INDEX,
  GEN8_PPAT_* cache attributes) and TBIMR_FAST_CLIP
- PAT programming: Gen9 uses 0x40E0 base with LLC/LLCELLC attributes,
  Gen12 uses 0x4800 base with simple WB/WC/WT/UC (no LLC on Xe2)
- All changes compile clean (0 errors)
This commit is contained in:
2026-06-03 10:57:21 +03:00
parent 77f527e896
commit 31c19fe839
4 changed files with 177 additions and 30 deletions
@@ -1,40 +1,149 @@
use std::collections::BTreeMap;
use std::sync::Arc;
use redox_driver_sys::memory::MmioRegion;
use crate::driver::Result;
use crate::driver::DriverError;
use crate::driver::Result;
struct FreeBlock {
offset: u64,
size: u64,
}
struct AllocEntry {
size: u64,
}
pub struct LmemAllocator {
mmio: Arc<MmioRegion>,
total_bytes: u64,
used_bytes: u64,
largest_free: u64,
free_list: BTreeMap<u64, FreeBlock>,
allocs: BTreeMap<u64, AllocEntry>,
alignment: u64,
}
impl LmemAllocator {
pub fn new(mmio: Arc<MmioRegion>, total: u64) -> Self {
let mut free_list = BTreeMap::new();
free_list.insert(0, FreeBlock { offset: 0, size: total });
Self {
mmio, total_bytes: total, used_bytes: 0,
largest_free: total, alignment: 65536,
mmio,
total_bytes: total,
free_list,
allocs: BTreeMap::new(),
alignment: 65536,
}
}
pub fn allocate(&mut self, size: u64) -> Option<u64> {
let aligned = ((size + self.alignment - 1) / self.alignment) * self.alignment;
if aligned > self.remaining() { return None; }
let offset = self.used_bytes;
self.used_bytes += aligned;
self.largest_free = self.remaining();
let aligned = Self::align_up(size, self.alignment);
if aligned > self.total_bytes {
return None;
}
let mut best_offset = None;
let mut best_size = u64::MAX;
for (&off, block) in self.free_list.iter() {
if block.size >= aligned && block.size < best_size {
best_offset = Some(off);
best_size = block.size;
}
}
let offset = best_offset?;
let block = self.free_list.remove(&offset).expect("block must exist");
let remaining = block.size - aligned;
if remaining > 0 {
self.free_list
.insert(offset + aligned, FreeBlock { offset: offset + aligned, size: remaining });
}
self.allocs.insert(offset, AllocEntry { size: aligned });
Some(offset)
}
pub fn free(&mut self, offset: u64, size: u64) {
let aligned = ((size + self.alignment - 1) / self.alignment) * self.alignment;
self.used_bytes = self.used_bytes.saturating_sub(aligned);
self.largest_free = self.remaining();
let aligned = Self::align_up(size, self.alignment);
let entry = match self.allocs.remove(&offset) {
Some(e) => e,
None => return,
};
let freed = entry.size.min(aligned);
let next_offset = offset + freed;
if let Some(next_block) = self.free_list.remove(&next_offset) {
let merged = FreeBlock { offset, size: freed + next_block.size };
self.free_list.insert(offset, merged);
} else {
self.free_list.insert(offset, FreeBlock { offset, size: freed });
}
if offset >= self.alignment {
let mut prev_key = None;
for (&off, block) in self.free_list.iter() {
if off + block.size == offset {
prev_key = Some(off);
}
}
if let Some(prev_off) = prev_key {
let prev_block = self.free_list.remove(&prev_off).expect("must exist");
let merged = FreeBlock {
offset: prev_off,
size: prev_block.size + freed,
};
self.free_list.remove(&offset);
self.free_list.insert(prev_off, merged);
}
}
}
pub fn remaining(&self) -> u64 { self.total_bytes.saturating_sub(self.used_bytes) }
pub fn allocate_aligned(&mut self, size: u64, align: u64) -> Option<u64> {
let aligned_size = Self::align_up(size, self.alignment);
let align_mask = align - 1;
for (&off, block) in self.free_list.iter() {
if block.size >= aligned_size {
let aligned_offset = (off + align_mask) & !align_mask;
if aligned_offset + aligned_size <= off + block.size {
let before = aligned_offset.saturating_sub(off);
let after = (off + block.size) - (aligned_offset + aligned_size);
self.free_list.remove(&off);
if before > 0 {
self.free_list.insert(off, FreeBlock { offset: off, size: before });
}
if after > 0 {
let after_off = aligned_offset + aligned_size;
self.free_list.insert(after_off, FreeBlock { offset: after_off, size: after });
}
self.allocs.insert(aligned_offset, AllocEntry { size: aligned_size });
return Some(aligned_offset);
}
}
}
None
}
pub fn remaining(&self) -> u64 {
self.free_list.values().map(|b| b.size).sum()
}
pub fn largest_free(&self) -> u64 {
self.free_list.values().map(|b| b.size).max().unwrap_or(0)
}
pub fn used_bytes(&self) -> u64 {
self.total_bytes - self.remaining()
}
pub fn total_bytes(&self) -> u64 {
self.total_bytes
}
#[inline]
fn align_up(size: u64, align: u64) -> u64 {
((size + align - 1) / align) * align
}
pub fn write_region(&self, offset: u64, data: &[u8]) -> Result<()> {
if offset + data.len() as u64 > self.total_bytes {
@@ -42,10 +151,8 @@ impl LmemAllocator {
}
for (i, &byte) in data.iter().enumerate() {
if (offset as usize + i) % 4 == 0 && i + 3 < data.len() {
let val = u32::from_le_bytes([
data[i], data[i+1], data[i+2], data[i+3],
]);
self.mmio.write32((offset as usize + i), val);
let val = u32::from_le_bytes([data[i], data[i + 1], data[i + 2], data[i + 3]]);
self.mmio.write32(offset as usize + i, val);
}
}
Ok(())
@@ -56,18 +163,11 @@ impl LmemAllocator {
return Err(DriverError::Buffer("LMEM read out of bounds".into()));
}
let mut i = 0;
while i < buf.len() {
if i + 4 <= buf.len() {
let val = self.mmio.read32((offset as usize + i));
buf[i..i+4].copy_from_slice(&val.to_le_bytes());
i += 4;
} else {
break;
}
while i + 4 <= buf.len() {
let val = self.mmio.read32(offset as usize + i);
buf[i..i + 4].copy_from_slice(&val.to_le_bytes());
i += 4;
}
Ok(())
}
pub fn used_bytes(&self) -> u64 { self.used_bytes }
pub fn total_bytes(&self) -> u64 { self.total_bytes }
}
}
@@ -15,6 +15,13 @@ const GEN12_MOCS_UC: u32 = 0x0000_0008;
const GEN12_MOCS_WT: u32 = 0x0000_0018;
const GEN12_MOCS_WB: u32 = 0x0005_0038;
const GEN8_PPAT_WB: u32 = 0x3;
const GEN8_PPAT_WT: u32 = 0x2;
const GEN8_PPAT_WC: u32 = 0x1;
const GEN8_PPAT_UC: u32 = 0x0;
const GEN8_PPAT_LLC: u32 = 1 << 2;
const GEN8_PPAT_LLCELLC: u32 = 2 << 2;
pub fn init_mocs(mmio: &Arc<MmioRegion>, device_info: &IntelDeviceInfo) -> Result<()> {
if device_info.is_gen12_or_later() {
let entries: [u32; 64] = [
@@ -36,3 +43,30 @@ pub fn init_mocs(mmio: &Arc<MmioRegion>, device_info: &IntelDeviceInfo) -> Resul
}
Ok(())
}
pub fn init_pat(mmio: &Arc<MmioRegion>, device_info: &IntelDeviceInfo) -> Result<()> {
if device_info.is_gen12_or_later() {
let base = 0x4800usize;
mmio.write32(base, GEN8_PPAT_WB);
mmio.write32(base + 4, GEN8_PPAT_WC);
mmio.write32(base + 8, GEN8_PPAT_WT);
mmio.write32(base + 12, GEN8_PPAT_UC);
mmio.write32(base + 16, GEN8_PPAT_WB);
mmio.write32(base + 20, GEN8_PPAT_WB);
mmio.write32(base + 24, GEN8_PPAT_WB);
mmio.write32(base + 28, GEN8_PPAT_WB);
info!("redox-drm-intel: Gen12 PAT initialized");
} else if device_info.is_gen9_or_later() {
let base = 0x40E0usize;
mmio.write32(base, GEN8_PPAT_WB | GEN8_PPAT_LLC);
mmio.write32(base + 4, GEN8_PPAT_WC | GEN8_PPAT_LLCELLC);
mmio.write32(base + 8, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC);
mmio.write32(base + 12, GEN8_PPAT_UC);
mmio.write32(base + 16, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC);
mmio.write32(base + 20, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC);
mmio.write32(base + 24, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC);
mmio.write32(base + 28, GEN8_PPAT_WB | GEN8_PPAT_LLCELLC);
info!("redox-drm-intel: Gen9 PAT initialized");
}
Ok(())
}
@@ -354,6 +354,7 @@ impl IntelDriver {
);
mocs::init_mocs(&mmio_arc, &device_info)?;
mocs::init_pat(&mmio_arc, &device_info)?;
let mut dpll = DisplayPll::new(mmio_arc.clone(), &device_info);
dpll.init()?;
@@ -450,3 +450,15 @@ pub const ICL_HDC_MODE: usize = 0x7300;
pub const TBIMR_FAST_CLIP: u32 = 1 << 5;
pub const L3_PWM_TIMER_INIT_VAL_MASK: u32 = 0x3FF;
pub const FF_MODE2_TDS_TIMER_MASK: u32 = 0xFF << 16;
pub const GEN8_PRIVATE_PAT_LO: usize = 0x40E0;
pub const GEN8_PRIVATE_PAT_HI: usize = 0x40E4;
pub const GEN12_PAT_INDEX: usize = 0x4800;
pub const GEN8_PPAT_WB: u32 = 0x3;
pub const GEN8_PPAT_WT: u32 = 0x2;
pub const GEN8_PPAT_WC: u32 = 0x1;
pub const GEN8_PPAT_UC: u32 = 0x0;
pub const GEN8_PPAT_LLC: u32 = 1 << 2;
pub const GEN8_PPAT_LLCELLC: u32 = 2 << 2;
pub const GEN8_PPAT_ELLC_OVERRIDE: u32 = 0 << 2;