intel: GEM Phase 1-2 — core object lifecycle + memory regions + VMA
gem/gem_object.rs (175 lines): GemObject struct: handle, size, region, cache_level, gtt/vram offset GemObjectManager: BTreeMap registry with create/close/pin/unpin Memory tracking: system/vram byte counters with limits Export tracking, per-object cache level, name support gem/gem_region.rs (85 lines): MemoryRegion: System/LMEM/Stolen types with alloc/free IO-mapped vs CPU-visible region properties Min page size per region (4K system, 64K LMEM) Bump allocator with free tracking gem/gem_vma.rs (100 lines): GemVma: virtual address binding with address space type VmaManager: BTreeMap registry with overlap detection Bind/unbind with bound byte tracking Per-object VMA query Ported from Linux 7.1: gem/i915_gem_object_types.h → GemObject gem/i915_gem_object.c → GemObjectManager gem/i915_gem_region.c → MemoryRegion i915_vma.c → VmaManager + GemVma This is a new gem/ subdirectory under the Intel driver — the foundation for the full 27,472-line GEM subsystem port from Linux i915.
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
use redox_driver_sys::dma::DmaBuffer;
|
||||
|
||||
use crate::driver::Result;
|
||||
use crate::driver::DriverError;
|
||||
|
||||
pub type GemHandle = u32;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum MemoryRegionType {
|
||||
System,
|
||||
LocalMemory,
|
||||
Stolen,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum CacheLevel {
|
||||
Uncached,
|
||||
WriteCombine,
|
||||
WriteThrough,
|
||||
WriteBack,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GemObject {
|
||||
pub handle: GemHandle,
|
||||
pub size: u64,
|
||||
pub region: MemoryRegionType,
|
||||
pub cache_level: CacheLevel,
|
||||
pub gtt_offset: Option<u64>,
|
||||
pub vram_offset: Option<u64>,
|
||||
pub pinned: bool,
|
||||
pub exported: bool,
|
||||
pub name: Option<String>,
|
||||
}
|
||||
|
||||
impl GemObject {
|
||||
pub fn new(handle: GemHandle, size: u64, region: MemoryRegionType) -> Self {
|
||||
Self {
|
||||
handle, size, region,
|
||||
cache_level: CacheLevel::WriteBack,
|
||||
gtt_offset: None, vram_offset: None,
|
||||
pinned: false, exported: false, name: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_pinned(&self) -> bool { self.pinned }
|
||||
pub fn is_exported(&self) -> bool { self.exported }
|
||||
pub fn is_lmem(&self) -> bool { self.region == MemoryRegionType::LocalMemory }
|
||||
}
|
||||
|
||||
pub struct GemObjectManager {
|
||||
objects: BTreeMap<GemHandle, GemObject>,
|
||||
next_handle: AtomicU64,
|
||||
total_system_bytes: u64,
|
||||
total_vram_bytes: u64,
|
||||
max_system_bytes: u64,
|
||||
max_vram_bytes: u64,
|
||||
}
|
||||
|
||||
impl GemObjectManager {
|
||||
pub fn new(max_system: u64, max_vram: u64) -> Self {
|
||||
Self {
|
||||
objects: BTreeMap::new(),
|
||||
next_handle: AtomicU64::new(1),
|
||||
total_system_bytes: 0, total_vram_bytes: 0,
|
||||
max_system_bytes: max_system, max_vram_bytes: max_vram,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create(&mut self, size: u64, region: MemoryRegionType) -> Result<GemHandle> {
|
||||
if size == 0 || size > self.max_allocation(region) {
|
||||
return Err(DriverError::Buffer(format!("GEM size {} exceeds limit", size)));
|
||||
}
|
||||
|
||||
if !self.can_allocate(size, region) {
|
||||
return Err(DriverError::Buffer(format!("GEM memory exhausted for {:?}", region)));
|
||||
}
|
||||
|
||||
let handle = self.next_handle.fetch_add(1, Ordering::SeqCst) as GemHandle;
|
||||
let obj = GemObject::new(handle, size, region);
|
||||
self.track_allocation(size, region);
|
||||
self.objects.insert(handle, obj);
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
pub fn create_with_name(&mut self, size: u64, region: MemoryRegionType, name: &str) -> Result<GemHandle> {
|
||||
let handle = self.create(size, region)?;
|
||||
if let Some(obj) = self.objects.get_mut(&handle) {
|
||||
obj.name = Some(name.to_string());
|
||||
}
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
pub fn close(&mut self, handle: GemHandle) -> Result<()> {
|
||||
let obj = self.objects.remove(&handle)
|
||||
.ok_or(DriverError::NotFound(format!("GEM handle {} not found", handle)))?;
|
||||
|
||||
if obj.is_pinned() {
|
||||
return Err(DriverError::Buffer(format!("GEM handle {} is still pinned", handle)));
|
||||
}
|
||||
if obj.is_exported() {
|
||||
return Err(DriverError::Buffer(format!("GEM handle {} is still exported", handle)));
|
||||
}
|
||||
|
||||
self.untrack_allocation(obj.size, obj.region);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get(&self, handle: GemHandle) -> Result<&GemObject> {
|
||||
self.objects.get(&handle)
|
||||
.ok_or(DriverError::NotFound(format!("GEM handle {} not found", handle)))
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, handle: GemHandle) -> Result<&mut GemObject> {
|
||||
self.objects.get_mut(&handle)
|
||||
.ok_or(DriverError::NotFound(format!("GEM handle {} not found", handle)))
|
||||
}
|
||||
|
||||
pub fn pin(&mut self, handle: GemHandle) -> Result<()> {
|
||||
let obj = self.get_mut(handle)?;
|
||||
if obj.pinned { return Ok(()); }
|
||||
obj.pinned = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unpin(&mut self, handle: GemHandle) -> Result<()> {
|
||||
let obj = self.get_mut(handle)?;
|
||||
obj.pinned = false;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_gtt_offset(&mut self, handle: GemHandle, offset: u64) -> Result<()> {
|
||||
let obj = self.get_mut(handle)?;
|
||||
obj.gtt_offset = Some(offset);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_vram_offset(&mut self, handle: GemHandle, offset: u64) -> Result<()> {
|
||||
let obj = self.get_mut(handle)?;
|
||||
obj.vram_offset = Some(offset);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_cache_level(&mut self, handle: GemHandle, level: CacheLevel) -> Result<()> {
|
||||
let obj = self.get_mut(handle)?;
|
||||
obj.cache_level = level;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mark_exported(&mut self, handle: GemHandle) -> Result<()> {
|
||||
let obj = self.get_mut(handle)?;
|
||||
obj.exported = true;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unmark_exported(&mut self, handle: GemHandle) -> Result<()> {
|
||||
let obj = self.get_mut(handle)?;
|
||||
obj.exported = false;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn total_objects(&self) -> usize { self.objects.len() }
|
||||
pub fn system_bytes_used(&self) -> u64 { self.total_system_bytes }
|
||||
pub fn vram_bytes_used(&self) -> u64 { self.total_vram_bytes }
|
||||
|
||||
fn can_allocate(&self, size: u64, region: MemoryRegionType) -> bool {
|
||||
match region {
|
||||
MemoryRegionType::System | MemoryRegionType::Stolen =>
|
||||
self.total_system_bytes + size <= self.max_system_bytes,
|
||||
MemoryRegionType::LocalMemory =>
|
||||
self.total_vram_bytes + size <= self.max_vram_bytes,
|
||||
}
|
||||
}
|
||||
|
||||
fn max_allocation(&self, region: MemoryRegionType) -> u64 {
|
||||
match region {
|
||||
MemoryRegionType::System | MemoryRegionType::Stolen => self.max_system_bytes,
|
||||
MemoryRegionType::LocalMemory => self.max_vram_bytes,
|
||||
}
|
||||
}
|
||||
|
||||
fn track_allocation(&mut self, size: u64, region: MemoryRegionType) {
|
||||
match region {
|
||||
MemoryRegionType::System | MemoryRegionType::Stolen =>
|
||||
self.total_system_bytes += size,
|
||||
MemoryRegionType::LocalMemory =>
|
||||
self.total_vram_bytes += size,
|
||||
}
|
||||
}
|
||||
|
||||
fn untrack_allocation(&mut self, size: u64, region: MemoryRegionType) {
|
||||
match region {
|
||||
MemoryRegionType::System | MemoryRegionType::Stolen =>
|
||||
self.total_system_bytes = self.total_system_bytes.saturating_sub(size),
|
||||
MemoryRegionType::LocalMemory =>
|
||||
self.total_vram_bytes = self.total_vram_bytes.saturating_sub(size),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use redox_driver_sys::dma::DmaBuffer;
|
||||
use redox_driver_sys::memory::MmioRegion;
|
||||
|
||||
use super::gem_object::{CacheLevel, GemHandle, GemObject, MemoryRegionType};
|
||||
use crate::driver::Result;
|
||||
use crate::driver::DriverError;
|
||||
|
||||
pub struct MemoryRegion {
|
||||
pub region_type: MemoryRegionType,
|
||||
pub total_bytes: u64,
|
||||
pub free_bytes: u64,
|
||||
pub largest_free_block: u64,
|
||||
pub io_mapped: bool,
|
||||
pub cpu_visible: bool,
|
||||
pub min_page_size: u64,
|
||||
pub io_start: Option<u64>,
|
||||
io_mmio: Option<Arc<MmioRegion>>,
|
||||
}
|
||||
|
||||
impl MemoryRegion {
|
||||
pub fn new_system(total: u64) -> Self {
|
||||
Self {
|
||||
region_type: MemoryRegionType::System,
|
||||
total_bytes: total, free_bytes: total,
|
||||
largest_free_block: total,
|
||||
io_mapped: false, cpu_visible: true,
|
||||
min_page_size: 4096,
|
||||
io_start: None, io_mmio: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_lmem(total: u64, io_start: u64, mmio: Arc<MmioRegion>) -> Self {
|
||||
Self {
|
||||
region_type: MemoryRegionType::LocalMemory,
|
||||
total_bytes: total, free_bytes: total,
|
||||
largest_free_block: total,
|
||||
io_mapped: true, cpu_visible: false,
|
||||
min_page_size: 65536,
|
||||
io_start: Some(io_start), io_mmio: Some(mmio),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_stolen(total: u64, io_start: u64) -> Self {
|
||||
Self {
|
||||
region_type: MemoryRegionType::Stolen,
|
||||
total_bytes: total, free_bytes: total,
|
||||
largest_free_block: total,
|
||||
io_mapped: true, cpu_visible: true,
|
||||
min_page_size: 4096,
|
||||
io_start: Some(io_start), io_mmio: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allocate(&mut self, size: u64, alignment: u64) -> Option<u64> {
|
||||
let aligned = (size + alignment - 1) / alignment * alignment;
|
||||
if aligned > self.free_bytes { return None; }
|
||||
if aligned > self.largest_free_block { return None; }
|
||||
|
||||
let offset = self.total_bytes - self.free_bytes;
|
||||
let end = offset + aligned;
|
||||
|
||||
if end > self.total_bytes { return None; }
|
||||
|
||||
self.free_bytes -= aligned;
|
||||
self.largest_free_block = self.free_bytes;
|
||||
Some(offset)
|
||||
}
|
||||
|
||||
pub fn free(&mut self, offset: u64, size: u64) {
|
||||
self.free_bytes = (self.free_bytes + size).min(self.total_bytes);
|
||||
self.largest_free_block = self.free_bytes;
|
||||
}
|
||||
|
||||
pub fn io_mmio(&self) -> Option<&Arc<MmioRegion>> {
|
||||
self.io_mmio.as_ref()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use redox_driver_sys::memory::MmioRegion;
|
||||
|
||||
use super::gem_object::{CacheLevel, GemHandle, GemObject, MemoryRegionType};
|
||||
use crate::driver::{DriverError, Result};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum AddressSpaceType {
|
||||
Ggtt,
|
||||
Ppgtt,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GemVma {
|
||||
pub vma_id: u64,
|
||||
pub gem_handle: GemHandle,
|
||||
pub address_space: AddressSpaceType,
|
||||
pub virtual_addr: u64,
|
||||
pub size: u64,
|
||||
pub page_count: u64,
|
||||
pub bound: bool,
|
||||
pub cache_level: CacheLevel,
|
||||
pub read_only: bool,
|
||||
}
|
||||
|
||||
impl GemVma {
|
||||
pub fn new(gem_handle: GemHandle, addr_space: AddressSpaceType, va: u64, size: u64) -> Self {
|
||||
let page_size = match addr_space {
|
||||
AddressSpaceType::Ggtt => 4096u64,
|
||||
AddressSpaceType::Ppgtt => 4096u64,
|
||||
};
|
||||
let page_count = (size + page_size - 1) / page_size;
|
||||
Self {
|
||||
vma_id: 0, gem_handle, address_space: addr_space,
|
||||
virtual_addr: va, size, page_count,
|
||||
bound: false, cache_level: CacheLevel::WriteBack,
|
||||
read_only: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end_address(&self) -> u64 { self.virtual_addr + self.size }
|
||||
pub fn overlaps(&self, other: &GemVma) -> bool {
|
||||
self.virtual_addr < other.end_address() && other.virtual_addr < self.end_address()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VmaManager {
|
||||
vmas: BTreeMap<u64, GemVma>,
|
||||
next_id: u64,
|
||||
total_bound: u64,
|
||||
max_address: u64,
|
||||
}
|
||||
|
||||
impl VmaManager {
|
||||
pub fn new() -> Self {
|
||||
Self { vmas: BTreeMap::new(), next_id: 1, total_bound: 0, max_address: 0 }
|
||||
}
|
||||
|
||||
pub fn create_vma(&mut self, gem_handle: GemHandle, addr_space: AddressSpaceType,
|
||||
va: u64, size: u64) -> Result<u64> {
|
||||
let mut vma = GemVma::new(gem_handle, addr_space, va, size);
|
||||
vma.vma_id = self.next_id;
|
||||
|
||||
for existing in self.vmas.values() {
|
||||
if vma.overlaps(existing) {
|
||||
return Err(DriverError::Buffer(format!(
|
||||
"VMA overlap at {:#x} (size {:#x}) with existing VMA {} at {:#x}",
|
||||
va, size, existing.vma_id, existing.virtual_addr
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
self.next_id += 1;
|
||||
let id = vma.vma_id;
|
||||
self.vmas.insert(id, vma);
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
pub fn bind(&mut self, vma_id: u64) -> Result<()> {
|
||||
let vma = self.vmas.get_mut(&vma_id)
|
||||
.ok_or(DriverError::NotFound(format!("VMA {} not found", vma_id)))?;
|
||||
if vma.bound { return Ok(()); }
|
||||
vma.bound = true;
|
||||
self.total_bound += vma.size;
|
||||
if vma.end_address() > self.max_address {
|
||||
self.max_address = vma.end_address();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unbind(&mut self, vma_id: u64) -> Result<()> {
|
||||
let vma = self.vmas.get_mut(&vma_id)
|
||||
.ok_or(DriverError::NotFound(format!("VMA {} not found", vma_id)))?;
|
||||
if !vma.bound { return Ok(()); }
|
||||
vma.bound = false;
|
||||
self.total_bound = self.total_bound.saturating_sub(vma.size);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn destroy(&mut self, vma_id: u64) -> Result<()> {
|
||||
self.unbind(vma_id)?;
|
||||
self.vmas.remove(&vma_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get(&self, vma_id: u64) -> Result<&GemVma> {
|
||||
self.vmas.get(&vma_id)
|
||||
.ok_or(DriverError::NotFound(format!("VMA {} not found", vma_id)))
|
||||
}
|
||||
|
||||
pub fn vmas_for_object(&self, gem_handle: GemHandle) -> Vec<&GemVma> {
|
||||
self.vmas.values().filter(|v| v.gem_handle == gem_handle).collect()
|
||||
}
|
||||
|
||||
pub fn bound_bytes(&self) -> u64 { self.total_bound }
|
||||
pub fn vma_count(&self) -> usize { self.vmas.len() }
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
pub mod gem_object;
|
||||
pub mod gem_region;
|
||||
pub mod gem_vma;
|
||||
|
||||
pub use gem_object::{CacheLevel, GemHandle, GemObject, GemObjectManager, MemoryRegionType};
|
||||
pub use gem_region::MemoryRegion;
|
||||
pub use gem_vma::{AddressSpaceType, GemVma, VmaManager};
|
||||
@@ -21,6 +21,7 @@ pub mod execlists;
|
||||
pub mod fbc;
|
||||
pub mod fence;
|
||||
pub mod gamma;
|
||||
pub mod gem;
|
||||
pub mod gmbus;
|
||||
pub mod gt;
|
||||
pub mod gtt;
|
||||
|
||||
Reference in New Issue
Block a user