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) 1895d44018SKarol Wachowski #define IVPU_MMU_ENTRY_FLAGS_MASK (BIT(52) | GENMASK(11, 0)) 1995d44018SKarol Wachowski #define IVPU_MMU_ENTRY_FLAG_CONT BIT(52) 20263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_NG BIT(11) 21263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_AF BIT(10) 22263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_USER BIT(6) 23263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_LLC_COHERENT BIT(2) 24263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_TYPE_PAGE BIT(1) 25263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_VALID BIT(0) 26263b2ba5SJacek Lawrynowicz 27263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PAGE_SIZE SZ_4K 2895d44018SKarol Wachowski #define IVPU_MMU_CONT_PAGES_SIZE (IVPU_MMU_PAGE_SIZE * 16) 29263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PTE_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PAGE_SIZE) 30263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PMD_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PTE_MAP_SIZE) 31a2fd4a6fSKarol Wachowski #define IVPU_MMU_PUD_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PMD_MAP_SIZE) 32a2fd4a6fSKarol Wachowski #define IVPU_MMU_PGD_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PUD_MAP_SIZE) 33263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PGTABLE_SIZE (IVPU_MMU_PGTABLE_ENTRIES * sizeof(u64)) 34263b2ba5SJacek Lawrynowicz 35263b2ba5SJacek Lawrynowicz #define IVPU_MMU_DUMMY_ADDRESS 0xdeadb000 36263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_VALID (IVPU_MMU_ENTRY_FLAG_TYPE_PAGE | IVPU_MMU_ENTRY_FLAG_VALID) 37263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_INVALID (IVPU_MMU_DUMMY_ADDRESS & ~IVPU_MMU_ENTRY_FLAGS_MASK) 38263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_MAPPED (IVPU_MMU_ENTRY_FLAG_AF | IVPU_MMU_ENTRY_FLAG_USER | \ 39263b2ba5SJacek Lawrynowicz IVPU_MMU_ENTRY_FLAG_NG | IVPU_MMU_ENTRY_VALID) 40263b2ba5SJacek Lawrynowicz 41263b2ba5SJacek Lawrynowicz static int ivpu_mmu_pgtable_init(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable) 42263b2ba5SJacek Lawrynowicz { 43263b2ba5SJacek Lawrynowicz dma_addr_t pgd_dma; 44263b2ba5SJacek Lawrynowicz 45103d2ea1SKarol Wachowski pgtable->pgd_dma_ptr = dma_alloc_coherent(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pgd_dma, 46103d2ea1SKarol Wachowski GFP_KERNEL); 47103d2ea1SKarol Wachowski if (!pgtable->pgd_dma_ptr) 48263b2ba5SJacek Lawrynowicz return -ENOMEM; 49263b2ba5SJacek Lawrynowicz 50263b2ba5SJacek Lawrynowicz pgtable->pgd_dma = pgd_dma; 51263b2ba5SJacek Lawrynowicz 52263b2ba5SJacek Lawrynowicz return 0; 53263b2ba5SJacek Lawrynowicz } 54263b2ba5SJacek Lawrynowicz 55103d2ea1SKarol Wachowski static void ivpu_mmu_pgtable_free(struct ivpu_device *vdev, u64 *cpu_addr, dma_addr_t dma_addr) 56103d2ea1SKarol Wachowski { 57103d2ea1SKarol Wachowski if (cpu_addr) 58103d2ea1SKarol Wachowski dma_free_coherent(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, cpu_addr, 59103d2ea1SKarol Wachowski dma_addr & ~IVPU_MMU_ENTRY_FLAGS_MASK); 60103d2ea1SKarol Wachowski } 61103d2ea1SKarol Wachowski 62103d2ea1SKarol Wachowski static void ivpu_mmu_pgtables_free(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable) 63263b2ba5SJacek Lawrynowicz { 64a2fd4a6fSKarol Wachowski int pgd_idx, pud_idx, pmd_idx; 65103d2ea1SKarol Wachowski dma_addr_t pud_dma, pmd_dma, pte_dma; 66103d2ea1SKarol Wachowski u64 *pud_dma_ptr, *pmd_dma_ptr, *pte_dma_ptr; 67263b2ba5SJacek Lawrynowicz 68a2fd4a6fSKarol Wachowski for (pgd_idx = 0; pgd_idx < IVPU_MMU_PGTABLE_ENTRIES; ++pgd_idx) { 69103d2ea1SKarol Wachowski pud_dma_ptr = pgtable->pud_ptrs[pgd_idx]; 70103d2ea1SKarol Wachowski pud_dma = pgtable->pgd_dma_ptr[pgd_idx]; 71a2fd4a6fSKarol Wachowski 72103d2ea1SKarol Wachowski if (!pud_dma_ptr) 73a2fd4a6fSKarol Wachowski continue; 74a2fd4a6fSKarol Wachowski 75a2fd4a6fSKarol Wachowski for (pud_idx = 0; pud_idx < IVPU_MMU_PGTABLE_ENTRIES; ++pud_idx) { 76103d2ea1SKarol Wachowski pmd_dma_ptr = pgtable->pmd_ptrs[pgd_idx][pud_idx]; 77103d2ea1SKarol Wachowski pmd_dma = pgtable->pud_ptrs[pgd_idx][pud_idx]; 78263b2ba5SJacek Lawrynowicz 79103d2ea1SKarol Wachowski if (!pmd_dma_ptr) 80263b2ba5SJacek Lawrynowicz continue; 81263b2ba5SJacek Lawrynowicz 82a2fd4a6fSKarol Wachowski for (pmd_idx = 0; pmd_idx < IVPU_MMU_PGTABLE_ENTRIES; ++pmd_idx) { 83103d2ea1SKarol Wachowski pte_dma_ptr = pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx]; 84103d2ea1SKarol Wachowski pte_dma = pgtable->pmd_ptrs[pgd_idx][pud_idx][pmd_idx]; 85103d2ea1SKarol Wachowski 86103d2ea1SKarol Wachowski ivpu_mmu_pgtable_free(vdev, pte_dma_ptr, pte_dma); 87263b2ba5SJacek Lawrynowicz } 88263b2ba5SJacek Lawrynowicz 89103d2ea1SKarol Wachowski kfree(pgtable->pte_ptrs[pgd_idx][pud_idx]); 90103d2ea1SKarol Wachowski ivpu_mmu_pgtable_free(vdev, pmd_dma_ptr, pmd_dma); 91a2fd4a6fSKarol Wachowski } 92a2fd4a6fSKarol Wachowski 93103d2ea1SKarol Wachowski kfree(pgtable->pmd_ptrs[pgd_idx]); 94103d2ea1SKarol Wachowski kfree(pgtable->pte_ptrs[pgd_idx]); 95103d2ea1SKarol Wachowski ivpu_mmu_pgtable_free(vdev, pud_dma_ptr, pud_dma); 96263b2ba5SJacek Lawrynowicz } 97263b2ba5SJacek Lawrynowicz 98103d2ea1SKarol Wachowski ivpu_mmu_pgtable_free(vdev, pgtable->pgd_dma_ptr, pgtable->pgd_dma); 99263b2ba5SJacek Lawrynowicz } 100263b2ba5SJacek Lawrynowicz 101263b2ba5SJacek Lawrynowicz static u64* 102a2fd4a6fSKarol Wachowski ivpu_mmu_ensure_pud(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, int pgd_idx) 103a2fd4a6fSKarol Wachowski { 104103d2ea1SKarol Wachowski u64 *pud_dma_ptr = pgtable->pud_ptrs[pgd_idx]; 105a2fd4a6fSKarol Wachowski dma_addr_t pud_dma; 106a2fd4a6fSKarol Wachowski 107103d2ea1SKarol Wachowski if (pud_dma_ptr) 108103d2ea1SKarol Wachowski return pud_dma_ptr; 109a2fd4a6fSKarol Wachowski 110103d2ea1SKarol Wachowski pud_dma_ptr = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pud_dma, GFP_KERNEL); 111103d2ea1SKarol Wachowski if (!pud_dma_ptr) 112a2fd4a6fSKarol Wachowski return NULL; 113a2fd4a6fSKarol Wachowski 114103d2ea1SKarol Wachowski drm_WARN_ON(&vdev->drm, pgtable->pmd_ptrs[pgd_idx]); 115103d2ea1SKarol Wachowski pgtable->pmd_ptrs[pgd_idx] = kzalloc(IVPU_MMU_PGTABLE_SIZE, GFP_KERNEL); 116103d2ea1SKarol Wachowski if (!pgtable->pmd_ptrs[pgd_idx]) 117103d2ea1SKarol Wachowski goto err_free_pud_dma_ptr; 118a2fd4a6fSKarol Wachowski 119103d2ea1SKarol Wachowski drm_WARN_ON(&vdev->drm, pgtable->pte_ptrs[pgd_idx]); 120103d2ea1SKarol Wachowski pgtable->pte_ptrs[pgd_idx] = kzalloc(IVPU_MMU_PGTABLE_SIZE, GFP_KERNEL); 121103d2ea1SKarol Wachowski if (!pgtable->pte_ptrs[pgd_idx]) 122103d2ea1SKarol Wachowski goto err_free_pmd_ptrs; 123a2fd4a6fSKarol Wachowski 124103d2ea1SKarol Wachowski pgtable->pud_ptrs[pgd_idx] = pud_dma_ptr; 125103d2ea1SKarol Wachowski pgtable->pgd_dma_ptr[pgd_idx] = pud_dma | IVPU_MMU_ENTRY_VALID; 126a2fd4a6fSKarol Wachowski 127103d2ea1SKarol Wachowski return pud_dma_ptr; 128a2fd4a6fSKarol Wachowski 129103d2ea1SKarol Wachowski err_free_pmd_ptrs: 130103d2ea1SKarol Wachowski kfree(pgtable->pmd_ptrs[pgd_idx]); 131a2fd4a6fSKarol Wachowski 132103d2ea1SKarol Wachowski err_free_pud_dma_ptr: 133103d2ea1SKarol Wachowski ivpu_mmu_pgtable_free(vdev, pud_dma_ptr, pud_dma); 134a2fd4a6fSKarol Wachowski return NULL; 135a2fd4a6fSKarol Wachowski } 136a2fd4a6fSKarol Wachowski 137a2fd4a6fSKarol Wachowski static u64* 138103d2ea1SKarol Wachowski ivpu_mmu_ensure_pmd(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, int pgd_idx, 139103d2ea1SKarol Wachowski int pud_idx) 140263b2ba5SJacek Lawrynowicz { 141103d2ea1SKarol Wachowski u64 *pmd_dma_ptr = pgtable->pmd_ptrs[pgd_idx][pud_idx]; 142263b2ba5SJacek Lawrynowicz dma_addr_t pmd_dma; 143263b2ba5SJacek Lawrynowicz 144103d2ea1SKarol Wachowski if (pmd_dma_ptr) 145103d2ea1SKarol Wachowski return pmd_dma_ptr; 146263b2ba5SJacek Lawrynowicz 147103d2ea1SKarol Wachowski pmd_dma_ptr = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pmd_dma, GFP_KERNEL); 148103d2ea1SKarol Wachowski if (!pmd_dma_ptr) 149263b2ba5SJacek Lawrynowicz return NULL; 150263b2ba5SJacek Lawrynowicz 151103d2ea1SKarol Wachowski drm_WARN_ON(&vdev->drm, pgtable->pte_ptrs[pgd_idx][pud_idx]); 152103d2ea1SKarol Wachowski pgtable->pte_ptrs[pgd_idx][pud_idx] = kzalloc(IVPU_MMU_PGTABLE_SIZE, GFP_KERNEL); 153103d2ea1SKarol Wachowski if (!pgtable->pte_ptrs[pgd_idx][pud_idx]) 154103d2ea1SKarol Wachowski goto err_free_pmd_dma_ptr; 155263b2ba5SJacek Lawrynowicz 156103d2ea1SKarol Wachowski pgtable->pmd_ptrs[pgd_idx][pud_idx] = pmd_dma_ptr; 157103d2ea1SKarol Wachowski pgtable->pud_ptrs[pgd_idx][pud_idx] = pmd_dma | IVPU_MMU_ENTRY_VALID; 158263b2ba5SJacek Lawrynowicz 159103d2ea1SKarol Wachowski return pmd_dma_ptr; 160263b2ba5SJacek Lawrynowicz 161103d2ea1SKarol Wachowski err_free_pmd_dma_ptr: 162103d2ea1SKarol Wachowski ivpu_mmu_pgtable_free(vdev, pmd_dma_ptr, pmd_dma); 163263b2ba5SJacek Lawrynowicz return NULL; 164263b2ba5SJacek Lawrynowicz } 165263b2ba5SJacek Lawrynowicz 166263b2ba5SJacek Lawrynowicz static u64* 167263b2ba5SJacek Lawrynowicz ivpu_mmu_ensure_pte(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, 168a2fd4a6fSKarol Wachowski int pgd_idx, int pud_idx, int pmd_idx) 169263b2ba5SJacek Lawrynowicz { 170103d2ea1SKarol Wachowski u64 *pte_dma_ptr = pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx]; 171263b2ba5SJacek Lawrynowicz dma_addr_t pte_dma; 172263b2ba5SJacek Lawrynowicz 173103d2ea1SKarol Wachowski if (pte_dma_ptr) 174103d2ea1SKarol Wachowski return pte_dma_ptr; 175263b2ba5SJacek Lawrynowicz 176103d2ea1SKarol Wachowski pte_dma_ptr = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pte_dma, GFP_KERNEL); 177103d2ea1SKarol Wachowski if (!pte_dma_ptr) 178263b2ba5SJacek Lawrynowicz return NULL; 179263b2ba5SJacek Lawrynowicz 180103d2ea1SKarol Wachowski pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx] = pte_dma_ptr; 181103d2ea1SKarol Wachowski pgtable->pmd_ptrs[pgd_idx][pud_idx][pmd_idx] = pte_dma | IVPU_MMU_ENTRY_VALID; 182263b2ba5SJacek Lawrynowicz 183103d2ea1SKarol Wachowski return pte_dma_ptr; 184263b2ba5SJacek Lawrynowicz } 185263b2ba5SJacek Lawrynowicz 186263b2ba5SJacek Lawrynowicz static int 187263b2ba5SJacek Lawrynowicz ivpu_mmu_context_map_page(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, 18895d44018SKarol Wachowski u64 vpu_addr, dma_addr_t dma_addr, u64 prot) 189263b2ba5SJacek Lawrynowicz { 190263b2ba5SJacek Lawrynowicz u64 *pte; 191a2fd4a6fSKarol Wachowski int pgd_idx = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr); 192a2fd4a6fSKarol Wachowski int pud_idx = FIELD_GET(IVPU_MMU_PUD_INDEX_MASK, vpu_addr); 193a2fd4a6fSKarol Wachowski int pmd_idx = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr); 194a2fd4a6fSKarol Wachowski int pte_idx = FIELD_GET(IVPU_MMU_PTE_INDEX_MASK, vpu_addr); 195a2fd4a6fSKarol Wachowski 196103d2ea1SKarol Wachowski /* Allocate PUD - second level page table if needed */ 197a2fd4a6fSKarol Wachowski if (!ivpu_mmu_ensure_pud(vdev, &ctx->pgtable, pgd_idx)) 198a2fd4a6fSKarol Wachowski return -ENOMEM; 199263b2ba5SJacek Lawrynowicz 200103d2ea1SKarol Wachowski /* Allocate PMD - third level page table if needed */ 201a2fd4a6fSKarol Wachowski if (!ivpu_mmu_ensure_pmd(vdev, &ctx->pgtable, pgd_idx, pud_idx)) 202263b2ba5SJacek Lawrynowicz return -ENOMEM; 203263b2ba5SJacek Lawrynowicz 204103d2ea1SKarol Wachowski /* Allocate PTE - fourth level page table if needed */ 205a2fd4a6fSKarol Wachowski pte = ivpu_mmu_ensure_pte(vdev, &ctx->pgtable, pgd_idx, pud_idx, pmd_idx); 206263b2ba5SJacek Lawrynowicz if (!pte) 207263b2ba5SJacek Lawrynowicz return -ENOMEM; 208263b2ba5SJacek Lawrynowicz 209103d2ea1SKarol Wachowski /* Update PTE */ 210a2fd4a6fSKarol Wachowski pte[pte_idx] = dma_addr | prot; 211263b2ba5SJacek Lawrynowicz 212263b2ba5SJacek Lawrynowicz return 0; 213263b2ba5SJacek Lawrynowicz } 214263b2ba5SJacek Lawrynowicz 21595d44018SKarol Wachowski static int 21695d44018SKarol Wachowski ivpu_mmu_context_map_cont_64k(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u64 vpu_addr, 21795d44018SKarol Wachowski dma_addr_t dma_addr, u64 prot) 21895d44018SKarol Wachowski { 21995d44018SKarol Wachowski size_t size = IVPU_MMU_CONT_PAGES_SIZE; 22095d44018SKarol Wachowski 22195d44018SKarol Wachowski drm_WARN_ON(&vdev->drm, !IS_ALIGNED(vpu_addr, size)); 22295d44018SKarol Wachowski drm_WARN_ON(&vdev->drm, !IS_ALIGNED(dma_addr, size)); 22395d44018SKarol Wachowski 22495d44018SKarol Wachowski prot |= IVPU_MMU_ENTRY_FLAG_CONT; 22595d44018SKarol Wachowski 22695d44018SKarol Wachowski while (size) { 22795d44018SKarol Wachowski int ret = ivpu_mmu_context_map_page(vdev, ctx, vpu_addr, dma_addr, prot); 22895d44018SKarol Wachowski 22995d44018SKarol Wachowski if (ret) 23095d44018SKarol Wachowski return ret; 23195d44018SKarol Wachowski 23295d44018SKarol Wachowski size -= IVPU_MMU_PAGE_SIZE; 23395d44018SKarol Wachowski vpu_addr += IVPU_MMU_PAGE_SIZE; 23495d44018SKarol Wachowski dma_addr += IVPU_MMU_PAGE_SIZE; 23595d44018SKarol Wachowski } 23695d44018SKarol Wachowski 23795d44018SKarol Wachowski return 0; 23895d44018SKarol Wachowski } 23995d44018SKarol Wachowski 240263b2ba5SJacek Lawrynowicz static void ivpu_mmu_context_unmap_page(struct ivpu_mmu_context *ctx, u64 vpu_addr) 241263b2ba5SJacek Lawrynowicz { 242a2fd4a6fSKarol Wachowski int pgd_idx = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr); 243a2fd4a6fSKarol Wachowski int pud_idx = FIELD_GET(IVPU_MMU_PUD_INDEX_MASK, vpu_addr); 244a2fd4a6fSKarol Wachowski int pmd_idx = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr); 245a2fd4a6fSKarol Wachowski int pte_idx = FIELD_GET(IVPU_MMU_PTE_INDEX_MASK, vpu_addr); 246263b2ba5SJacek Lawrynowicz 247263b2ba5SJacek Lawrynowicz /* Update PTE with dummy physical address and clear flags */ 248103d2ea1SKarol Wachowski ctx->pgtable.pte_ptrs[pgd_idx][pud_idx][pmd_idx][pte_idx] = IVPU_MMU_ENTRY_INVALID; 249263b2ba5SJacek Lawrynowicz } 250263b2ba5SJacek Lawrynowicz 251263b2ba5SJacek Lawrynowicz static void 252263b2ba5SJacek Lawrynowicz ivpu_mmu_context_flush_page_tables(struct ivpu_mmu_context *ctx, u64 vpu_addr, size_t size) 253263b2ba5SJacek Lawrynowicz { 254103d2ea1SKarol Wachowski struct ivpu_mmu_pgtable *pgtable = &ctx->pgtable; 255263b2ba5SJacek Lawrynowicz u64 end_addr = vpu_addr + size; 256263b2ba5SJacek Lawrynowicz 257263b2ba5SJacek Lawrynowicz /* Align to PMD entry (2 MB) */ 258263b2ba5SJacek Lawrynowicz vpu_addr &= ~(IVPU_MMU_PTE_MAP_SIZE - 1); 259103d2ea1SKarol Wachowski 260263b2ba5SJacek Lawrynowicz while (vpu_addr < end_addr) { 261a2fd4a6fSKarol Wachowski int pgd_idx = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr); 262a2fd4a6fSKarol Wachowski u64 pud_end = (pgd_idx + 1) * (u64)IVPU_MMU_PUD_MAP_SIZE; 263a2fd4a6fSKarol Wachowski 264a2fd4a6fSKarol Wachowski while (vpu_addr < end_addr && vpu_addr < pud_end) { 265a2fd4a6fSKarol Wachowski int pud_idx = FIELD_GET(IVPU_MMU_PUD_INDEX_MASK, vpu_addr); 266a2fd4a6fSKarol Wachowski u64 pmd_end = (pud_idx + 1) * (u64)IVPU_MMU_PMD_MAP_SIZE; 267263b2ba5SJacek Lawrynowicz 268263b2ba5SJacek Lawrynowicz while (vpu_addr < end_addr && vpu_addr < pmd_end) { 269a2fd4a6fSKarol Wachowski int pmd_idx = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr); 270263b2ba5SJacek Lawrynowicz 271103d2ea1SKarol Wachowski clflush_cache_range(pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx], 272103d2ea1SKarol Wachowski IVPU_MMU_PGTABLE_SIZE); 273263b2ba5SJacek Lawrynowicz vpu_addr += IVPU_MMU_PTE_MAP_SIZE; 274263b2ba5SJacek Lawrynowicz } 275103d2ea1SKarol Wachowski clflush_cache_range(pgtable->pmd_ptrs[pgd_idx][pud_idx], 276103d2ea1SKarol Wachowski IVPU_MMU_PGTABLE_SIZE); 277263b2ba5SJacek Lawrynowicz } 278103d2ea1SKarol Wachowski clflush_cache_range(pgtable->pud_ptrs[pgd_idx], IVPU_MMU_PGTABLE_SIZE); 279a2fd4a6fSKarol Wachowski } 280103d2ea1SKarol Wachowski clflush_cache_range(pgtable->pgd_dma_ptr, IVPU_MMU_PGTABLE_SIZE); 281263b2ba5SJacek Lawrynowicz } 282263b2ba5SJacek Lawrynowicz 283263b2ba5SJacek Lawrynowicz static int 284263b2ba5SJacek Lawrynowicz ivpu_mmu_context_map_pages(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, 28595d44018SKarol Wachowski u64 vpu_addr, dma_addr_t dma_addr, size_t size, u64 prot) 286263b2ba5SJacek Lawrynowicz { 28795d44018SKarol Wachowski int map_size; 28895d44018SKarol Wachowski int ret; 28995d44018SKarol Wachowski 290263b2ba5SJacek Lawrynowicz while (size) { 29195d44018SKarol Wachowski if (!ivpu_disable_mmu_cont_pages && size >= IVPU_MMU_CONT_PAGES_SIZE && 29295d44018SKarol Wachowski IS_ALIGNED(vpu_addr | dma_addr, IVPU_MMU_CONT_PAGES_SIZE)) { 29395d44018SKarol Wachowski ret = ivpu_mmu_context_map_cont_64k(vdev, ctx, vpu_addr, dma_addr, prot); 29495d44018SKarol Wachowski map_size = IVPU_MMU_CONT_PAGES_SIZE; 29595d44018SKarol Wachowski } else { 29695d44018SKarol Wachowski ret = ivpu_mmu_context_map_page(vdev, ctx, vpu_addr, dma_addr, prot); 29795d44018SKarol Wachowski map_size = IVPU_MMU_PAGE_SIZE; 29895d44018SKarol Wachowski } 299263b2ba5SJacek Lawrynowicz 300263b2ba5SJacek Lawrynowicz if (ret) 301263b2ba5SJacek Lawrynowicz return ret; 302263b2ba5SJacek Lawrynowicz 30395d44018SKarol Wachowski vpu_addr += map_size; 30495d44018SKarol Wachowski dma_addr += map_size; 30595d44018SKarol Wachowski size -= map_size; 306263b2ba5SJacek Lawrynowicz } 307263b2ba5SJacek Lawrynowicz 308263b2ba5SJacek Lawrynowicz return 0; 309263b2ba5SJacek Lawrynowicz } 310263b2ba5SJacek Lawrynowicz 311263b2ba5SJacek Lawrynowicz static void ivpu_mmu_context_unmap_pages(struct ivpu_mmu_context *ctx, u64 vpu_addr, size_t size) 312263b2ba5SJacek Lawrynowicz { 313263b2ba5SJacek Lawrynowicz while (size) { 314263b2ba5SJacek Lawrynowicz ivpu_mmu_context_unmap_page(ctx, vpu_addr); 315263b2ba5SJacek Lawrynowicz vpu_addr += IVPU_MMU_PAGE_SIZE; 316263b2ba5SJacek Lawrynowicz size -= IVPU_MMU_PAGE_SIZE; 317263b2ba5SJacek Lawrynowicz } 318263b2ba5SJacek Lawrynowicz } 319263b2ba5SJacek Lawrynowicz 320263b2ba5SJacek Lawrynowicz int 321263b2ba5SJacek Lawrynowicz ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, 322263b2ba5SJacek Lawrynowicz u64 vpu_addr, struct sg_table *sgt, bool llc_coherent) 323263b2ba5SJacek Lawrynowicz { 324263b2ba5SJacek Lawrynowicz struct scatterlist *sg; 325263b2ba5SJacek Lawrynowicz int ret; 32695d44018SKarol Wachowski u64 prot; 327263b2ba5SJacek Lawrynowicz u64 i; 328263b2ba5SJacek Lawrynowicz 329263b2ba5SJacek Lawrynowicz if (!IS_ALIGNED(vpu_addr, IVPU_MMU_PAGE_SIZE)) 330263b2ba5SJacek Lawrynowicz return -EINVAL; 331263b2ba5SJacek Lawrynowicz /* 332263b2ba5SJacek Lawrynowicz * VPU is only 32 bit, but DMA engine is 38 bit 333263b2ba5SJacek Lawrynowicz * Ranges < 2 GB are reserved for VPU internal registers 334263b2ba5SJacek Lawrynowicz * Limit range to 8 GB 335263b2ba5SJacek Lawrynowicz */ 336263b2ba5SJacek Lawrynowicz if (vpu_addr < SZ_2G || vpu_addr > SZ_8G) 337263b2ba5SJacek Lawrynowicz return -EINVAL; 338263b2ba5SJacek Lawrynowicz 339263b2ba5SJacek Lawrynowicz prot = IVPU_MMU_ENTRY_MAPPED; 340263b2ba5SJacek Lawrynowicz if (llc_coherent) 341263b2ba5SJacek Lawrynowicz prot |= IVPU_MMU_ENTRY_FLAG_LLC_COHERENT; 342263b2ba5SJacek Lawrynowicz 343263b2ba5SJacek Lawrynowicz mutex_lock(&ctx->lock); 344263b2ba5SJacek Lawrynowicz 345263b2ba5SJacek Lawrynowicz for_each_sgtable_dma_sg(sgt, sg, i) { 346103d2ea1SKarol Wachowski dma_addr_t dma_addr = sg_dma_address(sg) - sg->offset; 347263b2ba5SJacek Lawrynowicz size_t size = sg_dma_len(sg) + sg->offset; 348263b2ba5SJacek Lawrynowicz 349263b2ba5SJacek Lawrynowicz ret = ivpu_mmu_context_map_pages(vdev, ctx, vpu_addr, dma_addr, size, prot); 350263b2ba5SJacek Lawrynowicz if (ret) { 351263b2ba5SJacek Lawrynowicz ivpu_err(vdev, "Failed to map context pages\n"); 352263b2ba5SJacek Lawrynowicz mutex_unlock(&ctx->lock); 353263b2ba5SJacek Lawrynowicz return ret; 354263b2ba5SJacek Lawrynowicz } 355263b2ba5SJacek Lawrynowicz ivpu_mmu_context_flush_page_tables(ctx, vpu_addr, size); 356263b2ba5SJacek Lawrynowicz vpu_addr += size; 357263b2ba5SJacek Lawrynowicz } 358263b2ba5SJacek Lawrynowicz 359263b2ba5SJacek Lawrynowicz mutex_unlock(&ctx->lock); 360263b2ba5SJacek Lawrynowicz 361263b2ba5SJacek Lawrynowicz ret = ivpu_mmu_invalidate_tlb(vdev, ctx->id); 362263b2ba5SJacek Lawrynowicz if (ret) 363263b2ba5SJacek Lawrynowicz ivpu_err(vdev, "Failed to invalidate TLB for ctx %u: %d\n", ctx->id, ret); 364263b2ba5SJacek Lawrynowicz return ret; 365263b2ba5SJacek Lawrynowicz } 366263b2ba5SJacek Lawrynowicz 367263b2ba5SJacek Lawrynowicz void 368263b2ba5SJacek Lawrynowicz ivpu_mmu_context_unmap_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, 369263b2ba5SJacek Lawrynowicz u64 vpu_addr, struct sg_table *sgt) 370263b2ba5SJacek Lawrynowicz { 371263b2ba5SJacek Lawrynowicz struct scatterlist *sg; 372263b2ba5SJacek Lawrynowicz int ret; 373263b2ba5SJacek Lawrynowicz u64 i; 374263b2ba5SJacek Lawrynowicz 375263b2ba5SJacek Lawrynowicz if (!IS_ALIGNED(vpu_addr, IVPU_MMU_PAGE_SIZE)) 376263b2ba5SJacek Lawrynowicz ivpu_warn(vdev, "Unaligned vpu_addr: 0x%llx\n", vpu_addr); 377263b2ba5SJacek Lawrynowicz 378263b2ba5SJacek Lawrynowicz mutex_lock(&ctx->lock); 379263b2ba5SJacek Lawrynowicz 380263b2ba5SJacek Lawrynowicz for_each_sgtable_dma_sg(sgt, sg, i) { 381263b2ba5SJacek Lawrynowicz size_t size = sg_dma_len(sg) + sg->offset; 382263b2ba5SJacek Lawrynowicz 383263b2ba5SJacek Lawrynowicz ivpu_mmu_context_unmap_pages(ctx, vpu_addr, size); 384263b2ba5SJacek Lawrynowicz ivpu_mmu_context_flush_page_tables(ctx, vpu_addr, size); 385263b2ba5SJacek Lawrynowicz vpu_addr += size; 386263b2ba5SJacek Lawrynowicz } 387263b2ba5SJacek Lawrynowicz 388263b2ba5SJacek Lawrynowicz mutex_unlock(&ctx->lock); 389263b2ba5SJacek Lawrynowicz 390263b2ba5SJacek Lawrynowicz ret = ivpu_mmu_invalidate_tlb(vdev, ctx->id); 391263b2ba5SJacek Lawrynowicz if (ret) 392263b2ba5SJacek Lawrynowicz ivpu_warn(vdev, "Failed to invalidate TLB for ctx %u: %d\n", ctx->id, ret); 393263b2ba5SJacek Lawrynowicz } 394263b2ba5SJacek Lawrynowicz 395263b2ba5SJacek Lawrynowicz int 396263b2ba5SJacek Lawrynowicz ivpu_mmu_context_insert_node_locked(struct ivpu_mmu_context *ctx, 397263b2ba5SJacek Lawrynowicz const struct ivpu_addr_range *range, 398263b2ba5SJacek Lawrynowicz u64 size, struct drm_mm_node *node) 399263b2ba5SJacek Lawrynowicz { 400263b2ba5SJacek Lawrynowicz lockdep_assert_held(&ctx->lock); 401263b2ba5SJacek Lawrynowicz 40295d44018SKarol Wachowski if (!ivpu_disable_mmu_cont_pages && size >= IVPU_MMU_CONT_PAGES_SIZE) { 40395d44018SKarol Wachowski if (!drm_mm_insert_node_in_range(&ctx->mm, node, size, IVPU_MMU_CONT_PAGES_SIZE, 0, 40495d44018SKarol Wachowski range->start, range->end, DRM_MM_INSERT_BEST)) 40595d44018SKarol Wachowski return 0; 40695d44018SKarol Wachowski } 40795d44018SKarol Wachowski 40895d44018SKarol Wachowski return drm_mm_insert_node_in_range(&ctx->mm, node, size, IVPU_MMU_PAGE_SIZE, 0, 40995d44018SKarol Wachowski range->start, range->end, DRM_MM_INSERT_BEST); 410263b2ba5SJacek Lawrynowicz } 411263b2ba5SJacek Lawrynowicz 412263b2ba5SJacek Lawrynowicz void 413263b2ba5SJacek Lawrynowicz ivpu_mmu_context_remove_node_locked(struct ivpu_mmu_context *ctx, struct drm_mm_node *node) 414263b2ba5SJacek Lawrynowicz { 415263b2ba5SJacek Lawrynowicz lockdep_assert_held(&ctx->lock); 416263b2ba5SJacek Lawrynowicz 417263b2ba5SJacek Lawrynowicz drm_mm_remove_node(node); 418263b2ba5SJacek Lawrynowicz } 419263b2ba5SJacek Lawrynowicz 420263b2ba5SJacek Lawrynowicz static int 421263b2ba5SJacek Lawrynowicz ivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 context_id) 422263b2ba5SJacek Lawrynowicz { 423263b2ba5SJacek Lawrynowicz u64 start, end; 424263b2ba5SJacek Lawrynowicz int ret; 425263b2ba5SJacek Lawrynowicz 426263b2ba5SJacek Lawrynowicz mutex_init(&ctx->lock); 427263b2ba5SJacek Lawrynowicz INIT_LIST_HEAD(&ctx->bo_list); 428263b2ba5SJacek Lawrynowicz 429263b2ba5SJacek Lawrynowicz ret = ivpu_mmu_pgtable_init(vdev, &ctx->pgtable); 430*0a9cd792SJacek Lawrynowicz if (ret) { 431*0a9cd792SJacek Lawrynowicz ivpu_err(vdev, "Failed to initialize pgtable for ctx %u: %d\n", context_id, ret); 432263b2ba5SJacek Lawrynowicz return ret; 433*0a9cd792SJacek Lawrynowicz } 434263b2ba5SJacek Lawrynowicz 435263b2ba5SJacek Lawrynowicz if (!context_id) { 436162f17b2SKarol Wachowski start = vdev->hw->ranges.global.start; 437162f17b2SKarol Wachowski end = vdev->hw->ranges.shave.end; 438263b2ba5SJacek Lawrynowicz } else { 439162f17b2SKarol Wachowski start = vdev->hw->ranges.user.start; 440162f17b2SKarol Wachowski end = vdev->hw->ranges.dma.end; 441263b2ba5SJacek Lawrynowicz } 442263b2ba5SJacek Lawrynowicz 443263b2ba5SJacek Lawrynowicz drm_mm_init(&ctx->mm, start, end - start); 444263b2ba5SJacek Lawrynowicz ctx->id = context_id; 445263b2ba5SJacek Lawrynowicz 446263b2ba5SJacek Lawrynowicz return 0; 447263b2ba5SJacek Lawrynowicz } 448263b2ba5SJacek Lawrynowicz 449263b2ba5SJacek Lawrynowicz static void ivpu_mmu_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx) 450263b2ba5SJacek Lawrynowicz { 451103d2ea1SKarol Wachowski if (drm_WARN_ON(&vdev->drm, !ctx->pgtable.pgd_dma_ptr)) 452103d2ea1SKarol Wachowski return; 453263b2ba5SJacek Lawrynowicz 454263b2ba5SJacek Lawrynowicz mutex_destroy(&ctx->lock); 455103d2ea1SKarol Wachowski ivpu_mmu_pgtables_free(vdev, &ctx->pgtable); 456263b2ba5SJacek Lawrynowicz drm_mm_takedown(&ctx->mm); 457103d2ea1SKarol Wachowski 458103d2ea1SKarol Wachowski ctx->pgtable.pgd_dma_ptr = NULL; 459103d2ea1SKarol Wachowski ctx->pgtable.pgd_dma = 0; 460263b2ba5SJacek Lawrynowicz } 461263b2ba5SJacek Lawrynowicz 462263b2ba5SJacek Lawrynowicz int ivpu_mmu_global_context_init(struct ivpu_device *vdev) 463263b2ba5SJacek Lawrynowicz { 464263b2ba5SJacek Lawrynowicz return ivpu_mmu_context_init(vdev, &vdev->gctx, IVPU_GLOBAL_CONTEXT_MMU_SSID); 465263b2ba5SJacek Lawrynowicz } 466263b2ba5SJacek Lawrynowicz 467263b2ba5SJacek Lawrynowicz void ivpu_mmu_global_context_fini(struct ivpu_device *vdev) 468263b2ba5SJacek Lawrynowicz { 469263b2ba5SJacek Lawrynowicz return ivpu_mmu_context_fini(vdev, &vdev->gctx); 470263b2ba5SJacek Lawrynowicz } 471263b2ba5SJacek Lawrynowicz 472263b2ba5SJacek Lawrynowicz void ivpu_mmu_user_context_mark_invalid(struct ivpu_device *vdev, u32 ssid) 473263b2ba5SJacek Lawrynowicz { 474263b2ba5SJacek Lawrynowicz struct ivpu_file_priv *file_priv; 475263b2ba5SJacek Lawrynowicz 476263b2ba5SJacek Lawrynowicz xa_lock(&vdev->context_xa); 477263b2ba5SJacek Lawrynowicz 478263b2ba5SJacek Lawrynowicz file_priv = xa_load(&vdev->context_xa, ssid); 479263b2ba5SJacek Lawrynowicz if (file_priv) 480263b2ba5SJacek Lawrynowicz file_priv->has_mmu_faults = true; 481263b2ba5SJacek Lawrynowicz 482263b2ba5SJacek Lawrynowicz xa_unlock(&vdev->context_xa); 483263b2ba5SJacek Lawrynowicz } 484263b2ba5SJacek Lawrynowicz 485263b2ba5SJacek Lawrynowicz int ivpu_mmu_user_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 ctx_id) 486263b2ba5SJacek Lawrynowicz { 487263b2ba5SJacek Lawrynowicz int ret; 488263b2ba5SJacek Lawrynowicz 489263b2ba5SJacek Lawrynowicz drm_WARN_ON(&vdev->drm, !ctx_id); 490263b2ba5SJacek Lawrynowicz 491263b2ba5SJacek Lawrynowicz ret = ivpu_mmu_context_init(vdev, ctx, ctx_id); 492263b2ba5SJacek Lawrynowicz if (ret) { 493263b2ba5SJacek Lawrynowicz ivpu_err(vdev, "Failed to initialize context: %d\n", ret); 494263b2ba5SJacek Lawrynowicz return ret; 495263b2ba5SJacek Lawrynowicz } 496263b2ba5SJacek Lawrynowicz 497263b2ba5SJacek Lawrynowicz ret = ivpu_mmu_set_pgtable(vdev, ctx_id, &ctx->pgtable); 498263b2ba5SJacek Lawrynowicz if (ret) { 499263b2ba5SJacek Lawrynowicz ivpu_err(vdev, "Failed to set page table: %d\n", ret); 500263b2ba5SJacek Lawrynowicz goto err_context_fini; 501263b2ba5SJacek Lawrynowicz } 502263b2ba5SJacek Lawrynowicz 503263b2ba5SJacek Lawrynowicz return 0; 504263b2ba5SJacek Lawrynowicz 505263b2ba5SJacek Lawrynowicz err_context_fini: 506263b2ba5SJacek Lawrynowicz ivpu_mmu_context_fini(vdev, ctx); 507263b2ba5SJacek Lawrynowicz return ret; 508263b2ba5SJacek Lawrynowicz } 509263b2ba5SJacek Lawrynowicz 510263b2ba5SJacek Lawrynowicz void ivpu_mmu_user_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx) 511263b2ba5SJacek Lawrynowicz { 512263b2ba5SJacek Lawrynowicz drm_WARN_ON(&vdev->drm, !ctx->id); 513263b2ba5SJacek Lawrynowicz 514263b2ba5SJacek Lawrynowicz ivpu_mmu_clear_pgtable(vdev, ctx->id); 515263b2ba5SJacek Lawrynowicz ivpu_mmu_context_fini(vdev, ctx); 516263b2ba5SJacek Lawrynowicz } 517