diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/gem/gem_object.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/gem/gem_object.rs new file mode 100644 index 0000000000..2e723304f9 --- /dev/null +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/gem/gem_object.rs @@ -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, + pub vram_offset: Option, + pub pinned: bool, + pub exported: bool, + pub name: Option, +} + +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, + 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 { + 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 { + 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), + } + } +} diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/gem/gem_region.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/gem/gem_region.rs new file mode 100644 index 0000000000..da3c280654 --- /dev/null +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/gem/gem_region.rs @@ -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, + io_mmio: Option>, +} + +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) -> 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 { + 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> { + self.io_mmio.as_ref() + } +} diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/gem/gem_vma.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/gem/gem_vma.rs new file mode 100644 index 0000000000..f704f433e1 --- /dev/null +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/gem/gem_vma.rs @@ -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, + 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 { + 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() } +} diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/gem/mod.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/gem/mod.rs new file mode 100644 index 0000000000..26028b6f9a --- /dev/null +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/gem/mod.rs @@ -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}; diff --git a/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs b/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs index 5f428ee2be..8100e0a5da 100644 --- a/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs +++ b/local/recipes/gpu/redox-drm/source/src/drivers/intel/mod.rs @@ -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;