162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2020-2023 Intel Corporation 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/bitfield.h> 762306a36Sopenharmony_ci#include <linux/highmem.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "ivpu_drv.h" 1062306a36Sopenharmony_ci#include "ivpu_hw.h" 1162306a36Sopenharmony_ci#include "ivpu_mmu.h" 1262306a36Sopenharmony_ci#include "ivpu_mmu_context.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define IVPU_MMU_VPU_ADDRESS_MASK GENMASK(47, 12) 1562306a36Sopenharmony_ci#define IVPU_MMU_PGD_INDEX_MASK GENMASK(47, 39) 1662306a36Sopenharmony_ci#define IVPU_MMU_PUD_INDEX_MASK GENMASK(38, 30) 1762306a36Sopenharmony_ci#define IVPU_MMU_PMD_INDEX_MASK GENMASK(29, 21) 1862306a36Sopenharmony_ci#define IVPU_MMU_PTE_INDEX_MASK GENMASK(20, 12) 1962306a36Sopenharmony_ci#define IVPU_MMU_ENTRY_FLAGS_MASK (BIT(52) | GENMASK(11, 0)) 2062306a36Sopenharmony_ci#define IVPU_MMU_ENTRY_FLAG_CONT BIT(52) 2162306a36Sopenharmony_ci#define IVPU_MMU_ENTRY_FLAG_NG BIT(11) 2262306a36Sopenharmony_ci#define IVPU_MMU_ENTRY_FLAG_AF BIT(10) 2362306a36Sopenharmony_ci#define IVPU_MMU_ENTRY_FLAG_USER BIT(6) 2462306a36Sopenharmony_ci#define IVPU_MMU_ENTRY_FLAG_LLC_COHERENT BIT(2) 2562306a36Sopenharmony_ci#define IVPU_MMU_ENTRY_FLAG_TYPE_PAGE BIT(1) 2662306a36Sopenharmony_ci#define IVPU_MMU_ENTRY_FLAG_VALID BIT(0) 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define IVPU_MMU_PAGE_SIZE SZ_4K 2962306a36Sopenharmony_ci#define IVPU_MMU_CONT_PAGES_SIZE (IVPU_MMU_PAGE_SIZE * 16) 3062306a36Sopenharmony_ci#define IVPU_MMU_PTE_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PAGE_SIZE) 3162306a36Sopenharmony_ci#define IVPU_MMU_PMD_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PTE_MAP_SIZE) 3262306a36Sopenharmony_ci#define IVPU_MMU_PUD_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PMD_MAP_SIZE) 3362306a36Sopenharmony_ci#define IVPU_MMU_PGD_MAP_SIZE (IVPU_MMU_PGTABLE_ENTRIES * IVPU_MMU_PUD_MAP_SIZE) 3462306a36Sopenharmony_ci#define IVPU_MMU_PGTABLE_SIZE (IVPU_MMU_PGTABLE_ENTRIES * sizeof(u64)) 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define IVPU_MMU_DUMMY_ADDRESS 0xdeadb000 3762306a36Sopenharmony_ci#define IVPU_MMU_ENTRY_VALID (IVPU_MMU_ENTRY_FLAG_TYPE_PAGE | IVPU_MMU_ENTRY_FLAG_VALID) 3862306a36Sopenharmony_ci#define IVPU_MMU_ENTRY_INVALID (IVPU_MMU_DUMMY_ADDRESS & ~IVPU_MMU_ENTRY_FLAGS_MASK) 3962306a36Sopenharmony_ci#define IVPU_MMU_ENTRY_MAPPED (IVPU_MMU_ENTRY_FLAG_AF | IVPU_MMU_ENTRY_FLAG_USER | \ 4062306a36Sopenharmony_ci IVPU_MMU_ENTRY_FLAG_NG | IVPU_MMU_ENTRY_VALID) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic int ivpu_mmu_pgtable_init(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci dma_addr_t pgd_dma; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci pgtable->pgd_dma_ptr = dma_alloc_coherent(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pgd_dma, 4762306a36Sopenharmony_ci GFP_KERNEL); 4862306a36Sopenharmony_ci if (!pgtable->pgd_dma_ptr) 4962306a36Sopenharmony_ci return -ENOMEM; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci pgtable->pgd_dma = pgd_dma; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci return 0; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic void ivpu_mmu_pgtable_free(struct ivpu_device *vdev, u64 *cpu_addr, dma_addr_t dma_addr) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci if (cpu_addr) 5962306a36Sopenharmony_ci dma_free_coherent(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, cpu_addr, 6062306a36Sopenharmony_ci dma_addr & ~IVPU_MMU_ENTRY_FLAGS_MASK); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic void ivpu_mmu_pgtables_free(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci int pgd_idx, pud_idx, pmd_idx; 6662306a36Sopenharmony_ci dma_addr_t pud_dma, pmd_dma, pte_dma; 6762306a36Sopenharmony_ci u64 *pud_dma_ptr, *pmd_dma_ptr, *pte_dma_ptr; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci for (pgd_idx = 0; pgd_idx < IVPU_MMU_PGTABLE_ENTRIES; ++pgd_idx) { 7062306a36Sopenharmony_ci pud_dma_ptr = pgtable->pud_ptrs[pgd_idx]; 7162306a36Sopenharmony_ci pud_dma = pgtable->pgd_dma_ptr[pgd_idx]; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (!pud_dma_ptr) 7462306a36Sopenharmony_ci continue; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci for (pud_idx = 0; pud_idx < IVPU_MMU_PGTABLE_ENTRIES; ++pud_idx) { 7762306a36Sopenharmony_ci pmd_dma_ptr = pgtable->pmd_ptrs[pgd_idx][pud_idx]; 7862306a36Sopenharmony_ci pmd_dma = pgtable->pud_ptrs[pgd_idx][pud_idx]; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci if (!pmd_dma_ptr) 8162306a36Sopenharmony_ci continue; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci for (pmd_idx = 0; pmd_idx < IVPU_MMU_PGTABLE_ENTRIES; ++pmd_idx) { 8462306a36Sopenharmony_ci pte_dma_ptr = pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx]; 8562306a36Sopenharmony_ci pte_dma = pgtable->pmd_ptrs[pgd_idx][pud_idx][pmd_idx]; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci ivpu_mmu_pgtable_free(vdev, pte_dma_ptr, pte_dma); 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci kfree(pgtable->pte_ptrs[pgd_idx][pud_idx]); 9162306a36Sopenharmony_ci ivpu_mmu_pgtable_free(vdev, pmd_dma_ptr, pmd_dma); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci kfree(pgtable->pmd_ptrs[pgd_idx]); 9562306a36Sopenharmony_ci kfree(pgtable->pte_ptrs[pgd_idx]); 9662306a36Sopenharmony_ci ivpu_mmu_pgtable_free(vdev, pud_dma_ptr, pud_dma); 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci ivpu_mmu_pgtable_free(vdev, pgtable->pgd_dma_ptr, pgtable->pgd_dma); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic u64* 10362306a36Sopenharmony_ciivpu_mmu_ensure_pud(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, int pgd_idx) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci u64 *pud_dma_ptr = pgtable->pud_ptrs[pgd_idx]; 10662306a36Sopenharmony_ci dma_addr_t pud_dma; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (pud_dma_ptr) 10962306a36Sopenharmony_ci return pud_dma_ptr; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci pud_dma_ptr = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pud_dma, GFP_KERNEL); 11262306a36Sopenharmony_ci if (!pud_dma_ptr) 11362306a36Sopenharmony_ci return NULL; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci drm_WARN_ON(&vdev->drm, pgtable->pmd_ptrs[pgd_idx]); 11662306a36Sopenharmony_ci pgtable->pmd_ptrs[pgd_idx] = kzalloc(IVPU_MMU_PGTABLE_SIZE, GFP_KERNEL); 11762306a36Sopenharmony_ci if (!pgtable->pmd_ptrs[pgd_idx]) 11862306a36Sopenharmony_ci goto err_free_pud_dma_ptr; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci drm_WARN_ON(&vdev->drm, pgtable->pte_ptrs[pgd_idx]); 12162306a36Sopenharmony_ci pgtable->pte_ptrs[pgd_idx] = kzalloc(IVPU_MMU_PGTABLE_SIZE, GFP_KERNEL); 12262306a36Sopenharmony_ci if (!pgtable->pte_ptrs[pgd_idx]) 12362306a36Sopenharmony_ci goto err_free_pmd_ptrs; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci pgtable->pud_ptrs[pgd_idx] = pud_dma_ptr; 12662306a36Sopenharmony_ci pgtable->pgd_dma_ptr[pgd_idx] = pud_dma | IVPU_MMU_ENTRY_VALID; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return pud_dma_ptr; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cierr_free_pmd_ptrs: 13162306a36Sopenharmony_ci kfree(pgtable->pmd_ptrs[pgd_idx]); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cierr_free_pud_dma_ptr: 13462306a36Sopenharmony_ci ivpu_mmu_pgtable_free(vdev, pud_dma_ptr, pud_dma); 13562306a36Sopenharmony_ci return NULL; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic u64* 13962306a36Sopenharmony_ciivpu_mmu_ensure_pmd(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, int pgd_idx, 14062306a36Sopenharmony_ci int pud_idx) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci u64 *pmd_dma_ptr = pgtable->pmd_ptrs[pgd_idx][pud_idx]; 14362306a36Sopenharmony_ci dma_addr_t pmd_dma; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (pmd_dma_ptr) 14662306a36Sopenharmony_ci return pmd_dma_ptr; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci pmd_dma_ptr = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pmd_dma, GFP_KERNEL); 14962306a36Sopenharmony_ci if (!pmd_dma_ptr) 15062306a36Sopenharmony_ci return NULL; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci drm_WARN_ON(&vdev->drm, pgtable->pte_ptrs[pgd_idx][pud_idx]); 15362306a36Sopenharmony_ci pgtable->pte_ptrs[pgd_idx][pud_idx] = kzalloc(IVPU_MMU_PGTABLE_SIZE, GFP_KERNEL); 15462306a36Sopenharmony_ci if (!pgtable->pte_ptrs[pgd_idx][pud_idx]) 15562306a36Sopenharmony_ci goto err_free_pmd_dma_ptr; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci pgtable->pmd_ptrs[pgd_idx][pud_idx] = pmd_dma_ptr; 15862306a36Sopenharmony_ci pgtable->pud_ptrs[pgd_idx][pud_idx] = pmd_dma | IVPU_MMU_ENTRY_VALID; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci return pmd_dma_ptr; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cierr_free_pmd_dma_ptr: 16362306a36Sopenharmony_ci ivpu_mmu_pgtable_free(vdev, pmd_dma_ptr, pmd_dma); 16462306a36Sopenharmony_ci return NULL; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic u64* 16862306a36Sopenharmony_ciivpu_mmu_ensure_pte(struct ivpu_device *vdev, struct ivpu_mmu_pgtable *pgtable, 16962306a36Sopenharmony_ci int pgd_idx, int pud_idx, int pmd_idx) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci u64 *pte_dma_ptr = pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx]; 17262306a36Sopenharmony_ci dma_addr_t pte_dma; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (pte_dma_ptr) 17562306a36Sopenharmony_ci return pte_dma_ptr; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci pte_dma_ptr = dma_alloc_wc(vdev->drm.dev, IVPU_MMU_PGTABLE_SIZE, &pte_dma, GFP_KERNEL); 17862306a36Sopenharmony_ci if (!pte_dma_ptr) 17962306a36Sopenharmony_ci return NULL; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx] = pte_dma_ptr; 18262306a36Sopenharmony_ci pgtable->pmd_ptrs[pgd_idx][pud_idx][pmd_idx] = pte_dma | IVPU_MMU_ENTRY_VALID; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return pte_dma_ptr; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic int 18862306a36Sopenharmony_ciivpu_mmu_context_map_page(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, 18962306a36Sopenharmony_ci u64 vpu_addr, dma_addr_t dma_addr, u64 prot) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci u64 *pte; 19262306a36Sopenharmony_ci int pgd_idx = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr); 19362306a36Sopenharmony_ci int pud_idx = FIELD_GET(IVPU_MMU_PUD_INDEX_MASK, vpu_addr); 19462306a36Sopenharmony_ci int pmd_idx = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr); 19562306a36Sopenharmony_ci int pte_idx = FIELD_GET(IVPU_MMU_PTE_INDEX_MASK, vpu_addr); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* Allocate PUD - second level page table if needed */ 19862306a36Sopenharmony_ci if (!ivpu_mmu_ensure_pud(vdev, &ctx->pgtable, pgd_idx)) 19962306a36Sopenharmony_ci return -ENOMEM; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* Allocate PMD - third level page table if needed */ 20262306a36Sopenharmony_ci if (!ivpu_mmu_ensure_pmd(vdev, &ctx->pgtable, pgd_idx, pud_idx)) 20362306a36Sopenharmony_ci return -ENOMEM; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* Allocate PTE - fourth level page table if needed */ 20662306a36Sopenharmony_ci pte = ivpu_mmu_ensure_pte(vdev, &ctx->pgtable, pgd_idx, pud_idx, pmd_idx); 20762306a36Sopenharmony_ci if (!pte) 20862306a36Sopenharmony_ci return -ENOMEM; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* Update PTE */ 21162306a36Sopenharmony_ci pte[pte_idx] = dma_addr | prot; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return 0; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic int 21762306a36Sopenharmony_ciivpu_mmu_context_map_cont_64k(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u64 vpu_addr, 21862306a36Sopenharmony_ci dma_addr_t dma_addr, u64 prot) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci size_t size = IVPU_MMU_CONT_PAGES_SIZE; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci drm_WARN_ON(&vdev->drm, !IS_ALIGNED(vpu_addr, size)); 22362306a36Sopenharmony_ci drm_WARN_ON(&vdev->drm, !IS_ALIGNED(dma_addr, size)); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci prot |= IVPU_MMU_ENTRY_FLAG_CONT; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci while (size) { 22862306a36Sopenharmony_ci int ret = ivpu_mmu_context_map_page(vdev, ctx, vpu_addr, dma_addr, prot); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (ret) 23162306a36Sopenharmony_ci return ret; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci size -= IVPU_MMU_PAGE_SIZE; 23462306a36Sopenharmony_ci vpu_addr += IVPU_MMU_PAGE_SIZE; 23562306a36Sopenharmony_ci dma_addr += IVPU_MMU_PAGE_SIZE; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return 0; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic void ivpu_mmu_context_unmap_page(struct ivpu_mmu_context *ctx, u64 vpu_addr) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci int pgd_idx = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr); 24462306a36Sopenharmony_ci int pud_idx = FIELD_GET(IVPU_MMU_PUD_INDEX_MASK, vpu_addr); 24562306a36Sopenharmony_ci int pmd_idx = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr); 24662306a36Sopenharmony_ci int pte_idx = FIELD_GET(IVPU_MMU_PTE_INDEX_MASK, vpu_addr); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* Update PTE with dummy physical address and clear flags */ 24962306a36Sopenharmony_ci ctx->pgtable.pte_ptrs[pgd_idx][pud_idx][pmd_idx][pte_idx] = IVPU_MMU_ENTRY_INVALID; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic void 25362306a36Sopenharmony_ciivpu_mmu_context_flush_page_tables(struct ivpu_mmu_context *ctx, u64 vpu_addr, size_t size) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct ivpu_mmu_pgtable *pgtable = &ctx->pgtable; 25662306a36Sopenharmony_ci u64 end_addr = vpu_addr + size; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Align to PMD entry (2 MB) */ 25962306a36Sopenharmony_ci vpu_addr &= ~(IVPU_MMU_PTE_MAP_SIZE - 1); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci while (vpu_addr < end_addr) { 26262306a36Sopenharmony_ci int pgd_idx = FIELD_GET(IVPU_MMU_PGD_INDEX_MASK, vpu_addr); 26362306a36Sopenharmony_ci u64 pud_end = (pgd_idx + 1) * (u64)IVPU_MMU_PUD_MAP_SIZE; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci while (vpu_addr < end_addr && vpu_addr < pud_end) { 26662306a36Sopenharmony_ci int pud_idx = FIELD_GET(IVPU_MMU_PUD_INDEX_MASK, vpu_addr); 26762306a36Sopenharmony_ci u64 pmd_end = (pud_idx + 1) * (u64)IVPU_MMU_PMD_MAP_SIZE; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci while (vpu_addr < end_addr && vpu_addr < pmd_end) { 27062306a36Sopenharmony_ci int pmd_idx = FIELD_GET(IVPU_MMU_PMD_INDEX_MASK, vpu_addr); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci clflush_cache_range(pgtable->pte_ptrs[pgd_idx][pud_idx][pmd_idx], 27362306a36Sopenharmony_ci IVPU_MMU_PGTABLE_SIZE); 27462306a36Sopenharmony_ci vpu_addr += IVPU_MMU_PTE_MAP_SIZE; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci clflush_cache_range(pgtable->pmd_ptrs[pgd_idx][pud_idx], 27762306a36Sopenharmony_ci IVPU_MMU_PGTABLE_SIZE); 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci clflush_cache_range(pgtable->pud_ptrs[pgd_idx], IVPU_MMU_PGTABLE_SIZE); 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci clflush_cache_range(pgtable->pgd_dma_ptr, IVPU_MMU_PGTABLE_SIZE); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic int 28562306a36Sopenharmony_ciivpu_mmu_context_map_pages(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, 28662306a36Sopenharmony_ci u64 vpu_addr, dma_addr_t dma_addr, size_t size, u64 prot) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci int map_size; 28962306a36Sopenharmony_ci int ret; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci while (size) { 29262306a36Sopenharmony_ci if (!ivpu_disable_mmu_cont_pages && size >= IVPU_MMU_CONT_PAGES_SIZE && 29362306a36Sopenharmony_ci IS_ALIGNED(vpu_addr | dma_addr, IVPU_MMU_CONT_PAGES_SIZE)) { 29462306a36Sopenharmony_ci ret = ivpu_mmu_context_map_cont_64k(vdev, ctx, vpu_addr, dma_addr, prot); 29562306a36Sopenharmony_ci map_size = IVPU_MMU_CONT_PAGES_SIZE; 29662306a36Sopenharmony_ci } else { 29762306a36Sopenharmony_ci ret = ivpu_mmu_context_map_page(vdev, ctx, vpu_addr, dma_addr, prot); 29862306a36Sopenharmony_ci map_size = IVPU_MMU_PAGE_SIZE; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci if (ret) 30262306a36Sopenharmony_ci return ret; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci vpu_addr += map_size; 30562306a36Sopenharmony_ci dma_addr += map_size; 30662306a36Sopenharmony_ci size -= map_size; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci return 0; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic void ivpu_mmu_context_unmap_pages(struct ivpu_mmu_context *ctx, u64 vpu_addr, size_t size) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci while (size) { 31562306a36Sopenharmony_ci ivpu_mmu_context_unmap_page(ctx, vpu_addr); 31662306a36Sopenharmony_ci vpu_addr += IVPU_MMU_PAGE_SIZE; 31762306a36Sopenharmony_ci size -= IVPU_MMU_PAGE_SIZE; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ciint 32262306a36Sopenharmony_ciivpu_mmu_context_map_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, 32362306a36Sopenharmony_ci u64 vpu_addr, struct sg_table *sgt, bool llc_coherent) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct scatterlist *sg; 32662306a36Sopenharmony_ci int ret; 32762306a36Sopenharmony_ci u64 prot; 32862306a36Sopenharmony_ci u64 i; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (!IS_ALIGNED(vpu_addr, IVPU_MMU_PAGE_SIZE)) 33162306a36Sopenharmony_ci return -EINVAL; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (vpu_addr & ~IVPU_MMU_VPU_ADDRESS_MASK) 33462306a36Sopenharmony_ci return -EINVAL; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci prot = IVPU_MMU_ENTRY_MAPPED; 33762306a36Sopenharmony_ci if (llc_coherent) 33862306a36Sopenharmony_ci prot |= IVPU_MMU_ENTRY_FLAG_LLC_COHERENT; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci mutex_lock(&ctx->lock); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci for_each_sgtable_dma_sg(sgt, sg, i) { 34362306a36Sopenharmony_ci dma_addr_t dma_addr = sg_dma_address(sg) - sg->offset; 34462306a36Sopenharmony_ci size_t size = sg_dma_len(sg) + sg->offset; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci ret = ivpu_mmu_context_map_pages(vdev, ctx, vpu_addr, dma_addr, size, prot); 34762306a36Sopenharmony_ci if (ret) { 34862306a36Sopenharmony_ci ivpu_err(vdev, "Failed to map context pages\n"); 34962306a36Sopenharmony_ci mutex_unlock(&ctx->lock); 35062306a36Sopenharmony_ci return ret; 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci ivpu_mmu_context_flush_page_tables(ctx, vpu_addr, size); 35362306a36Sopenharmony_ci vpu_addr += size; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci mutex_unlock(&ctx->lock); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci ret = ivpu_mmu_invalidate_tlb(vdev, ctx->id); 35962306a36Sopenharmony_ci if (ret) 36062306a36Sopenharmony_ci ivpu_err(vdev, "Failed to invalidate TLB for ctx %u: %d\n", ctx->id, ret); 36162306a36Sopenharmony_ci return ret; 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_civoid 36562306a36Sopenharmony_ciivpu_mmu_context_unmap_sgt(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, 36662306a36Sopenharmony_ci u64 vpu_addr, struct sg_table *sgt) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci struct scatterlist *sg; 36962306a36Sopenharmony_ci int ret; 37062306a36Sopenharmony_ci u64 i; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (!IS_ALIGNED(vpu_addr, IVPU_MMU_PAGE_SIZE)) 37362306a36Sopenharmony_ci ivpu_warn(vdev, "Unaligned vpu_addr: 0x%llx\n", vpu_addr); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci mutex_lock(&ctx->lock); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci for_each_sgtable_dma_sg(sgt, sg, i) { 37862306a36Sopenharmony_ci size_t size = sg_dma_len(sg) + sg->offset; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci ivpu_mmu_context_unmap_pages(ctx, vpu_addr, size); 38162306a36Sopenharmony_ci ivpu_mmu_context_flush_page_tables(ctx, vpu_addr, size); 38262306a36Sopenharmony_ci vpu_addr += size; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci mutex_unlock(&ctx->lock); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci ret = ivpu_mmu_invalidate_tlb(vdev, ctx->id); 38862306a36Sopenharmony_ci if (ret) 38962306a36Sopenharmony_ci ivpu_warn(vdev, "Failed to invalidate TLB for ctx %u: %d\n", ctx->id, ret); 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ciint 39362306a36Sopenharmony_ciivpu_mmu_context_insert_node_locked(struct ivpu_mmu_context *ctx, 39462306a36Sopenharmony_ci const struct ivpu_addr_range *range, 39562306a36Sopenharmony_ci u64 size, struct drm_mm_node *node) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci lockdep_assert_held(&ctx->lock); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (!ivpu_disable_mmu_cont_pages && size >= IVPU_MMU_CONT_PAGES_SIZE) { 40062306a36Sopenharmony_ci if (!drm_mm_insert_node_in_range(&ctx->mm, node, size, IVPU_MMU_CONT_PAGES_SIZE, 0, 40162306a36Sopenharmony_ci range->start, range->end, DRM_MM_INSERT_BEST)) 40262306a36Sopenharmony_ci return 0; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci return drm_mm_insert_node_in_range(&ctx->mm, node, size, IVPU_MMU_PAGE_SIZE, 0, 40662306a36Sopenharmony_ci range->start, range->end, DRM_MM_INSERT_BEST); 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_civoid 41062306a36Sopenharmony_ciivpu_mmu_context_remove_node_locked(struct ivpu_mmu_context *ctx, struct drm_mm_node *node) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci lockdep_assert_held(&ctx->lock); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci drm_mm_remove_node(node); 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic int 41862306a36Sopenharmony_ciivpu_mmu_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 context_id) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci u64 start, end; 42162306a36Sopenharmony_ci int ret; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci mutex_init(&ctx->lock); 42462306a36Sopenharmony_ci INIT_LIST_HEAD(&ctx->bo_list); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci ret = ivpu_mmu_pgtable_init(vdev, &ctx->pgtable); 42762306a36Sopenharmony_ci if (ret) 42862306a36Sopenharmony_ci return ret; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (!context_id) { 43162306a36Sopenharmony_ci start = vdev->hw->ranges.global.start; 43262306a36Sopenharmony_ci end = vdev->hw->ranges.shave.end; 43362306a36Sopenharmony_ci } else { 43462306a36Sopenharmony_ci start = vdev->hw->ranges.user.start; 43562306a36Sopenharmony_ci end = vdev->hw->ranges.dma.end; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci drm_mm_init(&ctx->mm, start, end - start); 43962306a36Sopenharmony_ci ctx->id = context_id; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic void ivpu_mmu_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci if (drm_WARN_ON(&vdev->drm, !ctx->pgtable.pgd_dma_ptr)) 44762306a36Sopenharmony_ci return; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci mutex_destroy(&ctx->lock); 45062306a36Sopenharmony_ci ivpu_mmu_pgtables_free(vdev, &ctx->pgtable); 45162306a36Sopenharmony_ci drm_mm_takedown(&ctx->mm); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci ctx->pgtable.pgd_dma_ptr = NULL; 45462306a36Sopenharmony_ci ctx->pgtable.pgd_dma = 0; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ciint ivpu_mmu_global_context_init(struct ivpu_device *vdev) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci return ivpu_mmu_context_init(vdev, &vdev->gctx, IVPU_GLOBAL_CONTEXT_MMU_SSID); 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_civoid ivpu_mmu_global_context_fini(struct ivpu_device *vdev) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci return ivpu_mmu_context_fini(vdev, &vdev->gctx); 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_civoid ivpu_mmu_user_context_mark_invalid(struct ivpu_device *vdev, u32 ssid) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci struct ivpu_file_priv *file_priv; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci xa_lock(&vdev->context_xa); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci file_priv = xa_load(&vdev->context_xa, ssid); 47462306a36Sopenharmony_ci if (file_priv) 47562306a36Sopenharmony_ci file_priv->has_mmu_faults = true; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci xa_unlock(&vdev->context_xa); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ciint ivpu_mmu_user_context_init(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx, u32 ctx_id) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci int ret; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci drm_WARN_ON(&vdev->drm, !ctx_id); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci ret = ivpu_mmu_context_init(vdev, ctx, ctx_id); 48762306a36Sopenharmony_ci if (ret) { 48862306a36Sopenharmony_ci ivpu_err(vdev, "Failed to initialize context: %d\n", ret); 48962306a36Sopenharmony_ci return ret; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci ret = ivpu_mmu_set_pgtable(vdev, ctx_id, &ctx->pgtable); 49362306a36Sopenharmony_ci if (ret) { 49462306a36Sopenharmony_ci ivpu_err(vdev, "Failed to set page table: %d\n", ret); 49562306a36Sopenharmony_ci goto err_context_fini; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci return 0; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cierr_context_fini: 50162306a36Sopenharmony_ci ivpu_mmu_context_fini(vdev, ctx); 50262306a36Sopenharmony_ci return ret; 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_civoid ivpu_mmu_user_context_fini(struct ivpu_device *vdev, struct ivpu_mmu_context *ctx) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci drm_WARN_ON(&vdev->drm, !ctx->id); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci ivpu_mmu_clear_pgtable(vdev, ctx->id); 51062306a36Sopenharmony_ci ivpu_mmu_context_fini(vdev, ctx); 51162306a36Sopenharmony_ci} 512