drivers/graphics/ihdgd: Implement support for mapping memory to the GPU

This is done by adding entries to the global GTT page table. At startup
it only contains entries for a small chunk of memory reserved for the
GPU. This small chunk is rarely enough to contain all framebuffers we
need to allocate.
This commit is contained in:
bjorn3
2026-03-23 22:51:27 +01:00
parent cc1dea2a82
commit f152bbad23
4 changed files with 162 additions and 45 deletions
+7 -21
View File
@@ -1,8 +1,8 @@
use std::{ptr, slice};
use std::{mem, ptr, slice};
use range_alloc::RangeAllocator;
use syscall::{Error, EIO};
use common::dma::Dma;
use crate::device::ggtt::GlobalGtt;
use crate::device::MmioRegion;
#[derive(Debug)]
@@ -28,35 +28,21 @@ impl GpuBuffer {
}
}
pub fn alloc(
gm: &MmioRegion,
alloc_surfaces: &mut RangeAllocator<u32>,
size: u32,
) -> syscall::Result<Self> {
let surf_size = size.next_multiple_of(4096);
let gm_offset = alloc_surfaces
.allocate_range(surf_size)
.map_err(|err| {
log::warn!("failed to allocate buffer of size {}: {:?}", surf_size, err);
Error::new(EIO)
})?
.start;
pub fn alloc(gm: &MmioRegion, ggtt: &mut GlobalGtt, size: u32) -> syscall::Result<Self> {
let gm_offset = ggtt.alloc_phys_mem(size)?;
Ok(unsafe { GpuBuffer::new(gm, gm_offset, size, true) })
}
pub fn alloc_dumb(
gm: &MmioRegion,
alloc_surfaces: &mut RangeAllocator<u32>,
ggtt: &mut GlobalGtt,
width: u32,
height: u32,
) -> syscall::Result<(Self, u32)> {
//TODO: documentation on this is not great
let stride = (width * 4).next_multiple_of(64);
Ok((
GpuBuffer::alloc(gm, alloc_surfaces, stride * height)?,
stride,
))
Ok((GpuBuffer::alloc(gm, ggtt, stride * height)?, stride))
}
}
+134
View File
@@ -0,0 +1,134 @@
use std::sync::Arc;
use std::{mem, ptr};
use pcid_interface::PciFunctionHandle;
use range_alloc::RangeAllocator;
use syscall::{Error, EIO};
use crate::device::MmioRegion;
/// Global Graphics Translation Table (global GTT)
///
/// The global GTT is a page table used by all parts of the GPU that don't use
/// the PPGTT (Per-Process GTT). This includes the display engine and the GM
/// aperture that the CPU can access.
///
/// The global GTT is located in the GTTMM BAR at offset 8MiB, is up to 8MiB big
/// and consists of 64bit entries. Each entry has a present bit as LSB and the
/// address of the frame at bits 12 through 38. The rest of the bits are ignored.
///
/// Source: Pages 6 and 75 of intel-gfx-prm-osrc-kbl-vol05-memory_views.pdf
pub struct GlobalGtt {
gttmm: Arc<MmioRegion>,
/// Base the GTT
gtt_base: *mut u64,
/// Size of the GTT
gtt_size: usize,
/// Allocator for GM aperture pages
gm_alloc: RangeAllocator<u32>,
// FIXME reuse DSM memory for something useful
/// Base Data of Stolen Memory (DSM)
base_dsm: *mut (),
/// Size of DSM
size_data_stolen_memory: usize,
}
const GTT_PAGE_SIZE: u32 = 4096;
impl GlobalGtt {
pub unsafe fn new(
pcid_handle: &mut PciFunctionHandle,
gttmm: Arc<MmioRegion>,
gm_size: u32,
) -> Self {
let gtt_offset = 8 * 1024 * 1024;
let gtt_base = ptr::with_exposed_provenance_mut(gttmm.virt + gtt_offset);
let base_dsm = unsafe { pcid_handle.read_config(0x5C) };
let ggc = unsafe { pcid_handle.read_config(0x50) };
let dsm_size = match (ggc >> 8) & 0xFF {
size if size & 0xF0 == 0 => size * 32 * 1024 * 1024,
size => (size & !0xF0) * 4 * 1024 * 1024,
} as usize;
let gtt_size = match (ggc >> 6) & 0x3 {
0 => 0,
1 => 2 * 1024 * 1024,
2 => 4 * 1024 * 1024,
3 => 8 * 1024 * 1024,
_ => unreachable!(),
} as usize;
log::info!("Base DSM: {:X}", base_dsm);
log::info!(
"GGC: {:X} => global GTT size: {}MiB; DSM size: {}MiB",
ggc,
gtt_size / 1024 / 1024,
dsm_size / 1024 / 1024,
);
let gm_alloc = RangeAllocator::new(0..gm_size / 4096);
GlobalGtt {
gttmm,
gtt_base,
gtt_size,
gm_alloc,
base_dsm: core::ptr::with_exposed_provenance_mut(base_dsm as usize),
size_data_stolen_memory: dsm_size,
}
}
/// Reset the global GTT by clearing out all existing mappings.
pub unsafe fn reset(&mut self) {
for i in 0..self.gtt_size / 8 {
unsafe { *self.gtt_base.add(i) = 0 };
}
}
pub fn reserve(&mut self, surf: u32, surf_size: u32) {
assert!(surf.is_multiple_of(GTT_PAGE_SIZE));
assert!(surf_size.is_multiple_of(GTT_PAGE_SIZE));
self.gm_alloc
.allocate_exact_range(
surf / GTT_PAGE_SIZE..surf / GTT_PAGE_SIZE + surf_size / GTT_PAGE_SIZE,
)
.unwrap_or_else(|err| {
panic!(
"failed to allocate pre-existing surface at 0x{:x} of size {}: {:?}",
surf, surf_size, err
);
});
}
pub fn alloc_phys_mem(&mut self, size: u32) -> syscall::Result<u32> {
let size = size.next_multiple_of(GTT_PAGE_SIZE);
let sgl = common::sgl::Sgl::new(size as usize)?;
let range = self
.gm_alloc
.allocate_range(size / GTT_PAGE_SIZE)
.map_err(|err| {
log::warn!("failed to allocate buffer of size {}: {:?}", size, err);
Error::new(EIO)
})?;
for chunk in sgl.chunks() {
for i in 0..chunk.length / GTT_PAGE_SIZE as usize {
unsafe {
*self
.gtt_base
.add(range.start as usize + chunk.offset / GTT_PAGE_SIZE as usize + i) =
chunk.phys as u64 + i as u64 * u64::from(GTT_PAGE_SIZE) + 1;
}
}
}
mem::forget(sgl);
Ok(range.start * 4096)
}
}
+16 -8
View File
@@ -19,6 +19,8 @@ mod gmbus;
pub use self::gmbus::*;
mod gpio;
pub use self::gpio::*;
mod ggtt;
use ggtt::*;
mod hal;
pub use self::hal::*;
mod pipe;
@@ -153,7 +155,6 @@ enum VideoInput {
pub struct Device {
kind: DeviceKind,
alloc_buffers: RangeAllocator<u32>,
alloc_surfaces: RangeAllocator<u32>,
bios: Option<Bios>,
ddis: Vec<Ddi>,
dpclka_cfgcr0: Option<MmioPtr<u32>>,
@@ -162,6 +163,7 @@ pub struct Device {
framebuffers: Vec<DeviceFb>,
int: Interrupter,
gttmm: Arc<MmioRegion>,
ggtt: GlobalGtt,
gm: MmioRegion,
gmbus: Gmbus,
pipes: Vec<Pipe>,
@@ -175,7 +177,6 @@ impl fmt::Debug for Device {
f.debug_struct("Device")
.field("kind", &self.kind)
.field("alloc_buffers", &self.alloc_buffers)
.field("alloc_surfaces", &self.alloc_surfaces)
.field("gttmm", &self.gttmm)
.field("gm", &self.gm)
.field("ref_freq", &self.ref_freq)
@@ -290,6 +291,16 @@ impl Device {
None
};
let ggtt = unsafe {
GlobalGtt::new(
pcid_handle,
gttmm.clone(),
//TODO: how to use 64-bit surface addresses?
gm.size.min(u32::MAX as usize) as u32,
)
};
//unsafe { ggtt.reset() };
// GMBUS seems to be stable for all generations
let gmbus = unsafe { Gmbus::new(&gttmm)? };
@@ -402,12 +413,9 @@ impl Device {
//TODO: get number of available buffers
let buffers = 1024;
//TODO: how to use 64-bit surface addresses?
let surface_memory = gm.size.min(u32::max_value() as usize) as u32;
Ok(Self {
kind,
alloc_buffers: RangeAllocator::new(0..buffers),
alloc_surfaces: RangeAllocator::new(0..surface_memory),
bios,
ddis,
dpclka_cfgcr0,
@@ -416,6 +424,7 @@ impl Device {
framebuffers: Vec::new(),
int,
gttmm,
ggtt,
gm,
gmbus,
pipes,
@@ -428,7 +437,6 @@ impl Device {
pub fn init_inner(&mut self) {
// Discover current framebuffers
self.alloc_buffers.reset();
self.alloc_surfaces.reset();
self.framebuffers.clear();
for pipe in self.pipes.iter() {
for plane in pipe.planes.iter() {
@@ -436,7 +444,7 @@ impl Device {
plane.fetch_modeset(&mut self.alloc_buffers);
self.framebuffers
.push(plane.fetch_framebuffer(&self.gm, &mut self.alloc_surfaces));
.push(plane.fetch_framebuffer(&self.gm, &mut self.ggtt));
}
}
}
@@ -720,7 +728,7 @@ impl Device {
let width = timing.horizontal_active_pixels as u32;
let height = timing.vertical_active_lines as u32;
let fb = DeviceFb::alloc(&self.gm, &mut self.alloc_surfaces, width, height)?;
let fb = DeviceFb::alloc(&self.gm, &mut self.ggtt, width, height)?;
plane.modeset(&mut self.alloc_buffers)?;
plane.set_framebuffer(&fb);
+5 -16
View File
@@ -4,7 +4,7 @@ use syscall::error::Result;
use syscall::{Error, EIO};
use super::buffer::GpuBuffer;
use super::MmioRegion;
use super::{GlobalGtt, MmioRegion};
pub const PLANE_CTL_ENABLE: u32 = 1 << 31;
@@ -38,11 +38,11 @@ impl DeviceFb {
pub fn alloc(
gm: &MmioRegion,
alloc_surfaces: &mut RangeAllocator<u32>,
ggtt: &mut GlobalGtt,
width: u32,
height: u32,
) -> syscall::Result<Self> {
let (buffer, stride) = GpuBuffer::alloc_dumb(gm, alloc_surfaces, width, height)?;
let (buffer, stride) = GpuBuffer::alloc_dumb(gm, ggtt, width, height)?;
Ok(DeviceFb {
buffer,
@@ -112,11 +112,7 @@ impl Plane {
Ok(())
}
pub fn fetch_framebuffer(
&self,
gm: &MmioRegion,
alloc_surfaces: &mut RangeAllocator<u32>,
) -> DeviceFb {
pub fn fetch_framebuffer(&self, gm: &MmioRegion, ggtt: &mut GlobalGtt) -> DeviceFb {
let size = self.size.read();
let width = (size & 0xFFFF) + 1;
let height = ((size >> 16) & 0xFFFF) + 1;
@@ -126,14 +122,7 @@ impl Plane {
let surf = self.surf.read() & 0xFFFFF000;
//TODO: read bits per pixel
let surf_size = (stride * height).next_multiple_of(4096);
alloc_surfaces
.allocate_exact_range(surf..(surf + surf_size))
.unwrap_or_else(|err| {
panic!(
"failed to allocate pre-existing surface at 0x{:x} of size {}: {:?}",
surf, surf_size, err
);
});
ggtt.reserve(surf, surf_size);
unsafe { DeviceFb::new(gm, surf, width, height, stride, true) }
}