1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
// 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<AddressSpaceManager<'drm>, 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<AsSlotManager<'drm>>,
}
impl<'drm> Mmu<'drm> {
/// Create an MMU component for this device.
pub(crate) fn new(
dev: &'drm Device<Bound>,
iomem: ArcBorrow<'_, IoMem<'drm>>,
gpu_info: &GpuInfo,
) -> Result<Arc<Mmu<'drm>>> {
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<u64>,
) -> 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)
}
}
|