From f152bbad239ce882ab8d00090b9e42158bd7dbb6 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 23 Mar 2026 22:51:27 +0100 Subject: [PATCH] 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. --- drivers/graphics/ihdgd/src/device/buffer.rs | 28 +--- drivers/graphics/ihdgd/src/device/ggtt.rs | 134 ++++++++++++++++++++ drivers/graphics/ihdgd/src/device/mod.rs | 24 ++-- drivers/graphics/ihdgd/src/device/pipe.rs | 21 +-- 4 files changed, 162 insertions(+), 45 deletions(-) create mode 100644 drivers/graphics/ihdgd/src/device/ggtt.rs diff --git a/drivers/graphics/ihdgd/src/device/buffer.rs b/drivers/graphics/ihdgd/src/device/buffer.rs index ccf3a1fadf..2cd83ea9e9 100644 --- a/drivers/graphics/ihdgd/src/device/buffer.rs +++ b/drivers/graphics/ihdgd/src/device/buffer.rs @@ -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, - size: u32, - ) -> syscall::Result { - 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 { + 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, + 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)) } } diff --git a/drivers/graphics/ihdgd/src/device/ggtt.rs b/drivers/graphics/ihdgd/src/device/ggtt.rs new file mode 100644 index 0000000000..5e39827aa6 --- /dev/null +++ b/drivers/graphics/ihdgd/src/device/ggtt.rs @@ -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, + /// Base the GTT + gtt_base: *mut u64, + /// Size of the GTT + gtt_size: usize, + + /// Allocator for GM aperture pages + gm_alloc: RangeAllocator, + + // 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, + 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 { + 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) + } +} diff --git a/drivers/graphics/ihdgd/src/device/mod.rs b/drivers/graphics/ihdgd/src/device/mod.rs index 7c751573f4..ed996d4990 100644 --- a/drivers/graphics/ihdgd/src/device/mod.rs +++ b/drivers/graphics/ihdgd/src/device/mod.rs @@ -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, - alloc_surfaces: RangeAllocator, bios: Option, ddis: Vec, dpclka_cfgcr0: Option>, @@ -162,6 +163,7 @@ pub struct Device { framebuffers: Vec, int: Interrupter, gttmm: Arc, + ggtt: GlobalGtt, gm: MmioRegion, gmbus: Gmbus, pipes: Vec, @@ -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(>tmm)? }; @@ -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); diff --git a/drivers/graphics/ihdgd/src/device/pipe.rs b/drivers/graphics/ihdgd/src/device/pipe.rs index ffad8f4e32..f527e2d615 100644 --- a/drivers/graphics/ihdgd/src/device/pipe.rs +++ b/drivers/graphics/ihdgd/src/device/pipe.rs @@ -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, + ggtt: &mut GlobalGtt, width: u32, height: u32, ) -> syscall::Result { - 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, - ) -> 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) } }