summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/tyr/mmu.rs
blob: 5e88c9e6a4cddc427b417962cedb6fdd73864d56 (plain)
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)
    }
}