1*263b2ba5SJacek Lawrynowicz // SPDX-License-Identifier: GPL-2.0-only 2*263b2ba5SJacek Lawrynowicz /* 3*263b2ba5SJacek Lawrynowicz * Copyright (C) 2020-2023 Intel Corporation 4*263b2ba5SJacek Lawrynowicz */ 5*263b2ba5SJacek Lawrynowicz 6*263b2ba5SJacek Lawrynowicz #include <linux/bitfield.h> 7*263b2ba5SJacek Lawrynowicz #include <linux/highmem.h> 8*263b2ba5SJacek Lawrynowicz 9*263b2ba5SJacek Lawrynowicz #include "ivpu_drv.h" 10*263b2ba5SJacek Lawrynowicz #include "ivpu_hw.h" 11*263b2ba5SJacek Lawrynowicz #include "ivpu_mmu.h" 12*263b2ba5SJacek Lawrynowicz #include "ivpu_mmu_context.h" 13*263b2ba5SJacek Lawrynowicz 14*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PGD_INDEX_MASK GENMASK(38, 30) 15*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PMD_INDEX_MASK GENMASK(29, 21) 16*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PTE_INDEX_MASK GENMASK(20, 12) 17*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAGS_MASK GENMASK(11, 0) 18*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_NG BIT(11) 19*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_AF BIT(10) 20*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_USER BIT(6) 21*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_LLC_COHERENT BIT(2) 22*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_TYPE_PAGE BIT(1) 23*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_FLAG_VALID BIT(0) 24*263b2ba5SJacek Lawrynowicz 25*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PAGE_SIZE SZ_4K 26*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PTE_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PAGE_SIZE) 27*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PMD_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PTE_MAP_SIZE) 28*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_PGTABLE_SIZE (IVPU_MMU_PGTABLE_ENTRIES * sizeof(u64)) 29*263b2ba5SJacek Lawrynowicz 30*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_DUMMY_ADDRESS 0xdeadb000 31*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_VALID (IVPU_MMU_ENTRY_FLAG_TYPE_PAGE | IVPU_MMU_ENTRY_FLAG_VALID) 32*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_INVALID (IVPU_MMU_DUMMY_ADDRESS & ~IVPU_MMU_ENTRY_FLAGS_MASK) 33*263b2ba5SJacek Lawrynowicz #define IVPU_MMU_ENTRY_MAPPED (IVPU_MMU_ENTRY_FLAG_AF | IVPU_MMU_ENTRY_FLAG_USER | \ 34*263b2ba5SJacek Lawrynowicz IVPU_MMU_ENTRY_FLAG_NG | IVPU_MMU_ENTRY_VALID) 35*263b2ba5SJacek Lawrynowicz 36*263b2ba5SJacek Lawrynowicz static int ivpu_mmu_pgtable_init(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable) 37*263b2ba5SJacek Lawrynowicz { 38*263b2ba5SJacek Lawrynowicz dma_addr_t pgd_dma; 39*263b2ba5SJacek Lawrynowicz u64 *pgd; 40*263b2ba5SJacek Lawrynowicz 41*263b2ba5SJacek Lawrynowicz pgd = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pgd_dma, GFP_KERNEL); 42*263b2ba5SJacek Lawrynowicz if (!pgd) 43*263b2ba5SJacek Lawrynowicz return -ENOMEM; 44*263b2ba5SJacek Lawrynowicz 45*263b2ba5SJacek Lawrynowicz pgtable->pgd = pgd; 46*263b2ba5SJacek Lawrynowicz pgtable->pgd_dma = pgd_dma; 47*263b2ba5SJacek Lawrynowicz 48*263b2ba5SJacek Lawrynowicz return 0; 49*263b2ba5SJacek Lawrynowicz } 50*263b2ba5SJacek Lawrynowicz 51*263b2ba5SJacek Lawrynowicz static void ivpu_mmu_pgtable_free(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable) 52*263b2ba5SJacek Lawrynowicz { 53*263b2ba5SJacek Lawrynowicz int pgd_index, pmd_index; 54*263b2ba5SJacek Lawrynowicz 55*263b2ba5SJacek Lawrynowicz for (pgd_index = 0; pgd_index < IVPU_MMU_PGTABLE_ENTRIES; ++pgd_index) { 56*263b2ba5SJacek Lawrynowicz u64 **pmd_entries = pgtable->pgd_cpu_entries[pgd_index]; 57*263b2ba5SJacek Lawrynowicz u64 *pmd = pgtable->pgd_entries[pgd_index]; 58*263b2ba5SJacek Lawrynowicz 59*263b2ba5SJacek Lawrynowicz if (!pmd_entries) 60*263b2ba5SJacek Lawrynowicz continue; 61*263b2ba5SJacek Lawrynowicz 62*263b2ba5SJacek Lawrynowicz for (pmd_index = 0; pmd_index < IVPU_MMU_PGTABLE_ENTRIES; ++pmd_index) { 63*263b2ba5SJacek Lawrynowicz if (pmd_entries[pmd_index]) 64*263b2ba5SJacek Lawrynowicz dma_free_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, 65*263b2ba5SJacek Lawrynowicz pmd_entries[pmd_index], 66*263b2ba5SJacek Lawrynowicz pmd[pmd_index] & ~IVPU_MMU_ENTRY_FLAGS_MASK); 67*263b2ba5SJacek Lawrynowicz } 68*263b2ba5SJacek Lawrynowicz 69*263b2ba5SJacek Lawrynowicz kfree(pmd_entries); 70*263b2ba5SJacek Lawrynowicz dma_free_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, pgtable->pgd_entries[pgd_index], 71*263b2ba5SJacek Lawrynowicz pgtable->pgd[pgd_index] & ~IVPU_MMU_ENTRY_FLAGS_MASK); 72*263b2ba5SJacek Lawrynowicz } 73*263b2ba5SJacek Lawrynowicz 74*263b2ba5SJacek Lawrynowicz dma_free_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, pgtable->pgd, 75*263b2ba5SJacek Lawrynowicz pgtable->pgd_dma & ~IVPU_MMU_ENTRY_FLAGS_MASK); 76*263b2ba5SJacek Lawrynowicz } 77*263b2ba5SJacek Lawrynowicz 78*263b2ba5SJacek Lawrynowicz static u64* 79*263b2ba5SJacek Lawrynowicz ivpu_mmu_ensure_pmd(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, u64 pgd_index) 80*263b2ba5SJacek Lawrynowicz { 81*263b2ba5SJacek Lawrynowicz u64 **pmd_entries; 82*263b2ba5SJacek Lawrynowicz dma_addr_t pmd_dma; 83*263b2ba5SJacek Lawrynowicz u64 *pmd; 84*263b2ba5SJacek Lawrynowicz 85*263b2ba5SJacek Lawrynowicz if (pgtable->pgd_entries[pgd_index]) 86*263b2ba5SJacek Lawrynowicz return pgtable->pgd_entries[pgd_index]; 87*263b2ba5SJacek Lawrynowicz 88*263b2ba5SJacek Lawrynowicz pmd = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pmd_dma, GFP_KERNEL); 89*263b2ba5SJacek Lawrynowicz if (!pmd) 90*263b2ba5SJacek Lawrynowicz return NULL; 91*263b2ba5SJacek Lawrynowicz 92*263b2ba5SJacek Lawrynowicz pmd_entries = kzalloc(IVPU_MMU_PGTABLE_SIZE, GFP_KERNEL); 93*263b2ba5SJacek Lawrynowicz if (!pmd_entries) 94*263b2ba5SJacek Lawrynowicz goto err_free_pgd; 95*263b2ba5SJacek Lawrynowicz 96*263b2ba5SJacek Lawrynowicz pgtable->pgd_entries[pgd_index] = pmd; 97*263b2ba5SJacek Lawrynowicz pgtable->pgd_cpu_entries[pgd_index] = pmd_entries; 98*263b2ba5SJacek Lawrynowicz pgtable->pgd[pgd_index] = pmd_dma | IVPU_MMU_ENTRY_VALID; 99*263b2ba5SJacek Lawrynowicz 100*263b2ba5SJacek Lawrynowicz return pmd; 101*263b2ba5SJacek Lawrynowicz 102*263b2ba5SJacek Lawrynowicz err_free_pgd: 103*263b2ba5SJacek Lawrynowicz dma_free_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, pmd, pmd_dma); 104*263b2ba5SJacek Lawrynowicz return NULL; 105*263b2ba5SJacek Lawrynowicz } 106*263b2ba5SJacek Lawrynowicz 107*263b2ba5SJacek Lawrynowicz static u64* 108*263b2ba5SJacek Lawrynowicz ivpu_mmu_ensure_pte(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, 109*263b2ba5SJacek Lawrynowicz int pgd_index, int pmd_index) 110*263b2ba5SJacek Lawrynowicz { 111*263b2ba5SJacek Lawrynowicz dma_addr_t pte_dma; 112*263b2ba5SJacek Lawrynowicz u64 *pte; 113*263b2ba5SJacek Lawrynowicz 114*263b2ba5SJacek Lawrynowicz if (pgtable->pgd_cpu_entries[pgd_index][pmd_index]) 115*263b2ba5SJacek Lawrynowicz return pgtable->pgd_cpu_entries[pgd_index][pmd_index]; 116*263b2ba5SJacek Lawrynowicz 117*263b2ba5SJacek Lawrynowicz pte = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pte_dma, GFP_KERNEL); 118*263b2ba5SJacek Lawrynowicz if (!pte) 119*263b2ba5SJacek Lawrynowicz return NULL; 120*263b2ba5SJacek Lawrynowicz 121*263b2ba5SJacek Lawrynowicz pgtable->pgd_cpu_entries[pgd_index][pmd_index] = pte; 122*263b2ba5SJacek Lawrynowicz pgtable->pgd_entries[pgd_index][pmd_index] = pte_dma | IVPU_MMU_ENTRY_VALID; 123*263b2ba5SJacek Lawrynowicz 124*263b2ba5SJacek Lawrynowicz return pte; 125*263b2ba5SJacek Lawrynowicz } 126*263b2ba5SJacek Lawrynowicz 127*263b2ba5SJacek Lawrynowicz static int 128*263b2ba5SJacek Lawrynowicz ivpu_mmu_context_map_page(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, 129*263b2ba5SJacek Lawrynowicz u64 vpu_addr, dma_addr_t dma_addr, int prot) 130*263b2ba5SJacek Lawrynowicz { 131*263b2ba5SJacek Lawrynowicz u64 *pte; 132*263b2ba5SJacek Lawrynowicz int pgd_index = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr); 133*263b2ba5SJacek Lawrynowicz int pmd_index = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr); 134*263b2ba5SJacek Lawrynowicz int pte_index = FIELD_GET(IVPU_MMU_PTE_INDEX_MASK, vpu_addr); 135*263b2ba5SJacek Lawrynowicz 136*263b2ba5SJacek Lawrynowicz /* Allocate PMD - second level page table if needed */ 137*263b2ba5SJacek Lawrynowicz if (!ivpu_mmu_ensure_pmd(vdev, &ctx->pgtable, pgd_index)) 138*263b2ba5SJacek Lawrynowicz return -ENOMEM; 139*263b2ba5SJacek Lawrynowicz 140*263b2ba5SJacek Lawrynowicz /* Allocate PTE - third level page table if needed */ 141*263b2ba5SJacek Lawrynowicz pte = ivpu_mmu_ensure_pte(vdev, &ctx->pgtable, pgd_index, pmd_index); 142*263b2ba5SJacek Lawrynowicz if (!pte) 143*263b2ba5SJacek Lawrynowicz return -ENOMEM; 144*263b2ba5SJacek Lawrynowicz 145*263b2ba5SJacek Lawrynowicz /* Update PTE - third level page table with DMA address */ 146*263b2ba5SJacek Lawrynowicz pte[pte_index] = dma_addr | prot; 147*263b2ba5SJacek Lawrynowicz 148*263b2ba5SJacek Lawrynowicz return 0; 149*263b2ba5SJacek Lawrynowicz } 150*263b2ba5SJacek Lawrynowicz 151*263b2ba5SJacek Lawrynowicz static void ivpu_mmu_context_unmap_page(struct ivpu_mmu_context *ctx, u64 vpu_addr) 152*263b2ba5SJacek Lawrynowicz { 153*263b2ba5SJacek Lawrynowicz int pgd_index = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr); 154*263b2ba5SJacek Lawrynowicz int pmd_index = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr); 155*263b2ba5SJacek Lawrynowicz int pte_index = FIELD_GET(IVPU_MMU_PTE_INDEX_MASK, vpu_addr); 156*263b2ba5SJacek Lawrynowicz 157*263b2ba5SJacek Lawrynowicz /* Update PTE with dummy physical address and clear flags */ 158*263b2ba5SJacek Lawrynowicz ctx->pgtable.pgd_cpu_entries[pgd_index][pmd_index][pte_index] = IVPU_MMU_ENTRY_INVALID; 159*263b2ba5SJacek Lawrynowicz } 160*263b2ba5SJacek Lawrynowicz 161*263b2ba5SJacek Lawrynowicz static void 162*263b2ba5SJacek Lawrynowicz ivpu_mmu_context_flush_page_tables(struct ivpu_mmu_context *ctx, u64 vpu_addr, size_t size) 163*263b2ba5SJacek Lawrynowicz { 164*263b2ba5SJacek Lawrynowicz u64 end_addr = vpu_addr + size; 165*263b2ba5SJacek Lawrynowicz u64 *pgd = ctx->pgtable.pgd; 166*263b2ba5SJacek Lawrynowicz 167*263b2ba5SJacek Lawrynowicz /* Align to PMD entry (2 MB) */ 168*263b2ba5SJacek Lawrynowicz vpu_addr &= ~(IVPU_MMU_PTE_MAP_SIZE - 1); 169*263b2ba5SJacek Lawrynowicz 170*263b2ba5SJacek Lawrynowicz while (vpu_addr < end_addr) { 171*263b2ba5SJacek Lawrynowicz int pgd_index = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr); 172*263b2ba5SJacek Lawrynowicz u64 pmd_end = (pgd_index + 1) * (u64)IVPU_MMU_PMD_MAP_SIZE; 173*263b2ba5SJacek Lawrynowicz u64 *pmd = ctx->pgtable.pgd_entries[pgd_index]; 174*263b2ba5SJacek Lawrynowicz 175*263b2ba5SJacek Lawrynowicz while (vpu_addr < end_addr && vpu_addr < pmd_end) { 176*263b2ba5SJacek Lawrynowicz int pmd_index = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr); 177*263b2ba5SJacek Lawrynowicz u64 *pte = ctx->pgtable.pgd_cpu_entries[pgd_index][pmd_index]; 178*263b2ba5SJacek Lawrynowicz 179*263b2ba5SJacek Lawrynowicz clflush_cache_range(pte, IVPU_MMU_PGTABLE_SIZE); 180*263b2ba5SJacek Lawrynowicz vpu_addr += IVPU_MMU_PTE_MAP_SIZE; 181*263b2ba5SJacek Lawrynowicz } 182*263b2ba5SJacek Lawrynowicz clflush_cache_range(pmd, IVPU_MMU_PGTABLE_SIZE); 183*263b2ba5SJacek Lawrynowicz } 184*263b2ba5SJacek Lawrynowicz clflush_cache_range(pgd, IVPU_MMU_PGTABLE_SIZE); 185*263b2ba5SJacek Lawrynowicz } 186*263b2ba5SJacek Lawrynowicz 187*263b2ba5SJacek Lawrynowicz static int 188*263b2ba5SJacek Lawrynowicz ivpu_mmu_context_map_pages(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, 189*263b2ba5SJacek Lawrynowicz u64 vpu_addr, dma_addr_t dma_addr, size_t size, int prot) 190*263b2ba5SJacek Lawrynowicz { 191*263b2ba5SJacek Lawrynowicz while (size) { 192*263b2ba5SJacek Lawrynowicz int ret = ivpu_mmu_context_map_page(vdev, ctx, vpu_addr, dma_addr, prot); 193*263b2ba5SJacek Lawrynowicz 194*263b2ba5SJacek Lawrynowicz if (ret) 195*263b2ba5SJacek Lawrynowicz return ret; 196*263b2ba5SJacek Lawrynowicz 197*263b2ba5SJacek Lawrynowicz vpu_addr += IVPU_MMU_PAGE_SIZE; 198*263b2ba5SJacek Lawrynowicz dma_addr += IVPU_MMU_PAGE_SIZE; 199*263b2ba5SJacek Lawrynowicz size -= IVPU_MMU_PAGE_SIZE; 200*263b2ba5SJacek Lawrynowicz } 201*263b2ba5SJacek Lawrynowicz 202*263b2ba5SJacek Lawrynowicz return 0; 203*263b2ba5SJacek Lawrynowicz } 204*263b2ba5SJacek Lawrynowicz 205*263b2ba5SJacek Lawrynowicz static void ivpu_mmu_context_unmap_pages(struct ivpu_mmu_context *ctx, u64 vpu_addr, size_t size) 206*263b2ba5SJacek Lawrynowicz { 207*263b2ba5SJacek Lawrynowicz while (size) { 208*263b2ba5SJacek Lawrynowicz ivpu_mmu_context_unmap_page(ctx, vpu_addr); 209*263b2ba5SJacek Lawrynowicz vpu_addr += IVPU_MMU_PAGE_SIZE; 210*263b2ba5SJacek Lawrynowicz size -= IVPU_MMU_PAGE_SIZE; 211*263b2ba5SJacek Lawrynowicz } 212*263b2ba5SJacek Lawrynowicz } 213*263b2ba5SJacek Lawrynowicz 214*263b2ba5SJacek Lawrynowicz int 215*263b2ba5SJacek Lawrynowicz ivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, 216*263b2ba5SJacek Lawrynowicz u64 vpu_addr, struct sg_table *sgt, bool llc_coherent) 217*263b2ba5SJacek Lawrynowicz { 218*263b2ba5SJacek Lawrynowicz struct scatterlist *sg; 219*263b2ba5SJacek Lawrynowicz int prot; 220*263b2ba5SJacek Lawrynowicz int ret; 221*263b2ba5SJacek Lawrynowicz u64 i; 222*263b2ba5SJacek Lawrynowicz 223*263b2ba5SJacek Lawrynowicz if (!IS_ALIGNED(vpu_addr, IVPU_MMU_PAGE_SIZE)) 224*263b2ba5SJacek Lawrynowicz return -EINVAL; 225*263b2ba5SJacek Lawrynowicz /* 226*263b2ba5SJacek Lawrynowicz * VPU is only 32 bit, but DMA engine is 38 bit 227*263b2ba5SJacek Lawrynowicz * Ranges < 2 GB are reserved for VPU internal registers 228*263b2ba5SJacek Lawrynowicz * Limit range to 8 GB 229*263b2ba5SJacek Lawrynowicz */ 230*263b2ba5SJacek Lawrynowicz if (vpu_addr < SZ_2G || vpu_addr > SZ_8G) 231*263b2ba5SJacek Lawrynowicz return -EINVAL; 232*263b2ba5SJacek Lawrynowicz 233*263b2ba5SJacek Lawrynowicz prot = IVPU_MMU_ENTRY_MAPPED; 234*263b2ba5SJacek Lawrynowicz if (llc_coherent) 235*263b2ba5SJacek Lawrynowicz prot |= IVPU_MMU_ENTRY_FLAG_LLC_COHERENT; 236*263b2ba5SJacek Lawrynowicz 237*263b2ba5SJacek Lawrynowicz mutex_lock(&ctx->lock); 238*263b2ba5SJacek Lawrynowicz 239*263b2ba5SJacek Lawrynowicz for_each_sgtable_dma_sg(sgt, sg, i) { 240*263b2ba5SJacek Lawrynowicz u64 dma_addr = sg_dma_address(sg) - sg->offset; 241*263b2ba5SJacek Lawrynowicz size_t size = sg_dma_len(sg) + sg->offset; 242*263b2ba5SJacek Lawrynowicz 243*263b2ba5SJacek Lawrynowicz ret = ivpu_mmu_context_map_pages(vdev, ctx, vpu_addr, dma_addr, size, prot); 244*263b2ba5SJacek Lawrynowicz if (ret) { 245*263b2ba5SJacek Lawrynowicz ivpu_err(vdev, "Failed to map context pages\n"); 246*263b2ba5SJacek Lawrynowicz mutex_unlock(&ctx->lock); 247*263b2ba5SJacek Lawrynowicz return ret; 248*263b2ba5SJacek Lawrynowicz } 249*263b2ba5SJacek Lawrynowicz ivpu_mmu_context_flush_page_tables(ctx, vpu_addr, size); 250*263b2ba5SJacek Lawrynowicz vpu_addr += size; 251*263b2ba5SJacek Lawrynowicz } 252*263b2ba5SJacek Lawrynowicz 253*263b2ba5SJacek Lawrynowicz mutex_unlock(&ctx->lock); 254*263b2ba5SJacek Lawrynowicz 255*263b2ba5SJacek Lawrynowicz ret = ivpu_mmu_invalidate_tlb(vdev, ctx->id); 256*263b2ba5SJacek Lawrynowicz if (ret) 257*263b2ba5SJacek Lawrynowicz ivpu_err(vdev, "Failed to invalidate TLB for ctx %u: %d\n", ctx->id, ret); 258*263b2ba5SJacek Lawrynowicz return ret; 259*263b2ba5SJacek Lawrynowicz } 260*263b2ba5SJacek Lawrynowicz 261*263b2ba5SJacek Lawrynowicz void 262*263b2ba5SJacek Lawrynowicz ivpu_mmu_context_unmap_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, 263*263b2ba5SJacek Lawrynowicz u64 vpu_addr, struct sg_table *sgt) 264*263b2ba5SJacek Lawrynowicz { 265*263b2ba5SJacek Lawrynowicz struct scatterlist *sg; 266*263b2ba5SJacek Lawrynowicz int ret; 267*263b2ba5SJacek Lawrynowicz u64 i; 268*263b2ba5SJacek Lawrynowicz 269*263b2ba5SJacek Lawrynowicz if (!IS_ALIGNED(vpu_addr, IVPU_MMU_PAGE_SIZE)) 270*263b2ba5SJacek Lawrynowicz ivpu_warn(vdev, "Unaligned vpu_addr: 0x%llx\n", vpu_addr); 271*263b2ba5SJacek Lawrynowicz 272*263b2ba5SJacek Lawrynowicz mutex_lock(&ctx->lock); 273*263b2ba5SJacek Lawrynowicz 274*263b2ba5SJacek Lawrynowicz for_each_sgtable_dma_sg(sgt, sg, i) { 275*263b2ba5SJacek Lawrynowicz size_t size = sg_dma_len(sg) + sg->offset; 276*263b2ba5SJacek Lawrynowicz 277*263b2ba5SJacek Lawrynowicz ivpu_mmu_context_unmap_pages(ctx, vpu_addr, size); 278*263b2ba5SJacek Lawrynowicz ivpu_mmu_context_flush_page_tables(ctx, vpu_addr, size); 279*263b2ba5SJacek Lawrynowicz vpu_addr += size; 280*263b2ba5SJacek Lawrynowicz } 281*263b2ba5SJacek Lawrynowicz 282*263b2ba5SJacek Lawrynowicz mutex_unlock(&ctx->lock); 283*263b2ba5SJacek Lawrynowicz 284*263b2ba5SJacek Lawrynowicz ret = ivpu_mmu_invalidate_tlb(vdev, ctx->id); 285*263b2ba5SJacek Lawrynowicz if (ret) 286*263b2ba5SJacek Lawrynowicz ivpu_warn(vdev, "Failed to invalidate TLB for ctx %u: %d\n", ctx->id, ret); 287*263b2ba5SJacek Lawrynowicz } 288*263b2ba5SJacek Lawrynowicz 289*263b2ba5SJacek Lawrynowicz int 290*263b2ba5SJacek Lawrynowicz ivpu_mmu_context_insert_node_locked(struct ivpu_mmu_context *ctx, 291*263b2ba5SJacek Lawrynowicz const struct ivpu_addr_range *range, 292*263b2ba5SJacek Lawrynowicz u64 size, struct drm_mm_node *node) 293*263b2ba5SJacek Lawrynowicz { 294*263b2ba5SJacek Lawrynowicz lockdep_assert_held(&ctx->lock); 295*263b2ba5SJacek Lawrynowicz 296*263b2ba5SJacek Lawrynowicz return drm_mm_insert_node_in_range(&ctx->mm, node, size, IVPU_MMU_PAGE_SIZE, 297*263b2ba5SJacek Lawrynowicz 0, range->start, range->end, DRM_MM_INSERT_BEST); 298*263b2ba5SJacek Lawrynowicz } 299*263b2ba5SJacek Lawrynowicz 300*263b2ba5SJacek Lawrynowicz void 301*263b2ba5SJacek Lawrynowicz ivpu_mmu_context_remove_node_locked(struct ivpu_mmu_context *ctx, struct drm_mm_node *node) 302*263b2ba5SJacek Lawrynowicz { 303*263b2ba5SJacek Lawrynowicz lockdep_assert_held(&ctx->lock); 304*263b2ba5SJacek Lawrynowicz 305*263b2ba5SJacek Lawrynowicz drm_mm_remove_node(node); 306*263b2ba5SJacek Lawrynowicz } 307*263b2ba5SJacek Lawrynowicz 308*263b2ba5SJacek Lawrynowicz static int 309*263b2ba5SJacek Lawrynowicz ivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 context_id) 310*263b2ba5SJacek Lawrynowicz { 311*263b2ba5SJacek Lawrynowicz u64 start, end; 312*263b2ba5SJacek Lawrynowicz int ret; 313*263b2ba5SJacek Lawrynowicz 314*263b2ba5SJacek Lawrynowicz mutex_init(&ctx->lock); 315*263b2ba5SJacek Lawrynowicz INIT_LIST_HEAD(&ctx->bo_list); 316*263b2ba5SJacek Lawrynowicz 317*263b2ba5SJacek Lawrynowicz ret = ivpu_mmu_pgtable_init(vdev, &ctx->pgtable); 318*263b2ba5SJacek Lawrynowicz if (ret) 319*263b2ba5SJacek Lawrynowicz return ret; 320*263b2ba5SJacek Lawrynowicz 321*263b2ba5SJacek Lawrynowicz if (!context_id) { 322*263b2ba5SJacek Lawrynowicz start = vdev->hw->ranges.global_low.start; 323*263b2ba5SJacek Lawrynowicz end = vdev->hw->ranges.global_high.end; 324*263b2ba5SJacek Lawrynowicz } else { 325*263b2ba5SJacek Lawrynowicz start = vdev->hw->ranges.user_low.start; 326*263b2ba5SJacek Lawrynowicz end = vdev->hw->ranges.user_high.end; 327*263b2ba5SJacek Lawrynowicz } 328*263b2ba5SJacek Lawrynowicz 329*263b2ba5SJacek Lawrynowicz drm_mm_init(&ctx->mm, start, end - start); 330*263b2ba5SJacek Lawrynowicz ctx->id = context_id; 331*263b2ba5SJacek Lawrynowicz 332*263b2ba5SJacek Lawrynowicz return 0; 333*263b2ba5SJacek Lawrynowicz } 334*263b2ba5SJacek Lawrynowicz 335*263b2ba5SJacek Lawrynowicz static void ivpu_mmu_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx) 336*263b2ba5SJacek Lawrynowicz { 337*263b2ba5SJacek Lawrynowicz drm_WARN_ON(&vdev->drm, !ctx->pgtable.pgd); 338*263b2ba5SJacek Lawrynowicz 339*263b2ba5SJacek Lawrynowicz mutex_destroy(&ctx->lock); 340*263b2ba5SJacek Lawrynowicz ivpu_mmu_pgtable_free(vdev, &ctx->pgtable); 341*263b2ba5SJacek Lawrynowicz drm_mm_takedown(&ctx->mm); 342*263b2ba5SJacek Lawrynowicz } 343*263b2ba5SJacek Lawrynowicz 344*263b2ba5SJacek Lawrynowicz int ivpu_mmu_global_context_init(struct ivpu_device *vdev) 345*263b2ba5SJacek Lawrynowicz { 346*263b2ba5SJacek Lawrynowicz return ivpu_mmu_context_init(vdev, &vdev->gctx, IVPU_GLOBAL_CONTEXT_MMU_SSID); 347*263b2ba5SJacek Lawrynowicz } 348*263b2ba5SJacek Lawrynowicz 349*263b2ba5SJacek Lawrynowicz void ivpu_mmu_global_context_fini(struct ivpu_device *vdev) 350*263b2ba5SJacek Lawrynowicz { 351*263b2ba5SJacek Lawrynowicz return ivpu_mmu_context_fini(vdev, &vdev->gctx); 352*263b2ba5SJacek Lawrynowicz } 353*263b2ba5SJacek Lawrynowicz 354*263b2ba5SJacek Lawrynowicz void ivpu_mmu_user_context_mark_invalid(struct ivpu_device *vdev, u32 ssid) 355*263b2ba5SJacek Lawrynowicz { 356*263b2ba5SJacek Lawrynowicz struct ivpu_file_priv *file_priv; 357*263b2ba5SJacek Lawrynowicz 358*263b2ba5SJacek Lawrynowicz xa_lock(&vdev->context_xa); 359*263b2ba5SJacek Lawrynowicz 360*263b2ba5SJacek Lawrynowicz file_priv = xa_load(&vdev->context_xa, ssid); 361*263b2ba5SJacek Lawrynowicz if (file_priv) 362*263b2ba5SJacek Lawrynowicz file_priv->has_mmu_faults = true; 363*263b2ba5SJacek Lawrynowicz 364*263b2ba5SJacek Lawrynowicz xa_unlock(&vdev->context_xa); 365*263b2ba5SJacek Lawrynowicz } 366*263b2ba5SJacek Lawrynowicz 367*263b2ba5SJacek Lawrynowicz int ivpu_mmu_user_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 ctx_id) 368*263b2ba5SJacek Lawrynowicz { 369*263b2ba5SJacek Lawrynowicz int ret; 370*263b2ba5SJacek Lawrynowicz 371*263b2ba5SJacek Lawrynowicz drm_WARN_ON(&vdev->drm, !ctx_id); 372*263b2ba5SJacek Lawrynowicz 373*263b2ba5SJacek Lawrynowicz ret = ivpu_mmu_context_init(vdev, ctx, ctx_id); 374*263b2ba5SJacek Lawrynowicz if (ret) { 375*263b2ba5SJacek Lawrynowicz ivpu_err(vdev, "Failed to initialize context: %d\n", ret); 376*263b2ba5SJacek Lawrynowicz return ret; 377*263b2ba5SJacek Lawrynowicz } 378*263b2ba5SJacek Lawrynowicz 379*263b2ba5SJacek Lawrynowicz ret = ivpu_mmu_set_pgtable(vdev, ctx_id, &ctx->pgtable); 380*263b2ba5SJacek Lawrynowicz if (ret) { 381*263b2ba5SJacek Lawrynowicz ivpu_err(vdev, "Failed to set page table: %d\n", ret); 382*263b2ba5SJacek Lawrynowicz goto err_context_fini; 383*263b2ba5SJacek Lawrynowicz } 384*263b2ba5SJacek Lawrynowicz 385*263b2ba5SJacek Lawrynowicz return 0; 386*263b2ba5SJacek Lawrynowicz 387*263b2ba5SJacek Lawrynowicz err_context_fini: 388*263b2ba5SJacek Lawrynowicz ivpu_mmu_context_fini(vdev, ctx); 389*263b2ba5SJacek Lawrynowicz return ret; 390*263b2ba5SJacek Lawrynowicz } 391*263b2ba5SJacek Lawrynowicz 392*263b2ba5SJacek Lawrynowicz void ivpu_mmu_user_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx) 393*263b2ba5SJacek Lawrynowicz { 394*263b2ba5SJacek Lawrynowicz drm_WARN_ON(&vdev->drm, !ctx->id); 395*263b2ba5SJacek Lawrynowicz 396*263b2ba5SJacek Lawrynowicz ivpu_mmu_clear_pgtable(vdev, ctx->id); 397*263b2ba5SJacek Lawrynowicz ivpu_mmu_context_fini(vdev, ctx); 398*263b2ba5SJacek Lawrynowicz } 399