// SPDX-License-Identifier: GPL-2.0 or MIT //! Memory Management Unit (MMU) module. //! //! The GPU MMU provides a limited number of memory address spaces for use by command streams. //! The MMU translates virtual addresses to physical addresses and manages memory configuration //! and access permissions. //! //! This MMU module is essentially a locked wrapper around a [`SlotManager`] instance. //! The [`SlotManager`] manages the assignment of virtual address spaces to hardware address-space //! (AS) slots. MMU commands such as updates and flushes are carried out by the //! [`AddressSpaceManager`] which actually writes to the MMU registers. use core::ops::Range; use kernel::{ device::{ Bound, Device, // }, new_mutex, prelude::*, sync::{ Arc, ArcBorrow, Mutex, // }, // }; use crate::{ driver::IoMem, gpu::GpuInfo, mmu::address_space::{ AddressSpaceManager, VmAsData, // }, regs::{ gpu_control::AS_PRESENT, MAX_AS, // }, slot::SlotManager, // }; pub(crate) mod address_space; pub(crate) type AsSlotManager<'drm> = SlotManager, MAX_AS>; /// Locked wrapper for carrying out virtual memory (VM) operations on the MMU. #[pin_data] pub(crate) struct Mmu<'drm> { /// Slot Manager instance used to allocate hardware slots and write to MMU registers. #[pin] pub(crate) as_manager: Mutex>, } impl<'drm> Mmu<'drm> { /// Create an MMU component for this device. pub(crate) fn new( dev: &'drm Device, iomem: ArcBorrow<'_, IoMem<'drm>>, gpu_info: &GpuInfo, ) -> Result>> { let present = AS_PRESENT::from_raw(gpu_info.as_present).present().get(); let slot_count = present.count_ones().try_into()?; let address_space_manager = AddressSpaceManager::new(dev, iomem.into(), present)?; let as_slot_manager = SlotManager::new(address_space_manager, slot_count).inspect_err(|e| { dev_err!( dev, "Failed to initialize MMU slot manager with {} slots: {:?}", slot_count, e ); })?; let mmu_init = try_pin_init!(Self{ as_manager <- new_mutex!(as_slot_manager), }); Arc::pin_init(mmu_init, GFP_KERNEL) } /// Assign a VM to an AS slot, provide a translation table, /// and update the MMU to make the VM resident. pub(crate) fn activate_vm(&self, vm_as_data: ArcBorrow<'_, VmAsData<'drm>>) -> Result { self.as_manager.lock().activate_vm(vm_as_data) } /// Evict a VM from its AS slot and flush the MMU. pub(crate) fn deactivate_vm(&self, vm_as_data: &VmAsData<'drm>) -> Result { self.as_manager.lock().deactivate_vm(vm_as_data) } /// Flush MMU translation caches after a VM update. pub(crate) fn flush_vm(&self, vm_as_data: &VmAsData<'drm>) -> Result { self.as_manager.lock().flush_vm(vm_as_data) } /// Flags the start of a VM update. /// /// If the VM is resident, any GPU access on the memory range being /// updated will be blocked until `Mmu::end_vm_update()` is called. /// This guarantees the atomicity of a VM update. /// If the VM is not resident, this is a NOP. pub(crate) fn start_vm_update( &self, vm_as_data: &VmAsData<'drm>, region: &Range, ) -> Result { self.as_manager.lock().start_vm_update(vm_as_data, region) } /// Flags the end of a VM update. /// /// If the VM is resident, this will let GPU accesses on the updated /// range go through, in case any of them were blocked. /// If the VM is not resident, this is a NOP. pub(crate) fn end_vm_update(&self, vm_as_data: &VmAsData<'drm>) -> Result { self.as_manager.lock().end_vm_update(vm_as_data) } }