1263b2ba5SJacek Lawrynowicz // SPDX-License-Identifier: GPL-2.0-only 2263b2ba5SJacek Lawrynowicz /* 3263b2ba5SJacek Lawrynowicz * Copyright (C) 2020-2023 Intel Corporation 4263b2ba5SJacek Lawrynowicz */ 5263b2ba5SJacek Lawrynowicz 6263b2ba5SJacek Lawrynowicz #include <linux/bitfield.h> 7263b2ba5SJacek Lawrynowicz #include <linux/highmem.h> 8263b2ba5SJacek Lawrynowicz 9263b2ba5SJacek Lawrynowicz #include "ivpu_drv.h" 10263b2ba5SJacek Lawrynowicz #include "ivpu_hw.h" 11263b2ba5SJacek Lawrynowicz #include "ivpu_mmu.h" 12263b2ba5SJacek Lawrynowicz #include "ivpu_mmu_context.h" 13263b2ba5SJacek Lawrynowicz 14a2fd4a6fSKarol Wachowski #define IVPU_MMU_PGD_INDEX_MASK GENMASK(47, 39) 15a2fd4a6fSKarol Wachowski #define IVPU_MMU_PUD_INDEX_MASK GENMASK(38, 30) 16263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PMD_INDEX_MASK GENMASK(29, 21) 17263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PTE_INDEX_MASK GENMASK(20, 12) 18263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAGS_MASK GENMASK(11, 0) 19263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_NG BIT(11) 20263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_AF BIT(10) 21263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_USER BIT(6) 22263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_LLC_COHERENT BIT(2) 23263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_TYPE_PAGE BIT(1) 24263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_VALID BIT(0) 25263b2ba5SJacek Lawrynowicz 26263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PAGE_SIZE SZ_4K 27263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PTE_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PAGE_SIZE) 28263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PMD_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PTE_MAP_SIZE) 29a2fd4a6fSKarol Wachowski #define IVPU_MMU_PUD_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PMD_MAP_SIZE) 30a2fd4a6fSKarol Wachowski #define IVPU_MMU_PGD_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PUD_MAP_SIZE) 31263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PGTABLE_SIZE (IVPU_MMU_PGTABLE_ENTRIES * sizeof(u64)) 32263b2ba5SJacek Lawrynowicz 33263b2ba5SJacek Lawrynowicz #define IVPU_MMU_DUMMY_ADDRESS 0xdeadb000 34263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_VALID (IVPU_MMU_ENTRY_FLAG_TYPE_PAGE | IVPU_MMU_ENTRY_FLAG_VALID) 35263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_INVALID (IVPU_MMU_DUMMY_ADDRESS & ~IVPU_MMU_ENTRY_FLAGS_MASK) 36263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_MAPPED (IVPU_MMU_ENTRY_FLAG_AF | IVPU_MMU_ENTRY_FLAG_USER | \ 37263b2ba5SJacek Lawrynowicz IVPU_MMU_ENTRY_FLAG_NG | IVPU_MMU_ENTRY_VALID) 38263b2ba5SJacek Lawrynowicz 39263b2ba5SJacek Lawrynowicz static int ivpu_mmu_pgtable_init(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable) 40263b2ba5SJacek Lawrynowicz { 41263b2ba5SJacek Lawrynowicz dma_addr_t pgd_dma; 42263b2ba5SJacek Lawrynowicz 43*103d2ea1SKarol Wachowski pgtable->pgd_dma_ptr = dma_alloc_coherent(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pgd_dma, 44*103d2ea1SKarol Wachowski GFP_KERNEL); 45*103d2ea1SKarol Wachowski if (!pgtable->pgd_dma_ptr) 46263b2ba5SJacek Lawrynowicz return -ENOMEM; 47263b2ba5SJacek Lawrynowicz 48263b2ba5SJacek Lawrynowicz pgtable->pgd_dma = pgd_dma; 49263b2ba5SJacek Lawrynowicz 50263b2ba5SJacek Lawrynowicz return 0; 51263b2ba5SJacek Lawrynowicz } 52263b2ba5SJacek Lawrynowicz 53*103d2ea1SKarol Wachowski static void ivpu_mmu_pgtable_free(struct ivpu_device *vdev, u64 *cpu_addr, dma_addr_t dma_addr) 54*103d2ea1SKarol Wachowski { 55*103d2ea1SKarol Wachowski if (cpu_addr) 56*103d2ea1SKarol Wachowski dma_free_coherent(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, cpu_addr, 57*103d2ea1SKarol Wachowski dma_addr & ~IVPU_MMU_ENTRY_FLAGS_MASK); 58*103d2ea1SKarol Wachowski } 59*103d2ea1SKarol Wachowski 60*103d2ea1SKarol Wachowski static void ivpu_mmu_pgtables_free(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable) 61263b2ba5SJacek Lawrynowicz { 62a2fd4a6fSKarol Wachowski int pgd_idx, pud_idx, pmd_idx; 63*103d2ea1SKarol Wachowski dma_addr_t pud_dma, pmd_dma, pte_dma; 64*103d2ea1SKarol Wachowski u64 *pud_dma_ptr, *pmd_dma_ptr, *pte_dma_ptr; 65263b2ba5SJacek Lawrynowicz 66a2fd4a6fSKarol Wachowski for (pgd_idx = 0; pgd_idx < IVPU_MMU_PGTABLE_ENTRIES; ++pgd_idx) { 67*103d2ea1SKarol Wachowski pud_dma_ptr = pgtable->pud_ptrs[pgd_idx]; 68*103d2ea1SKarol Wachowski pud_dma = pgtable->pgd_dma_ptr[pgd_idx]; 69a2fd4a6fSKarol Wachowski 70*103d2ea1SKarol Wachowski if (!pud_dma_ptr) 71a2fd4a6fSKarol Wachowski continue; 72a2fd4a6fSKarol Wachowski 73a2fd4a6fSKarol Wachowski for (pud_idx = 0; pud_idx < IVPU_MMU_PGTABLE_ENTRIES; ++pud_idx) { 74*103d2ea1SKarol Wachowski pmd_dma_ptr = pgtable->pmd_ptrs[pgd_idx][pud_idx]; 75*103d2ea1SKarol Wachowski pmd_dma = pgtable->pud_ptrs[pgd_idx][pud_idx]; 76263b2ba5SJacek Lawrynowicz 77*103d2ea1SKarol Wachowski if (!pmd_dma_ptr) 78263b2ba5SJacek Lawrynowicz continue; 79263b2ba5SJacek Lawrynowicz 80a2fd4a6fSKarol Wachowski for (pmd_idx = 0; pmd_idx < IVPU_MMU_PGTABLE_ENTRIES; ++pmd_idx) { 81*103d2ea1SKarol Wachowski pte_dma_ptr = pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx]; 82*103d2ea1SKarol Wachowski pte_dma = pgtable->pmd_ptrs[pgd_idx][pud_idx][pmd_idx]; 83*103d2ea1SKarol Wachowski 84*103d2ea1SKarol Wachowski ivpu_mmu_pgtable_free(vdev, pte_dma_ptr, pte_dma); 85263b2ba5SJacek Lawrynowicz } 86263b2ba5SJacek Lawrynowicz 87*103d2ea1SKarol Wachowski kfree(pgtable->pte_ptrs[pgd_idx][pud_idx]); 88*103d2ea1SKarol Wachowski ivpu_mmu_pgtable_free(vdev, pmd_dma_ptr, pmd_dma); 89a2fd4a6fSKarol Wachowski } 90a2fd4a6fSKarol Wachowski 91*103d2ea1SKarol Wachowski kfree(pgtable->pmd_ptrs[pgd_idx]); 92*103d2ea1SKarol Wachowski kfree(pgtable->pte_ptrs[pgd_idx]); 93*103d2ea1SKarol Wachowski ivpu_mmu_pgtable_free(vdev, pud_dma_ptr, pud_dma); 94263b2ba5SJacek Lawrynowicz } 95263b2ba5SJacek Lawrynowicz 96*103d2ea1SKarol Wachowski ivpu_mmu_pgtable_free(vdev, pgtable->pgd_dma_ptr, pgtable->pgd_dma); 97263b2ba5SJacek Lawrynowicz } 98263b2ba5SJacek Lawrynowicz 99263b2ba5SJacek Lawrynowicz static u64* 100a2fd4a6fSKarol Wachowski ivpu_mmu_ensure_pud(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, int pgd_idx) 101a2fd4a6fSKarol Wachowski { 102*103d2ea1SKarol Wachowski u64 *pud_dma_ptr = pgtable->pud_ptrs[pgd_idx]; 103a2fd4a6fSKarol Wachowski dma_addr_t pud_dma; 104a2fd4a6fSKarol Wachowski 105*103d2ea1SKarol Wachowski if (pud_dma_ptr) 106*103d2ea1SKarol Wachowski return pud_dma_ptr; 107a2fd4a6fSKarol Wachowski 108*103d2ea1SKarol Wachowski pud_dma_ptr = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pud_dma, GFP_KERNEL); 109*103d2ea1SKarol Wachowski if (!pud_dma_ptr) 110a2fd4a6fSKarol Wachowski return NULL; 111a2fd4a6fSKarol Wachowski 112*103d2ea1SKarol Wachowski drm_WARN_ON(&vdev->drm, pgtable->pmd_ptrs[pgd_idx]); 113*103d2ea1SKarol Wachowski pgtable->pmd_ptrs[pgd_idx] = kzalloc(IVPU_MMU_PGTABLE_SIZE, GFP_KERNEL); 114*103d2ea1SKarol Wachowski if (!pgtable->pmd_ptrs[pgd_idx]) 115*103d2ea1SKarol Wachowski goto err_free_pud_dma_ptr; 116a2fd4a6fSKarol Wachowski 117*103d2ea1SKarol Wachowski drm_WARN_ON(&vdev->drm, pgtable->pte_ptrs[pgd_idx]); 118*103d2ea1SKarol Wachowski pgtable->pte_ptrs[pgd_idx] = kzalloc(IVPU_MMU_PGTABLE_SIZE, GFP_KERNEL); 119*103d2ea1SKarol Wachowski if (!pgtable->pte_ptrs[pgd_idx]) 120*103d2ea1SKarol Wachowski goto err_free_pmd_ptrs; 121a2fd4a6fSKarol Wachowski 122*103d2ea1SKarol Wachowski pgtable->pud_ptrs[pgd_idx] = pud_dma_ptr; 123*103d2ea1SKarol Wachowski pgtable->pgd_dma_ptr[pgd_idx] = pud_dma | IVPU_MMU_ENTRY_VALID; 124a2fd4a6fSKarol Wachowski 125*103d2ea1SKarol Wachowski return pud_dma_ptr; 126a2fd4a6fSKarol Wachowski 127*103d2ea1SKarol Wachowski err_free_pmd_ptrs: 128*103d2ea1SKarol Wachowski kfree(pgtable->pmd_ptrs[pgd_idx]); 129a2fd4a6fSKarol Wachowski 130*103d2ea1SKarol Wachowski err_free_pud_dma_ptr: 131*103d2ea1SKarol Wachowski ivpu_mmu_pgtable_free(vdev, pud_dma_ptr, pud_dma); 132a2fd4a6fSKarol Wachowski return NULL; 133a2fd4a6fSKarol Wachowski } 134a2fd4a6fSKarol Wachowski 135a2fd4a6fSKarol Wachowski static u64* 136*103d2ea1SKarol Wachowski ivpu_mmu_ensure_pmd(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, int pgd_idx, 137*103d2ea1SKarol Wachowski int pud_idx) 138263b2ba5SJacek Lawrynowicz { 139*103d2ea1SKarol Wachowski u64 *pmd_dma_ptr = pgtable->pmd_ptrs[pgd_idx][pud_idx]; 140263b2ba5SJacek Lawrynowicz dma_addr_t pmd_dma; 141263b2ba5SJacek Lawrynowicz 142*103d2ea1SKarol Wachowski if (pmd_dma_ptr) 143*103d2ea1SKarol Wachowski return pmd_dma_ptr; 144263b2ba5SJacek Lawrynowicz 145*103d2ea1SKarol Wachowski pmd_dma_ptr = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pmd_dma, GFP_KERNEL); 146*103d2ea1SKarol Wachowski if (!pmd_dma_ptr) 147263b2ba5SJacek Lawrynowicz return NULL; 148263b2ba5SJacek Lawrynowicz 149*103d2ea1SKarol Wachowski drm_WARN_ON(&vdev->drm, pgtable->pte_ptrs[pgd_idx][pud_idx]); 150*103d2ea1SKarol Wachowski pgtable->pte_ptrs[pgd_idx][pud_idx] = kzalloc(IVPU_MMU_PGTABLE_SIZE, GFP_KERNEL); 151*103d2ea1SKarol Wachowski if (!pgtable->pte_ptrs[pgd_idx][pud_idx]) 152*103d2ea1SKarol Wachowski goto err_free_pmd_dma_ptr; 153263b2ba5SJacek Lawrynowicz 154*103d2ea1SKarol Wachowski pgtable->pmd_ptrs[pgd_idx][pud_idx] = pmd_dma_ptr; 155*103d2ea1SKarol Wachowski pgtable->pud_ptrs[pgd_idx][pud_idx] = pmd_dma | IVPU_MMU_ENTRY_VALID; 156263b2ba5SJacek Lawrynowicz 157*103d2ea1SKarol Wachowski return pmd_dma_ptr; 158263b2ba5SJacek Lawrynowicz 159*103d2ea1SKarol Wachowski err_free_pmd_dma_ptr: 160*103d2ea1SKarol Wachowski ivpu_mmu_pgtable_free(vdev, pmd_dma_ptr, pmd_dma); 161263b2ba5SJacek Lawrynowicz return NULL; 162263b2ba5SJacek Lawrynowicz } 163263b2ba5SJacek Lawrynowicz 164263b2ba5SJacek Lawrynowicz static u64* 165263b2ba5SJacek Lawrynowicz ivpu_mmu_ensure_pte(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, 166a2fd4a6fSKarol Wachowski int pgd_idx, int pud_idx, int pmd_idx) 167263b2ba5SJacek Lawrynowicz { 168*103d2ea1SKarol Wachowski u64 *pte_dma_ptr = pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx]; 169263b2ba5SJacek Lawrynowicz dma_addr_t pte_dma; 170263b2ba5SJacek Lawrynowicz 171*103d2ea1SKarol Wachowski if (pte_dma_ptr) 172*103d2ea1SKarol Wachowski return pte_dma_ptr; 173263b2ba5SJacek Lawrynowicz 174*103d2ea1SKarol Wachowski pte_dma_ptr = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pte_dma, GFP_KERNEL); 175*103d2ea1SKarol Wachowski if (!pte_dma_ptr) 176263b2ba5SJacek Lawrynowicz return NULL; 177263b2ba5SJacek Lawrynowicz 178*103d2ea1SKarol Wachowski pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx] = pte_dma_ptr; 179*103d2ea1SKarol Wachowski pgtable->pmd_ptrs[pgd_idx][pud_idx][pmd_idx] = pte_dma | IVPU_MMU_ENTRY_VALID; 180263b2ba5SJacek Lawrynowicz 181*103d2ea1SKarol Wachowski return pte_dma_ptr; 182263b2ba5SJacek Lawrynowicz } 183263b2ba5SJacek Lawrynowicz 184263b2ba5SJacek Lawrynowicz static int 185263b2ba5SJacek Lawrynowicz ivpu_mmu_context_map_page(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, 186263b2ba5SJacek Lawrynowicz u64 vpu_addr, dma_addr_t dma_addr, int prot) 187263b2ba5SJacek Lawrynowicz { 188263b2ba5SJacek Lawrynowicz u64 *pte; 189a2fd4a6fSKarol Wachowski int pgd_idx = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr); 190a2fd4a6fSKarol Wachowski int pud_idx = FIELD_GET(IVPU_MMU_PUD_INDEX_MASK, vpu_addr); 191a2fd4a6fSKarol Wachowski int pmd_idx = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr); 192a2fd4a6fSKarol Wachowski int pte_idx = FIELD_GET(IVPU_MMU_PTE_INDEX_MASK, vpu_addr); 193a2fd4a6fSKarol Wachowski 194*103d2ea1SKarol Wachowski /* Allocate PUD - second level page table if needed */ 195a2fd4a6fSKarol Wachowski if (!ivpu_mmu_ensure_pud(vdev, &ctx->pgtable, pgd_idx)) 196a2fd4a6fSKarol Wachowski return -ENOMEM; 197263b2ba5SJacek Lawrynowicz 198*103d2ea1SKarol Wachowski /* Allocate PMD - third level page table if needed */ 199a2fd4a6fSKarol Wachowski if (!ivpu_mmu_ensure_pmd(vdev, &ctx->pgtable, pgd_idx, pud_idx)) 200263b2ba5SJacek Lawrynowicz return -ENOMEM; 201263b2ba5SJacek Lawrynowicz 202*103d2ea1SKarol Wachowski /* Allocate PTE - fourth level page table if needed */ 203a2fd4a6fSKarol Wachowski pte = ivpu_mmu_ensure_pte(vdev, &ctx->pgtable, pgd_idx, pud_idx, pmd_idx); 204263b2ba5SJacek Lawrynowicz if (!pte) 205263b2ba5SJacek Lawrynowicz return -ENOMEM; 206263b2ba5SJacek Lawrynowicz 207*103d2ea1SKarol Wachowski /* Update PTE */ 208a2fd4a6fSKarol Wachowski pte[pte_idx] = dma_addr | prot; 209263b2ba5SJacek Lawrynowicz 210263b2ba5SJacek Lawrynowicz return 0; 211263b2ba5SJacek Lawrynowicz } 212263b2ba5SJacek Lawrynowicz 213263b2ba5SJacek Lawrynowicz static void ivpu_mmu_context_unmap_page(struct ivpu_mmu_context *ctx, u64 vpu_addr) 214263b2ba5SJacek Lawrynowicz { 215a2fd4a6fSKarol Wachowski int pgd_idx = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr); 216a2fd4a6fSKarol Wachowski int pud_idx = FIELD_GET(IVPU_MMU_PUD_INDEX_MASK, vpu_addr); 217a2fd4a6fSKarol Wachowski int pmd_idx = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr); 218a2fd4a6fSKarol Wachowski int pte_idx = FIELD_GET(IVPU_MMU_PTE_INDEX_MASK, vpu_addr); 219263b2ba5SJacek Lawrynowicz 220263b2ba5SJacek Lawrynowicz /* Update PTE with dummy physical address and clear flags */ 221*103d2ea1SKarol Wachowski ctx->pgtable.pte_ptrs[pgd_idx][pud_idx][pmd_idx][pte_idx] = IVPU_MMU_ENTRY_INVALID; 222263b2ba5SJacek Lawrynowicz } 223263b2ba5SJacek Lawrynowicz 224263b2ba5SJacek Lawrynowicz static void 225263b2ba5SJacek Lawrynowicz ivpu_mmu_context_flush_page_tables(struct ivpu_mmu_context *ctx, u64 vpu_addr, size_t size) 226263b2ba5SJacek Lawrynowicz { 227*103d2ea1SKarol Wachowski struct ivpu_mmu_pgtable *pgtable = &ctx->pgtable; 228263b2ba5SJacek Lawrynowicz u64 end_addr = vpu_addr + size; 229263b2ba5SJacek Lawrynowicz 230263b2ba5SJacek Lawrynowicz /* Align to PMD entry (2 MB) */ 231263b2ba5SJacek Lawrynowicz vpu_addr &= ~(IVPU_MMU_PTE_MAP_SIZE - 1); 232*103d2ea1SKarol Wachowski 233263b2ba5SJacek Lawrynowicz while (vpu_addr < end_addr) { 234a2fd4a6fSKarol Wachowski int pgd_idx = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr); 235a2fd4a6fSKarol Wachowski u64 pud_end = (pgd_idx + 1) * (u64)IVPU_MMU_PUD_MAP_SIZE; 236a2fd4a6fSKarol Wachowski 237a2fd4a6fSKarol Wachowski while (vpu_addr < end_addr && vpu_addr < pud_end) { 238a2fd4a6fSKarol Wachowski int pud_idx = FIELD_GET(IVPU_MMU_PUD_INDEX_MASK, vpu_addr); 239a2fd4a6fSKarol Wachowski u64 pmd_end = (pud_idx + 1) * (u64)IVPU_MMU_PMD_MAP_SIZE; 240263b2ba5SJacek Lawrynowicz 241263b2ba5SJacek Lawrynowicz while (vpu_addr < end_addr && vpu_addr < pmd_end) { 242a2fd4a6fSKarol Wachowski int pmd_idx = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr); 243263b2ba5SJacek Lawrynowicz 244*103d2ea1SKarol Wachowski clflush_cache_range(pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx], 245*103d2ea1SKarol Wachowski IVPU_MMU_PGTABLE_SIZE); 246263b2ba5SJacek Lawrynowicz vpu_addr += IVPU_MMU_PTE_MAP_SIZE; 247263b2ba5SJacek Lawrynowicz } 248*103d2ea1SKarol Wachowski clflush_cache_range(pgtable->pmd_ptrs[pgd_idx][pud_idx], 249*103d2ea1SKarol Wachowski IVPU_MMU_PGTABLE_SIZE); 250263b2ba5SJacek Lawrynowicz } 251*103d2ea1SKarol Wachowski clflush_cache_range(pgtable->pud_ptrs[pgd_idx], IVPU_MMU_PGTABLE_SIZE); 252a2fd4a6fSKarol Wachowski } 253*103d2ea1SKarol Wachowski clflush_cache_range(pgtable->pgd_dma_ptr, IVPU_MMU_PGTABLE_SIZE); 254263b2ba5SJacek Lawrynowicz } 255263b2ba5SJacek Lawrynowicz 256263b2ba5SJacek Lawrynowicz static int 257263b2ba5SJacek Lawrynowicz ivpu_mmu_context_map_pages(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, 258263b2ba5SJacek Lawrynowicz u64 vpu_addr, dma_addr_t dma_addr, size_t size, int prot) 259263b2ba5SJacek Lawrynowicz { 260263b2ba5SJacek Lawrynowicz while (size) { 261263b2ba5SJacek Lawrynowicz int ret = ivpu_mmu_context_map_page(vdev, ctx, vpu_addr, dma_addr, prot); 262263b2ba5SJacek Lawrynowicz 263263b2ba5SJacek Lawrynowicz if (ret) 264263b2ba5SJacek Lawrynowicz return ret; 265263b2ba5SJacek Lawrynowicz 266263b2ba5SJacek Lawrynowicz vpu_addr += IVPU_MMU_PAGE_SIZE; 267263b2ba5SJacek Lawrynowicz dma_addr += IVPU_MMU_PAGE_SIZE; 268263b2ba5SJacek Lawrynowicz size -= IVPU_MMU_PAGE_SIZE; 269263b2ba5SJacek Lawrynowicz } 270263b2ba5SJacek Lawrynowicz 271263b2ba5SJacek Lawrynowicz return 0; 272263b2ba5SJacek Lawrynowicz } 273263b2ba5SJacek Lawrynowicz 274263b2ba5SJacek Lawrynowicz static void ivpu_mmu_context_unmap_pages(struct ivpu_mmu_context *ctx, u64 vpu_addr, size_t size) 275263b2ba5SJacek Lawrynowicz { 276263b2ba5SJacek Lawrynowicz while (size) { 277263b2ba5SJacek Lawrynowicz ivpu_mmu_context_unmap_page(ctx, vpu_addr); 278263b2ba5SJacek Lawrynowicz vpu_addr += IVPU_MMU_PAGE_SIZE; 279263b2ba5SJacek Lawrynowicz size -= IVPU_MMU_PAGE_SIZE; 280263b2ba5SJacek Lawrynowicz } 281263b2ba5SJacek Lawrynowicz } 282263b2ba5SJacek Lawrynowicz 283263b2ba5SJacek Lawrynowicz int 284263b2ba5SJacek Lawrynowicz ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, 285263b2ba5SJacek Lawrynowicz u64 vpu_addr, struct sg_table *sgt, bool llc_coherent) 286263b2ba5SJacek Lawrynowicz { 287263b2ba5SJacek Lawrynowicz struct scatterlist *sg; 288263b2ba5SJacek Lawrynowicz int prot; 289263b2ba5SJacek Lawrynowicz int ret; 290263b2ba5SJacek Lawrynowicz u64 i; 291263b2ba5SJacek Lawrynowicz 292263b2ba5SJacek Lawrynowicz if (!IS_ALIGNED(vpu_addr, IVPU_MMU_PAGE_SIZE)) 293263b2ba5SJacek Lawrynowicz return -EINVAL; 294263b2ba5SJacek Lawrynowicz /* 295263b2ba5SJacek Lawrynowicz * VPU is only 32 bit, but DMA engine is 38 bit 296263b2ba5SJacek Lawrynowicz * Ranges < 2 GB are reserved for VPU internal registers 297263b2ba5SJacek Lawrynowicz * Limit range to 8 GB 298263b2ba5SJacek Lawrynowicz */ 299263b2ba5SJacek Lawrynowicz if (vpu_addr < SZ_2G || vpu_addr > SZ_8G) 300263b2ba5SJacek Lawrynowicz return -EINVAL; 301263b2ba5SJacek Lawrynowicz 302263b2ba5SJacek Lawrynowicz prot = IVPU_MMU_ENTRY_MAPPED; 303263b2ba5SJacek Lawrynowicz if (llc_coherent) 304263b2ba5SJacek Lawrynowicz prot |= IVPU_MMU_ENTRY_FLAG_LLC_COHERENT; 305263b2ba5SJacek Lawrynowicz 306263b2ba5SJacek Lawrynowicz mutex_lock(&ctx->lock); 307263b2ba5SJacek Lawrynowicz 308263b2ba5SJacek Lawrynowicz for_each_sgtable_dma_sg(sgt, sg, i) { 309*103d2ea1SKarol Wachowski dma_addr_t dma_addr = sg_dma_address(sg) - sg->offset; 310263b2ba5SJacek Lawrynowicz size_t size = sg_dma_len(sg) + sg->offset; 311263b2ba5SJacek Lawrynowicz 312263b2ba5SJacek Lawrynowicz ret = ivpu_mmu_context_map_pages(vdev, ctx, vpu_addr, dma_addr, size, prot); 313263b2ba5SJacek Lawrynowicz if (ret) { 314263b2ba5SJacek Lawrynowicz ivpu_err(vdev, "Failed to map context pages\n"); 315263b2ba5SJacek Lawrynowicz mutex_unlock(&ctx->lock); 316263b2ba5SJacek Lawrynowicz return ret; 317263b2ba5SJacek Lawrynowicz } 318263b2ba5SJacek Lawrynowicz ivpu_mmu_context_flush_page_tables(ctx, vpu_addr, size); 319263b2ba5SJacek Lawrynowicz vpu_addr += size; 320263b2ba5SJacek Lawrynowicz } 321263b2ba5SJacek Lawrynowicz 322263b2ba5SJacek Lawrynowicz mutex_unlock(&ctx->lock); 323263b2ba5SJacek Lawrynowicz 324263b2ba5SJacek Lawrynowicz ret = ivpu_mmu_invalidate_tlb(vdev, ctx->id); 325263b2ba5SJacek Lawrynowicz if (ret) 326263b2ba5SJacek Lawrynowicz ivpu_err(vdev, "Failed to invalidate TLB for ctx %u: %d\n", ctx->id, ret); 327263b2ba5SJacek Lawrynowicz return ret; 328263b2ba5SJacek Lawrynowicz } 329263b2ba5SJacek Lawrynowicz 330263b2ba5SJacek Lawrynowicz void 331263b2ba5SJacek Lawrynowicz ivpu_mmu_context_unmap_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, 332263b2ba5SJacek Lawrynowicz u64 vpu_addr, struct sg_table *sgt) 333263b2ba5SJacek Lawrynowicz { 334263b2ba5SJacek Lawrynowicz struct scatterlist *sg; 335263b2ba5SJacek Lawrynowicz int ret; 336263b2ba5SJacek Lawrynowicz u64 i; 337263b2ba5SJacek Lawrynowicz 338263b2ba5SJacek Lawrynowicz if (!IS_ALIGNED(vpu_addr, IVPU_MMU_PAGE_SIZE)) 339263b2ba5SJacek Lawrynowicz ivpu_warn(vdev, "Unaligned vpu_addr: 0x%llx\n", vpu_addr); 340263b2ba5SJacek Lawrynowicz 341263b2ba5SJacek Lawrynowicz mutex_lock(&ctx->lock); 342263b2ba5SJacek Lawrynowicz 343263b2ba5SJacek Lawrynowicz for_each_sgtable_dma_sg(sgt, sg, i) { 344263b2ba5SJacek Lawrynowicz size_t size = sg_dma_len(sg) + sg->offset; 345263b2ba5SJacek Lawrynowicz 346263b2ba5SJacek Lawrynowicz ivpu_mmu_context_unmap_pages(ctx, vpu_addr, size); 347263b2ba5SJacek Lawrynowicz ivpu_mmu_context_flush_page_tables(ctx, vpu_addr, size); 348263b2ba5SJacek Lawrynowicz vpu_addr += size; 349263b2ba5SJacek Lawrynowicz } 350263b2ba5SJacek Lawrynowicz 351263b2ba5SJacek Lawrynowicz mutex_unlock(&ctx->lock); 352263b2ba5SJacek Lawrynowicz 353263b2ba5SJacek Lawrynowicz ret = ivpu_mmu_invalidate_tlb(vdev, ctx->id); 354263b2ba5SJacek Lawrynowicz if (ret) 355263b2ba5SJacek Lawrynowicz ivpu_warn(vdev, "Failed to invalidate TLB for ctx %u: %d\n", ctx->id, ret); 356263b2ba5SJacek Lawrynowicz } 357263b2ba5SJacek Lawrynowicz 358263b2ba5SJacek Lawrynowicz int 359263b2ba5SJacek Lawrynowicz ivpu_mmu_context_insert_node_locked(struct ivpu_mmu_context *ctx, 360263b2ba5SJacek Lawrynowicz const struct ivpu_addr_range *range, 361263b2ba5SJacek Lawrynowicz u64 size, struct drm_mm_node *node) 362263b2ba5SJacek Lawrynowicz { 363263b2ba5SJacek Lawrynowicz lockdep_assert_held(&ctx->lock); 364263b2ba5SJacek Lawrynowicz 365263b2ba5SJacek Lawrynowicz return drm_mm_insert_node_in_range(&ctx->mm, node, size, IVPU_MMU_PAGE_SIZE, 366263b2ba5SJacek Lawrynowicz 0, range->start, range->end, DRM_MM_INSERT_BEST); 367263b2ba5SJacek Lawrynowicz } 368263b2ba5SJacek Lawrynowicz 369263b2ba5SJacek Lawrynowicz void 370263b2ba5SJacek Lawrynowicz ivpu_mmu_context_remove_node_locked(struct ivpu_mmu_context *ctx, struct drm_mm_node *node) 371263b2ba5SJacek Lawrynowicz { 372263b2ba5SJacek Lawrynowicz lockdep_assert_held(&ctx->lock); 373263b2ba5SJacek Lawrynowicz 374263b2ba5SJacek Lawrynowicz drm_mm_remove_node(node); 375263b2ba5SJacek Lawrynowicz } 376263b2ba5SJacek Lawrynowicz 377263b2ba5SJacek Lawrynowicz static int 378263b2ba5SJacek Lawrynowicz ivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 context_id) 379263b2ba5SJacek Lawrynowicz { 380263b2ba5SJacek Lawrynowicz u64 start, end; 381263b2ba5SJacek Lawrynowicz int ret; 382263b2ba5SJacek Lawrynowicz 383263b2ba5SJacek Lawrynowicz mutex_init(&ctx->lock); 384263b2ba5SJacek Lawrynowicz INIT_LIST_HEAD(&ctx->bo_list); 385263b2ba5SJacek Lawrynowicz 386263b2ba5SJacek Lawrynowicz ret = ivpu_mmu_pgtable_init(vdev, &ctx->pgtable); 387263b2ba5SJacek Lawrynowicz if (ret) 388263b2ba5SJacek Lawrynowicz return ret; 389263b2ba5SJacek Lawrynowicz 390263b2ba5SJacek Lawrynowicz if (!context_id) { 391263b2ba5SJacek Lawrynowicz start = vdev->hw->ranges.global_low.start; 392263b2ba5SJacek Lawrynowicz end = vdev->hw->ranges.global_high.end; 393263b2ba5SJacek Lawrynowicz } else { 394263b2ba5SJacek Lawrynowicz start = vdev->hw->ranges.user_low.start; 395263b2ba5SJacek Lawrynowicz end = vdev->hw->ranges.user_high.end; 396263b2ba5SJacek Lawrynowicz } 397263b2ba5SJacek Lawrynowicz 398263b2ba5SJacek Lawrynowicz drm_mm_init(&ctx->mm, start, end - start); 399263b2ba5SJacek Lawrynowicz ctx->id = context_id; 400263b2ba5SJacek Lawrynowicz 401263b2ba5SJacek Lawrynowicz return 0; 402263b2ba5SJacek Lawrynowicz } 403263b2ba5SJacek Lawrynowicz 404263b2ba5SJacek Lawrynowicz static void ivpu_mmu_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx) 405263b2ba5SJacek Lawrynowicz { 406*103d2ea1SKarol Wachowski if (drm_WARN_ON(&vdev->drm, !ctx->pgtable.pgd_dma_ptr)) 407*103d2ea1SKarol Wachowski return; 408263b2ba5SJacek Lawrynowicz 409263b2ba5SJacek Lawrynowicz mutex_destroy(&ctx->lock); 410*103d2ea1SKarol Wachowski ivpu_mmu_pgtables_free(vdev, &ctx->pgtable); 411263b2ba5SJacek Lawrynowicz drm_mm_takedown(&ctx->mm); 412*103d2ea1SKarol Wachowski 413*103d2ea1SKarol Wachowski ctx->pgtable.pgd_dma_ptr = NULL; 414*103d2ea1SKarol Wachowski ctx->pgtable.pgd_dma = 0; 415263b2ba5SJacek Lawrynowicz } 416263b2ba5SJacek Lawrynowicz 417263b2ba5SJacek Lawrynowicz int ivpu_mmu_global_context_init(struct ivpu_device *vdev) 418263b2ba5SJacek Lawrynowicz { 419263b2ba5SJacek Lawrynowicz return ivpu_mmu_context_init(vdev, &vdev->gctx, IVPU_GLOBAL_CONTEXT_MMU_SSID); 420263b2ba5SJacek Lawrynowicz } 421263b2ba5SJacek Lawrynowicz 422263b2ba5SJacek Lawrynowicz void ivpu_mmu_global_context_fini(struct ivpu_device *vdev) 423263b2ba5SJacek Lawrynowicz { 424263b2ba5SJacek Lawrynowicz return ivpu_mmu_context_fini(vdev, &vdev->gctx); 425263b2ba5SJacek Lawrynowicz } 426263b2ba5SJacek Lawrynowicz 427263b2ba5SJacek Lawrynowicz void ivpu_mmu_user_context_mark_invalid(struct ivpu_device *vdev, u32 ssid) 428263b2ba5SJacek Lawrynowicz { 429263b2ba5SJacek Lawrynowicz struct ivpu_file_priv *file_priv; 430263b2ba5SJacek Lawrynowicz 431263b2ba5SJacek Lawrynowicz xa_lock(&vdev->context_xa); 432263b2ba5SJacek Lawrynowicz 433263b2ba5SJacek Lawrynowicz file_priv = xa_load(&vdev->context_xa, ssid); 434263b2ba5SJacek Lawrynowicz if (file_priv) 435263b2ba5SJacek Lawrynowicz file_priv->has_mmu_faults = true; 436263b2ba5SJacek Lawrynowicz 437263b2ba5SJacek Lawrynowicz xa_unlock(&vdev->context_xa); 438263b2ba5SJacek Lawrynowicz } 439263b2ba5SJacek Lawrynowicz 440263b2ba5SJacek Lawrynowicz int ivpu_mmu_user_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 ctx_id) 441263b2ba5SJacek Lawrynowicz { 442263b2ba5SJacek Lawrynowicz int ret; 443263b2ba5SJacek Lawrynowicz 444263b2ba5SJacek Lawrynowicz drm_WARN_ON(&vdev->drm, !ctx_id); 445263b2ba5SJacek Lawrynowicz 446263b2ba5SJacek Lawrynowicz ret = ivpu_mmu_context_init(vdev, ctx, ctx_id); 447263b2ba5SJacek Lawrynowicz if (ret) { 448263b2ba5SJacek Lawrynowicz ivpu_err(vdev, "Failed to initialize context: %d\n", ret); 449263b2ba5SJacek Lawrynowicz return ret; 450263b2ba5SJacek Lawrynowicz } 451263b2ba5SJacek Lawrynowicz 452263b2ba5SJacek Lawrynowicz ret = ivpu_mmu_set_pgtable(vdev, ctx_id, &ctx->pgtable); 453263b2ba5SJacek Lawrynowicz if (ret) { 454263b2ba5SJacek Lawrynowicz ivpu_err(vdev, "Failed to set page table: %d\n", ret); 455263b2ba5SJacek Lawrynowicz goto err_context_fini; 456263b2ba5SJacek Lawrynowicz } 457263b2ba5SJacek Lawrynowicz 458263b2ba5SJacek Lawrynowicz return 0; 459263b2ba5SJacek Lawrynowicz 460263b2ba5SJacek Lawrynowicz err_context_fini: 461263b2ba5SJacek Lawrynowicz ivpu_mmu_context_fini(vdev, ctx); 462263b2ba5SJacek Lawrynowicz return ret; 463263b2ba5SJacek Lawrynowicz } 464263b2ba5SJacek Lawrynowicz 465263b2ba5SJacek Lawrynowicz void ivpu_mmu_user_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx) 466263b2ba5SJacek Lawrynowicz { 467263b2ba5SJacek Lawrynowicz drm_WARN_ON(&vdev->drm, !ctx->id); 468263b2ba5SJacek Lawrynowicz 469263b2ba5SJacek Lawrynowicz ivpu_mmu_clear_pgtable(vdev, ctx->id); 470263b2ba5SJacek Lawrynowicz ivpu_mmu_context_fini(vdev, ctx); 471263b2ba5SJacek Lawrynowicz } 472