Path: blob/master/drivers/accel/habanalabs/common/mmu/mmu.c
26488 views
// SPDX-License-Identifier: GPL-2.012/*3* Copyright 2016-2022 HabanaLabs, Ltd.4* All Rights Reserved.5*/67#include <linux/slab.h>8#include <linux/pci.h>910#include "../habanalabs.h"1112#include <trace/events/habanalabs.h>1314/**15* hl_mmu_get_funcs() - get MMU functions structure16* @hdev: habanalabs device structure.17* @pgt_residency: page table residency.18* @is_dram_addr: true if we need HMMU functions19*20* @return appropriate MMU functions structure21*/22static struct hl_mmu_funcs *hl_mmu_get_funcs(struct hl_device *hdev, int pgt_residency,23bool is_dram_addr)24{25return &hdev->mmu_func[pgt_residency];26}2728bool hl_is_dram_va(struct hl_device *hdev, u64 virt_addr)29{30struct asic_fixed_properties *prop = &hdev->asic_prop;3132return hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size,33prop->dmmu.start_addr,34prop->dmmu.end_addr);35}3637/**38* hl_mmu_init() - initialize the MMU module.39* @hdev: habanalabs device structure.40*41* Return: 0 for success, non-zero for failure.42*/43int hl_mmu_init(struct hl_device *hdev)44{45int rc = -EOPNOTSUPP;4647if (hdev->mmu_disable)48return 0;4950mutex_init(&hdev->mmu_lock);5152if (hdev->mmu_func[MMU_DR_PGT].init != NULL) {53rc = hdev->mmu_func[MMU_DR_PGT].init(hdev);54if (rc)55return rc;56}5758if (hdev->mmu_func[MMU_HR_PGT].init != NULL) {59rc = hdev->mmu_func[MMU_HR_PGT].init(hdev);60if (rc)61goto fini_dr_mmu;62}6364return 0;6566fini_dr_mmu:67if (hdev->mmu_func[MMU_DR_PGT].fini != NULL)68hdev->mmu_func[MMU_DR_PGT].fini(hdev);6970return rc;71}7273/**74* hl_mmu_fini() - release the MMU module.75* @hdev: habanalabs device structure.76*77* This function does the following:78* - Disable MMU in H/W.79* - Free the pgt_infos pool.80*81* All contexts should be freed before calling this function.82*/83void hl_mmu_fini(struct hl_device *hdev)84{85if (hdev->mmu_disable)86return;8788if (hdev->mmu_func[MMU_DR_PGT].fini != NULL)89hdev->mmu_func[MMU_DR_PGT].fini(hdev);9091if (hdev->mmu_func[MMU_HR_PGT].fini != NULL)92hdev->mmu_func[MMU_HR_PGT].fini(hdev);9394mutex_destroy(&hdev->mmu_lock);95}9697/**98* hl_mmu_ctx_init() - initialize a context for using the MMU module.99* @ctx: pointer to the context structure to initialize.100*101* Initialize a mutex to protect the concurrent mapping flow, a hash to hold all102* page tables hops related to this context.103* Return: 0 on success, non-zero otherwise.104*/105int hl_mmu_ctx_init(struct hl_ctx *ctx)106{107struct hl_device *hdev = ctx->hdev;108int rc = -EOPNOTSUPP;109110if (hdev->mmu_disable)111return 0;112113if (hdev->mmu_func[MMU_DR_PGT].ctx_init != NULL) {114rc = hdev->mmu_func[MMU_DR_PGT].ctx_init(ctx);115if (rc)116return rc;117}118119if (hdev->mmu_func[MMU_HR_PGT].ctx_init != NULL) {120rc = hdev->mmu_func[MMU_HR_PGT].ctx_init(ctx);121if (rc)122goto fini_dr_ctx;123}124125return 0;126127fini_dr_ctx:128if (hdev->mmu_func[MMU_DR_PGT].fini != NULL)129hdev->mmu_func[MMU_DR_PGT].fini(hdev);130131return rc;132}133134/*135* hl_mmu_ctx_fini - disable a ctx from using the mmu module136*137* @ctx: pointer to the context structure138*139* This function does the following:140* - Free any pgts which were not freed yet141* - Free the mutex142* - Free DRAM default page mapping hops143*/144void hl_mmu_ctx_fini(struct hl_ctx *ctx)145{146struct hl_device *hdev = ctx->hdev;147148if (hdev->mmu_disable)149return;150151if (hdev->mmu_func[MMU_DR_PGT].ctx_fini != NULL)152hdev->mmu_func[MMU_DR_PGT].ctx_fini(ctx);153154if (hdev->mmu_func[MMU_HR_PGT].ctx_fini != NULL)155hdev->mmu_func[MMU_HR_PGT].ctx_fini(ctx);156}157158/*159* hl_mmu_get_real_page_size - get real page size to use in map/unmap operation160*161* @hdev: pointer to device data.162* @mmu_prop: MMU properties.163* @page_size: page size164* @real_page_size: set here the actual page size to use for the operation165* @is_dram_addr: true if DRAM address, otherwise false.166*167* @return 0 on success, otherwise non 0 error code168*169* note that this is general implementation that can fit most MMU arch. but as this is used as an170* MMU function:171* 1. it shall not be called directly- only from mmu_func structure instance172* 2. each MMU may modify the implementation internally173*/174int hl_mmu_get_real_page_size(struct hl_device *hdev, struct hl_mmu_properties *mmu_prop,175u32 page_size, u32 *real_page_size, bool is_dram_addr)176{177/*178* The H/W handles mapping of specific page sizes. Hence if the page179* size is bigger, we break it to sub-pages and map them separately.180*/181if ((page_size % mmu_prop->page_size) == 0) {182*real_page_size = mmu_prop->page_size;183return 0;184}185186dev_err(hdev->dev, "page size of %u is not %uKB aligned, can't map\n",187page_size, mmu_prop->page_size >> 10);188189return -EFAULT;190}191192static struct hl_mmu_properties *hl_mmu_get_prop(struct hl_device *hdev, u32 page_size,193bool is_dram_addr)194{195struct asic_fixed_properties *prop = &hdev->asic_prop;196197if (is_dram_addr)198return &prop->dmmu;199else if ((page_size % prop->pmmu_huge.page_size) == 0)200return &prop->pmmu_huge;201202return &prop->pmmu;203}204205/*206* hl_mmu_unmap_page - unmaps a virtual addr207*208* @ctx: pointer to the context structure209* @virt_addr: virt addr to map from210* @page_size: size of the page to unmap211* @flush_pte: whether to do a PCI flush212*213* This function does the following:214* - Check that the virt addr is mapped215* - Unmap the virt addr and frees pgts if possible216* - Returns 0 on success, -EINVAL if the given addr is not mapped217*218* Because this function changes the page tables in the device and because it219* changes the MMU hash, it must be protected by a lock.220* However, because it maps only a single page, the lock should be implemented221* in a higher level in order to protect the entire mapping of the memory area222*223* For optimization reasons PCI flush may be requested once after unmapping of224* large area.225*/226int hl_mmu_unmap_page(struct hl_ctx *ctx, u64 virt_addr, u32 page_size, bool flush_pte)227{228struct hl_device *hdev = ctx->hdev;229struct hl_mmu_properties *mmu_prop;230struct hl_mmu_funcs *mmu_funcs;231int i, pgt_residency, rc = 0;232u32 real_page_size, npages;233u64 real_virt_addr;234bool is_dram_addr;235236if (hdev->mmu_disable)237return 0;238239is_dram_addr = hl_is_dram_va(hdev, virt_addr);240mmu_prop = hl_mmu_get_prop(hdev, page_size, is_dram_addr);241242pgt_residency = mmu_prop->host_resident ? MMU_HR_PGT : MMU_DR_PGT;243mmu_funcs = hl_mmu_get_funcs(hdev, pgt_residency, is_dram_addr);244245rc = hdev->asic_funcs->mmu_get_real_page_size(hdev, mmu_prop, page_size, &real_page_size,246is_dram_addr);247if (rc)248return rc;249250npages = page_size / real_page_size;251real_virt_addr = virt_addr;252253for (i = 0 ; i < npages ; i++) {254rc = mmu_funcs->unmap(ctx, real_virt_addr, is_dram_addr);255if (rc)256break;257258real_virt_addr += real_page_size;259}260261if (flush_pte)262mmu_funcs->flush(ctx);263264if (trace_habanalabs_mmu_unmap_enabled() && !rc)265trace_habanalabs_mmu_unmap(&hdev->pdev->dev, virt_addr, 0, page_size, flush_pte);266267return rc;268}269270/*271* hl_mmu_map_page - maps a virtual addr to physical addr272*273* @ctx: pointer to the context structure274* @virt_addr: virt addr to map from275* @phys_addr: phys addr to map to276* @page_size: physical page size277* @flush_pte: whether to do a PCI flush278*279* This function does the following:280* - Check that the virt addr is not mapped281* - Allocate pgts as necessary in order to map the virt addr to the phys282* - Returns 0 on success, -EINVAL if addr is already mapped, or -ENOMEM.283*284* Because this function changes the page tables in the device and because it285* changes the MMU hash, it must be protected by a lock.286* However, because it maps only a single page, the lock should be implemented287* in a higher level in order to protect the entire mapping of the memory area288*289* For optimization reasons PCI flush may be requested once after mapping of290* large area.291*/292int hl_mmu_map_page(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, u32 page_size,293bool flush_pte)294{295int i, rc, pgt_residency, mapped_cnt = 0;296struct hl_device *hdev = ctx->hdev;297struct hl_mmu_properties *mmu_prop;298u64 real_virt_addr, real_phys_addr;299struct hl_mmu_funcs *mmu_funcs;300u32 real_page_size, npages;301bool is_dram_addr;302303304if (hdev->mmu_disable)305return 0;306307is_dram_addr = hl_is_dram_va(hdev, virt_addr);308mmu_prop = hl_mmu_get_prop(hdev, page_size, is_dram_addr);309310pgt_residency = mmu_prop->host_resident ? MMU_HR_PGT : MMU_DR_PGT;311mmu_funcs = hl_mmu_get_funcs(hdev, pgt_residency, is_dram_addr);312313rc = hdev->asic_funcs->mmu_get_real_page_size(hdev, mmu_prop, page_size, &real_page_size,314is_dram_addr);315if (rc)316return rc;317318/*319* Verify that the phys and virt addresses are aligned with the320* MMU page size (in dram this means checking the address and MMU321* after scrambling)322*/323if ((is_dram_addr &&324((hdev->asic_funcs->scramble_addr(hdev, phys_addr) &325(mmu_prop->page_size - 1)) ||326(hdev->asic_funcs->scramble_addr(hdev, virt_addr) &327(mmu_prop->page_size - 1)))) ||328(!is_dram_addr && ((phys_addr & (real_page_size - 1)) ||329(virt_addr & (real_page_size - 1)))))330dev_crit(hdev->dev,331"Mapping address 0x%llx with virtual address 0x%llx and page size of 0x%x is erroneous! Addresses must be divisible by page size",332phys_addr, virt_addr, real_page_size);333334npages = page_size / real_page_size;335real_virt_addr = virt_addr;336real_phys_addr = phys_addr;337338for (i = 0 ; i < npages ; i++) {339rc = mmu_funcs->map(ctx, real_virt_addr, real_phys_addr, real_page_size,340is_dram_addr);341if (rc)342goto err;343344real_virt_addr += real_page_size;345real_phys_addr += real_page_size;346mapped_cnt++;347}348349if (flush_pte)350mmu_funcs->flush(ctx);351352trace_habanalabs_mmu_map(&hdev->pdev->dev, virt_addr, phys_addr, page_size, flush_pte);353354return 0;355356err:357real_virt_addr = virt_addr;358for (i = 0 ; i < mapped_cnt ; i++) {359if (mmu_funcs->unmap(ctx, real_virt_addr, is_dram_addr))360dev_warn_ratelimited(hdev->dev,361"failed to unmap va: 0x%llx\n", real_virt_addr);362363real_virt_addr += real_page_size;364}365366mmu_funcs->flush(ctx);367368return rc;369}370371/*372* hl_mmu_map_contiguous - implements a wrapper for hl_mmu_map_page373* for mapping contiguous physical memory374*375* @ctx: pointer to the context structure376* @virt_addr: virt addr to map from377* @phys_addr: phys addr to map to378* @size: size to map379*380*/381int hl_mmu_map_contiguous(struct hl_ctx *ctx, u64 virt_addr,382u64 phys_addr, u32 size)383{384struct hl_device *hdev = ctx->hdev;385struct asic_fixed_properties *prop = &hdev->asic_prop;386u64 curr_va, curr_pa;387u32 page_size;388bool flush_pte;389int rc = 0, off;390391if (hl_mem_area_inside_range(virt_addr, size,392prop->dmmu.start_addr, prop->dmmu.end_addr))393page_size = prop->dmmu.page_size;394else if (hl_mem_area_inside_range(virt_addr, size,395prop->pmmu.start_addr, prop->pmmu.end_addr))396page_size = prop->pmmu.page_size;397else if (hl_mem_area_inside_range(virt_addr, size,398prop->pmmu_huge.start_addr, prop->pmmu_huge.end_addr))399page_size = prop->pmmu_huge.page_size;400else401return -EINVAL;402403for (off = 0 ; off < size ; off += page_size) {404curr_va = virt_addr + off;405curr_pa = phys_addr + off;406flush_pte = (off + page_size) >= size;407rc = hl_mmu_map_page(ctx, curr_va, curr_pa, page_size,408flush_pte);409if (rc) {410dev_err(hdev->dev,411"Map failed for va 0x%llx to pa 0x%llx\n",412curr_va, curr_pa);413/* last mapping failed so don't try to unmap it - reduce off by page_size */414off -= page_size;415goto unmap;416}417}418419return rc;420421unmap:422for (; off >= 0 ; off -= page_size) {423curr_va = virt_addr + off;424flush_pte = (off - (s32) page_size) < 0;425if (hl_mmu_unmap_page(ctx, curr_va, page_size, flush_pte))426dev_warn_ratelimited(hdev->dev,427"failed to unmap va 0x%llx\n", curr_va);428}429430return rc;431}432433/*434* hl_mmu_unmap_contiguous - implements a wrapper for hl_mmu_unmap_page435* for unmapping contiguous physical memory436*437* @ctx: pointer to the context structure438* @virt_addr: virt addr to unmap439* @size: size to unmap440*441*/442int hl_mmu_unmap_contiguous(struct hl_ctx *ctx, u64 virt_addr, u32 size)443{444struct hl_device *hdev = ctx->hdev;445struct asic_fixed_properties *prop = &hdev->asic_prop;446u64 curr_va;447u32 page_size;448bool flush_pte;449int rc = 0, off;450451if (hl_mem_area_inside_range(virt_addr, size,452prop->dmmu.start_addr, prop->dmmu.end_addr))453page_size = prop->dmmu.page_size;454else if (hl_mem_area_inside_range(virt_addr, size,455prop->pmmu.start_addr, prop->pmmu.end_addr))456page_size = prop->pmmu.page_size;457else if (hl_mem_area_inside_range(virt_addr, size,458prop->pmmu_huge.start_addr, prop->pmmu_huge.end_addr))459page_size = prop->pmmu_huge.page_size;460else461return -EINVAL;462463for (off = 0 ; off < size ; off += page_size) {464curr_va = virt_addr + off;465flush_pte = (off + page_size) >= size;466rc = hl_mmu_unmap_page(ctx, curr_va, page_size, flush_pte);467if (rc)468dev_warn_ratelimited(hdev->dev,469"Unmap failed for va 0x%llx\n", curr_va);470}471472return rc;473}474475static void hl_mmu_pa_page_with_offset(struct hl_ctx *ctx, u64 virt_addr,476struct hl_mmu_hop_info *hops,477u64 *phys_addr)478{479struct asic_fixed_properties *prop = &ctx->hdev->asic_prop;480u64 offset_mask, addr_mask, hop_shift, tmp_phys_addr;481struct hl_mmu_properties *mmu_prop;482483/* last hop holds the phys address and flags */484if (hops->unscrambled_paddr)485tmp_phys_addr = hops->unscrambled_paddr;486else487tmp_phys_addr = hops->hop_info[hops->used_hops - 1].hop_pte_val;488489if (hops->range_type == HL_VA_RANGE_TYPE_HOST_HUGE)490mmu_prop = &prop->pmmu_huge;491else if (hops->range_type == HL_VA_RANGE_TYPE_HOST)492mmu_prop = &prop->pmmu;493else /* HL_VA_RANGE_TYPE_DRAM */494mmu_prop = &prop->dmmu;495496if ((hops->range_type == HL_VA_RANGE_TYPE_DRAM) &&497!is_power_of_2(prop->dram_page_size)) {498u64 dram_page_size, dram_base, abs_phys_addr, abs_virt_addr,499page_id, page_start;500u32 page_off;501502/*503* Bit arithmetic cannot be used for non power of two page504* sizes. In addition, since bit arithmetic is not used,505* we cannot ignore dram base. All that shall be considered.506*/507508dram_page_size = prop->dram_page_size;509dram_base = prop->dram_base_address;510abs_phys_addr = tmp_phys_addr - dram_base;511abs_virt_addr = virt_addr - dram_base;512page_id = DIV_ROUND_DOWN_ULL(abs_phys_addr, dram_page_size);513page_start = page_id * dram_page_size;514div_u64_rem(abs_virt_addr, dram_page_size, &page_off);515516*phys_addr = page_start + page_off + dram_base;517} else {518/*519* find the correct hop shift field in hl_mmu_properties520* structure in order to determine the right masks521* for the page offset.522*/523hop_shift = mmu_prop->hop_shifts[hops->used_hops - 1];524offset_mask = (1ull << hop_shift) - 1;525addr_mask = ~(offset_mask);526*phys_addr = (tmp_phys_addr & addr_mask) |527(virt_addr & offset_mask);528}529}530531int hl_mmu_va_to_pa(struct hl_ctx *ctx, u64 virt_addr, u64 *phys_addr)532{533struct hl_mmu_hop_info hops;534int rc;535536memset(&hops, 0, sizeof(hops));537538rc = hl_mmu_get_tlb_info(ctx, virt_addr, &hops);539if (rc)540return rc;541542hl_mmu_pa_page_with_offset(ctx, virt_addr, &hops, phys_addr);543544return 0;545}546547int hl_mmu_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr,548struct hl_mmu_hop_info *hops)549{550struct hl_device *hdev = ctx->hdev;551struct asic_fixed_properties *prop;552struct hl_mmu_properties *mmu_prop;553struct hl_mmu_funcs *mmu_funcs;554int pgt_residency, rc;555bool is_dram_addr;556557if (hdev->mmu_disable)558return -EOPNOTSUPP;559560prop = &hdev->asic_prop;561hops->scrambled_vaddr = virt_addr; /* assume no scrambling */562563is_dram_addr = hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size,564prop->dmmu.start_addr,565prop->dmmu.end_addr);566567/* host-residency is the same in PMMU and PMMU huge, no need to distinguish here */568mmu_prop = is_dram_addr ? &prop->dmmu : &prop->pmmu;569pgt_residency = mmu_prop->host_resident ? MMU_HR_PGT : MMU_DR_PGT;570mmu_funcs = hl_mmu_get_funcs(hdev, pgt_residency, is_dram_addr);571572mutex_lock(&hdev->mmu_lock);573rc = mmu_funcs->get_tlb_info(ctx, virt_addr, hops);574mutex_unlock(&hdev->mmu_lock);575576if (rc)577return rc;578579/* add page offset to physical address */580if (hops->unscrambled_paddr)581hl_mmu_pa_page_with_offset(ctx, virt_addr, hops, &hops->unscrambled_paddr);582583return 0;584}585586int hl_mmu_if_set_funcs(struct hl_device *hdev)587{588struct asic_fixed_properties *prop = &hdev->asic_prop;589590if (hdev->mmu_disable)591return 0;592593switch (hdev->asic_type) {594case ASIC_GOYA:595case ASIC_GAUDI:596case ASIC_GAUDI_SEC:597hl_mmu_v1_set_funcs(hdev, &hdev->mmu_func[MMU_DR_PGT]);598break;599case ASIC_GAUDI2:600case ASIC_GAUDI2B:601case ASIC_GAUDI2C:602case ASIC_GAUDI2D:603hl_mmu_v2_set_funcs(hdev, &hdev->mmu_func[MMU_DR_PGT]);604if (prop->pmmu.host_resident)605hl_mmu_v2_hr_set_funcs(hdev, &hdev->mmu_func[MMU_HR_PGT]);606break;607default:608dev_err(hdev->dev, "Unrecognized ASIC type %d\n",609hdev->asic_type);610return -EOPNOTSUPP;611}612613return 0;614}615616/**617* hl_mmu_scramble_addr() - The generic mmu address scrambling routine.618* @hdev: pointer to device data.619* @addr: The address to scramble.620*621* Return: The scrambled address.622*/623u64 hl_mmu_scramble_addr(struct hl_device *hdev, u64 addr)624{625return addr;626}627628/**629* hl_mmu_descramble_addr() - The generic mmu address descrambling630* routine.631* @hdev: pointer to device data.632* @addr: The address to descramble.633*634* Return: The un-scrambled address.635*/636u64 hl_mmu_descramble_addr(struct hl_device *hdev, u64 addr)637{638return addr;639}640641int hl_mmu_invalidate_cache(struct hl_device *hdev, bool is_hard, u32 flags)642{643int rc;644645rc = hdev->asic_funcs->mmu_invalidate_cache(hdev, is_hard, flags);646if (rc)647dev_err_ratelimited(hdev->dev,648"%s: %s cache invalidation failed, rc=%d\n",649dev_name(&hdev->pdev->dev),650flags == VM_TYPE_USERPTR ? "PMMU" : "HMMU", rc);651652return rc;653}654655int hl_mmu_invalidate_cache_range(struct hl_device *hdev, bool is_hard,656u32 flags, u32 asid, u64 va, u64 size)657{658int rc;659660rc = hdev->asic_funcs->mmu_invalidate_cache_range(hdev, is_hard, flags,661asid, va, size);662if (rc)663dev_err_ratelimited(hdev->dev,664"%s: %s cache range invalidation failed: va=%#llx, size=%llu, rc=%d",665dev_name(&hdev->pdev->dev), flags == VM_TYPE_USERPTR ? "PMMU" : "HMMU",666va, size, rc);667668return rc;669}670671static void hl_mmu_prefetch_work_function(struct work_struct *work)672{673struct hl_prefetch_work *pfw = container_of(work, struct hl_prefetch_work, prefetch_work);674struct hl_ctx *ctx = pfw->ctx;675struct hl_device *hdev = ctx->hdev;676677if (!hl_device_operational(hdev, NULL))678goto put_ctx;679680mutex_lock(&hdev->mmu_lock);681682hdev->asic_funcs->mmu_prefetch_cache_range(ctx, pfw->flags, pfw->asid, pfw->va, pfw->size);683684mutex_unlock(&hdev->mmu_lock);685686put_ctx:687/*688* context was taken in the common mmu prefetch function- see comment there about689* context handling.690*/691hl_ctx_put(ctx);692kfree(pfw);693}694695int hl_mmu_prefetch_cache_range(struct hl_ctx *ctx, u32 flags, u32 asid, u64 va, u64 size)696{697struct hl_prefetch_work *handle_prefetch_work;698699handle_prefetch_work = kmalloc(sizeof(*handle_prefetch_work), GFP_KERNEL);700if (!handle_prefetch_work)701return -ENOMEM;702703INIT_WORK(&handle_prefetch_work->prefetch_work, hl_mmu_prefetch_work_function);704handle_prefetch_work->ctx = ctx;705handle_prefetch_work->va = va;706handle_prefetch_work->size = size;707handle_prefetch_work->flags = flags;708handle_prefetch_work->asid = asid;709710/*711* as actual prefetch is done in a WQ we must get the context (and put it712* at the end of the work function)713*/714hl_ctx_get(ctx);715queue_work(ctx->hdev->prefetch_wq, &handle_prefetch_work->prefetch_work);716717return 0;718}719720u64 hl_mmu_get_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte)721{722return (curr_pte & PAGE_PRESENT_MASK) ? (curr_pte & HOP_PHYS_ADDR_MASK) : ULLONG_MAX;723}724725/**726* hl_mmu_get_hop_pte_phys_addr() - extract PTE address from HOP727* @ctx: pointer to the context structure to initialize.728* @mmu_prop: MMU properties.729* @hop_idx: HOP index.730* @hop_addr: HOP address.731* @virt_addr: virtual address for the translation.732*733* @return the matching PTE value on success, otherwise U64_MAX.734*/735u64 hl_mmu_get_hop_pte_phys_addr(struct hl_ctx *ctx, struct hl_mmu_properties *mmu_prop,736u8 hop_idx, u64 hop_addr, u64 virt_addr)737{738u64 mask, shift;739740if (hop_idx >= mmu_prop->num_hops) {741dev_err_ratelimited(ctx->hdev->dev, "Invalid hop index %d\n", hop_idx);742return U64_MAX;743}744745shift = mmu_prop->hop_shifts[hop_idx];746mask = mmu_prop->hop_masks[hop_idx];747748return hop_addr + ctx->hdev->asic_prop.mmu_pte_size * ((virt_addr & mask) >> shift);749}750751static void mmu_dma_mem_free_from_chunk(struct gen_pool *pool,752struct gen_pool_chunk *chunk,753void *data)754{755struct hl_device *hdev = data;756757hl_asic_dma_free_coherent(hdev, (chunk->end_addr - chunk->start_addr) + 1,758(void *)chunk->start_addr, chunk->phys_addr);759}760761void hl_mmu_hr_flush(struct hl_ctx *ctx)762{763/* a flush operation requires memory barrier */764mb();765}766767/**768* hl_mmu_hr_pool_destroy() - destroy genpool769* @hdev: habanalabs device structure.770* @hr_priv: MMU HR private data.771* @hop_table_size: HOP table size.772*773* This function does the following:774* - free entries allocated for shadow HOP0775* - free pool chunks776* - free pool777*/778static void hl_mmu_hr_pool_destroy(struct hl_device *hdev, struct hl_mmu_hr_priv *hr_priv,779u32 hop_table_size)780{781struct asic_fixed_properties *prop = &hdev->asic_prop;782struct gen_pool **pool = &hr_priv->mmu_pgt_pool;783struct pgt_info *hop0_pgt;784int asid;785786if (ZERO_OR_NULL_PTR(*pool))787return;788789/* Free the Fixed allocation of HOPs0 */790if (hr_priv->mmu_asid_hop0) {791for (asid = 0 ; asid < prop->max_asid ; asid++) {792hop0_pgt = &hr_priv->mmu_asid_hop0[asid];793if (ZERO_OR_NULL_PTR(hop0_pgt->virt_addr))794continue;795796gen_pool_free(*pool, (uintptr_t) hop0_pgt->virt_addr, hop_table_size);797}798}799800gen_pool_for_each_chunk(*pool, mmu_dma_mem_free_from_chunk, hdev);801gen_pool_destroy(*pool);802803/* Make sure that if we arrive here again without init was called we804* won't cause kernel panic. This can happen for example if we fail805* during hard reset code at certain points806*/807*pool = NULL;808}809810/**811* hl_mmu_hr_init() - initialize the MMU module.812* @hdev: habanalabs device structure.813* @hr_priv: MMU HR private data.814* @hop_table_size: HOP table size.815* @pgt_size: memory size allocated for the page table816*817* @return 0 on success otherwise non-zero error code818*819* This function does the following:820* - Create a pool of pages for pgt_infos.821* - Create a shadow table for pgt822*/823int hl_mmu_hr_init(struct hl_device *hdev, struct hl_mmu_hr_priv *hr_priv, u32 hop_table_size,824u64 pgt_size)825{826struct asic_fixed_properties *prop = &hdev->asic_prop;827size_t pool_chunk_size = SZ_4M;828struct pgt_info *hop0_pgt;829dma_addr_t dma_addr;830u64 virt_addr;831int i, rc;832833/*834* we set alloc size as PAGE_SIZE (sine dma_alloc_coherent allocation order/size is835* PAGE_SHIFT/PAGE_SIZE) in order to be able to control the allocations alignment.836* This way we can call "DMA alloc align" according to dma_alloc granularity and supply837* allocations with higher-order alignment restrictions838*/839hr_priv->mmu_pgt_pool = gen_pool_create(PAGE_SHIFT, -1);840if (ZERO_OR_NULL_PTR(hr_priv->mmu_pgt_pool)) {841dev_err(hdev->dev, "Failed to create hr page pool\n");842return -ENOMEM;843}844845hr_priv->mmu_asid_hop0 = kvcalloc(prop->max_asid, sizeof(struct pgt_info), GFP_KERNEL);846if (ZERO_OR_NULL_PTR(hr_priv->mmu_asid_hop0)) {847dev_err(hdev->dev, "Failed to allocate hr-mmu hop0 table\n");848rc = -ENOMEM;849goto destroy_mmu_pgt_pool;850}851852for (i = 0 ; i < pgt_size ; i += pool_chunk_size) {853virt_addr = (uintptr_t) hl_asic_dma_alloc_coherent(hdev, pool_chunk_size,854&dma_addr,855GFP_KERNEL | __GFP_ZERO);856if (ZERO_OR_NULL_PTR(virt_addr)) {857dev_err(hdev->dev,858"Failed to allocate memory for host-resident page pool\n");859rc = -ENOMEM;860goto destroy_mmu_pgt_pool;861}862863rc = gen_pool_add_virt(hr_priv->mmu_pgt_pool, virt_addr, (phys_addr_t) dma_addr,864pool_chunk_size, -1);865if (rc) {866dev_err(hdev->dev, "Failed to fill host-resident page pool\n");867goto destroy_mmu_pgt_pool;868}869}870871for (i = 0 ; i < prop->max_asid ; i++) {872hop0_pgt = &hr_priv->mmu_asid_hop0[i];873hop0_pgt->virt_addr = (uintptr_t)874gen_pool_dma_zalloc_align(hr_priv->mmu_pgt_pool,875hop_table_size,876(dma_addr_t *) &hop0_pgt->phys_addr,877hop_table_size);878if (!hop0_pgt->virt_addr) {879dev_err(hdev->dev, "Failed to allocate HOP from pgt pool\n");880rc = -ENOMEM;881goto destroy_mmu_pgt_pool;882}883}884885/* MMU H/W init will be done in device hw_init() */886887return 0;888889destroy_mmu_pgt_pool:890hl_mmu_hr_pool_destroy(hdev, hr_priv, hop_table_size);891if (!ZERO_OR_NULL_PTR(hr_priv->mmu_asid_hop0))892kvfree(hr_priv->mmu_asid_hop0);893894return rc;895}896897/**898* hl_mmu_hr_fini() - release the MMU module.899* @hdev: habanalabs device structure.900* @hr_priv: MMU host resident private info.901* @hop_table_size: HOP table size902*903* This function does the following:904* - Disable MMU in H/W.905* - Free the pgt_infos pool.906*907* All contexts should be freed before calling this function.908*/909void hl_mmu_hr_fini(struct hl_device *hdev, struct hl_mmu_hr_priv *hr_priv, u32 hop_table_size)910{911/* MMU H/W fini was already done in device hw_fini() */912913hl_mmu_hr_pool_destroy(hdev, hr_priv, hop_table_size);914915if (!ZERO_OR_NULL_PTR(hr_priv->mmu_asid_hop0)) {916kvfree(hr_priv->mmu_asid_hop0);917918/* Make sure that if we arrive here again without init was919* called we won't cause kernel panic. This can happen for920* example if we fail during hard reset code at certain points921*/922hr_priv->mmu_asid_hop0 = NULL;923}924}925926/**927* hl_mmu_hr_free_hop_remove_pgt() - free HOP and remove PGT from hash928* @pgt_info: page table info structure.929* @hr_priv: MMU HR private data.930* @hop_table_size: HOP table size.931*/932void hl_mmu_hr_free_hop_remove_pgt(struct pgt_info *pgt_info, struct hl_mmu_hr_priv *hr_priv,933u32 hop_table_size)934{935gen_pool_free(hr_priv->mmu_pgt_pool, pgt_info->virt_addr, hop_table_size);936hash_del(&pgt_info->node);937kfree(pgt_info);938}939940/**941* hl_mmu_hr_pte_phys_to_virt() - translate PTE phys addr to virt addr942* @ctx: pointer to the context structure943* @pgt: pgt_info for the HOP hosting the PTE944* @phys_pte_addr: phys address of the PTE945* @hop_table_size: HOP table size946*947* @return PTE virtual address948*949* The function use the pgt_info to get HOP base virt addr and obtain the PTE's virt addr950* by adding the PTE offset.951*/952u64 hl_mmu_hr_pte_phys_to_virt(struct hl_ctx *ctx, struct pgt_info *pgt,953u64 phys_pte_addr, u32 hop_table_size)954{955u64 page_mask = (hop_table_size - 1);956u64 pte_offset = phys_pte_addr & page_mask;957958return pgt->virt_addr + pte_offset;959}960961/**962* hl_mmu_hr_write_pte() - write HR PTE963* @ctx: pointer to the context structure964* @pgt_info: HOP's page table info structure965* @phys_pte_addr: phys PTE address966* @val: raw PTE data967* @hop_table_size: HOP table size968*/969void hl_mmu_hr_write_pte(struct hl_ctx *ctx, struct pgt_info *pgt_info, u64 phys_pte_addr,970u64 val, u32 hop_table_size)971{972/*973* The value to write is the phys address of the next hop +974* flags at the 12 LSBs.975*/976u64 virt_addr = hl_mmu_hr_pte_phys_to_virt(ctx, pgt_info, phys_pte_addr, hop_table_size);977978*((u64 *) (uintptr_t) virt_addr) = val;979}980981/**982* hl_mmu_hr_clear_pte() - clear HR PTE983* @ctx: pointer to the context structure984* @pgt_info: HOP's page table info structure985* @phys_pte_addr: phys PTE address986* @hop_table_size: HOP table size987*/988void hl_mmu_hr_clear_pte(struct hl_ctx *ctx, struct pgt_info *pgt_info, u64 phys_pte_addr,989u32 hop_table_size)990{991/* no need to transform the value to physical address */992hl_mmu_hr_write_pte(ctx, pgt_info, phys_pte_addr, 0, hop_table_size);993}994995/**996* hl_mmu_hr_put_pte() - put HR PTE and remove it if necessary (no more PTEs)997* @ctx: pointer to the context structure998* @pgt_info: HOP's page table info structure999* @hr_priv: HR MMU private info1000* @hop_table_size: HOP table size1001*1002* @return number of PTEs still in the HOP1003*/1004int hl_mmu_hr_put_pte(struct hl_ctx *ctx, struct pgt_info *pgt_info,1005struct hl_mmu_hr_priv *hr_priv,1006u32 hop_table_size)1007{1008int num_of_ptes_left;10091010pgt_info->num_of_ptes--;10111012/*1013* Need to save the number of ptes left because free_hop might free1014* the pgt_info1015*/1016num_of_ptes_left = pgt_info->num_of_ptes;1017if (!num_of_ptes_left)1018hl_mmu_hr_free_hop_remove_pgt(pgt_info, hr_priv, hop_table_size);10191020return num_of_ptes_left;1021}10221023/**1024* hl_mmu_hr_get_pte() - increase PGT PTE count1025* @ctx: pointer to the context structure1026* @hr_func: host resident functions1027* @phys_hop_addr: HOP phys address1028*/1029void hl_mmu_hr_get_pte(struct hl_ctx *ctx, struct hl_hr_mmu_funcs *hr_func, u64 phys_hop_addr)1030{1031hr_func->get_pgt_info(ctx, phys_hop_addr)->num_of_ptes++;1032}10331034/**1035* hl_mmu_hr_get_next_hop_pgt_info() - get pgt_info structure for the next HOP1036* @ctx: pointer to the context structure.1037* @hr_func: host resident functions.1038* @curr_pte: current PTE value.1039*1040* @return pgt_info structure on success, otherwise NULL.1041*/1042struct pgt_info *hl_mmu_hr_get_next_hop_pgt_info(struct hl_ctx *ctx,1043struct hl_hr_mmu_funcs *hr_func,1044u64 curr_pte)1045{1046u64 next_hop_phys_addr = hl_mmu_get_next_hop_addr(ctx, curr_pte);10471048if (next_hop_phys_addr == ULLONG_MAX)1049return NULL;10501051return hr_func->get_pgt_info(ctx, next_hop_phys_addr);1052}10531054/**1055* hl_mmu_hr_alloc_hop() - allocate HOP1056* @ctx: pointer to the context structure.1057* @hr_priv: host resident private info structure.1058* @hr_func: host resident functions.1059* @mmu_prop: MMU properties.1060*1061* @return pgt_info structure associated with the allocated HOP on success, otherwise NULL.1062*/1063struct pgt_info *hl_mmu_hr_alloc_hop(struct hl_ctx *ctx, struct hl_mmu_hr_priv *hr_priv,1064struct hl_hr_mmu_funcs *hr_func,1065struct hl_mmu_properties *mmu_prop)1066{1067struct hl_device *hdev = ctx->hdev;1068struct pgt_info *pgt_info;1069dma_addr_t phys_addr;1070void *virt_addr;1071int i, retry = 1;10721073pgt_info = kmalloc(sizeof(*pgt_info), GFP_KERNEL);1074if (!pgt_info)1075return NULL;10761077for (i = 0; i <= retry; i++) {1078virt_addr = gen_pool_dma_zalloc_align(hr_priv->mmu_pgt_pool,1079mmu_prop->hop_table_size,1080&phys_addr,1081mmu_prop->hop_table_size);1082if (virt_addr)1083break;10841085/* No memory in pool - get some and try again */1086virt_addr = hl_asic_dma_alloc_coherent(hdev, SZ_2M, &phys_addr,1087GFP_KERNEL | __GFP_ZERO);1088if (ZERO_OR_NULL_PTR(virt_addr))1089break;10901091if (gen_pool_add_virt(hr_priv->mmu_pgt_pool, (unsigned long)virt_addr,1092phys_addr, SZ_2M, -1)) {1093hl_asic_dma_free_coherent(hdev, SZ_2M, virt_addr, phys_addr);1094virt_addr = NULL;1095break;1096}1097}10981099if (ZERO_OR_NULL_PTR(virt_addr)) {1100dev_err(hdev->dev, "failed to allocate page\n");1101goto pool_alloc_err;1102}11031104pgt_info->phys_addr = phys_addr;1105pgt_info->shadow_addr = (unsigned long) NULL;1106pgt_info->virt_addr = (unsigned long)virt_addr;1107pgt_info->ctx = ctx;1108pgt_info->num_of_ptes = 0;1109hr_func->add_pgt_info(ctx, pgt_info, phys_addr);11101111return pgt_info;11121113pool_alloc_err:1114kfree(pgt_info);11151116return NULL;1117}11181119/**1120* hl_mmu_hr_get_alloc_next_hop() - get the next HOP, allocate it if it does not exist1121* @ctx: pointer to the context structure.1122* @hr_priv: host resident private info structure.1123* @hr_func: host resident functions.1124* @mmu_prop: MMU properties.1125* @curr_pte: current PTE value.1126* @is_new_hop: set to true if HOP is new (caller responsibility to set it to false).1127*1128* @return pgt_info structure associated with the allocated HOP on success, otherwise NULL.1129*/1130struct pgt_info *hl_mmu_hr_get_alloc_next_hop(struct hl_ctx *ctx,1131struct hl_mmu_hr_priv *hr_priv,1132struct hl_hr_mmu_funcs *hr_func,1133struct hl_mmu_properties *mmu_prop,1134u64 curr_pte, bool *is_new_hop)1135{1136u64 hop_addr = hl_mmu_get_next_hop_addr(ctx, curr_pte);11371138if (hop_addr != ULLONG_MAX)1139return hr_func->get_pgt_info(ctx, hop_addr);11401141*is_new_hop = true;1142return hl_mmu_hr_alloc_hop(ctx, hr_priv, hr_func, mmu_prop);1143}11441145/**1146* hl_mmu_hr_get_tlb_info() - get the TLB info (info for a specific mapping)1147* @ctx: pointer to the context structure.1148* @virt_addr: the virt address for which to get info.1149* @hops: HOPs info structure.1150* @hr_func: host resident functions.1151*1152* @return 0 on success, otherwise non 0 error code..1153*/1154int hl_mmu_hr_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr, struct hl_mmu_hop_info *hops,1155struct hl_hr_mmu_funcs *hr_func)1156{1157/* using 6 HOPs as this is the maximum number of HOPs */1158struct pgt_info *hops_pgt_info[MMU_ARCH_6_HOPS] = { NULL };1159struct hl_device *hdev = ctx->hdev;1160struct hl_mmu_properties *mmu_prop;1161int rc, i, used_hops;1162bool is_huge;11631164rc = hr_func->get_tlb_mapping_params(hdev, &mmu_prop, hops, virt_addr, &is_huge);1165if (rc)1166return rc;11671168used_hops = mmu_prop->num_hops;11691170/* huge pages use one less hop */1171if (is_huge)1172used_hops--;11731174hops->scrambled_vaddr = hdev->asic_funcs->scramble_addr(hdev, virt_addr);11751176for (i = 0 ; i < used_hops ; i++) {1177if (i == 0)1178hops_pgt_info[i] = hr_func->get_hop0_pgt_info(ctx);1179else1180hops_pgt_info[i] = hl_mmu_hr_get_next_hop_pgt_info(ctx, hr_func,1181hops->hop_info[i - 1].hop_pte_val);11821183if (!hops_pgt_info[i])1184return -EFAULT;11851186hops->hop_info[i].hop_addr = hops_pgt_info[i]->phys_addr;1187hops->hop_info[i].hop_pte_addr =1188hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, i,1189hops->hop_info[i].hop_addr,1190hops->scrambled_vaddr);1191hops->hop_info[i].hop_pte_val = *(u64 *) (uintptr_t)1192hl_mmu_hr_pte_phys_to_virt(ctx, hops_pgt_info[i],1193hops->hop_info[i].hop_pte_addr,1194mmu_prop->hop_table_size);11951196if (!(hops->hop_info[i].hop_pte_val & PAGE_PRESENT_MASK))1197return -EFAULT;11981199if (hops->hop_info[i].hop_pte_val & mmu_prop->last_mask)1200break;1201}12021203/* if passed over all hops then no last hop was found */1204if (i == mmu_prop->num_hops)1205return -EFAULT;12061207if (hops->scrambled_vaddr != virt_addr)1208hops->unscrambled_paddr = hdev->asic_funcs->descramble_addr1209(hdev, hops->hop_info[i].hop_pte_val);1210else1211hops->unscrambled_paddr = hops->hop_info[i].hop_pte_val;12121213hops->used_hops = i + 1;12141215return 0;1216}12171218struct pgt_info *hl_mmu_dr_get_pgt_info(struct hl_ctx *ctx, u64 hop_addr)1219{1220struct pgt_info *pgt_info = NULL;12211222hash_for_each_possible(ctx->mmu_shadow_hash, pgt_info, node,1223(unsigned long) hop_addr)1224if (hop_addr == pgt_info->shadow_addr)1225break;12261227return pgt_info;1228}12291230void hl_mmu_dr_free_hop(struct hl_ctx *ctx, u64 hop_addr)1231{1232struct pgt_info *pgt_info = hl_mmu_dr_get_pgt_info(ctx, hop_addr);12331234hl_mmu_dr_free_pgt_node(ctx, pgt_info);1235}12361237void hl_mmu_dr_free_pgt_node(struct hl_ctx *ctx, struct pgt_info *pgt_info)1238{1239struct hl_device *hdev = ctx->hdev;12401241gen_pool_free(hdev->mmu_priv.dr.mmu_pgt_pool, pgt_info->phys_addr,1242hdev->asic_prop.dmmu.hop_table_size);1243hash_del(&pgt_info->node);1244kfree((u64 *) (uintptr_t) pgt_info->shadow_addr);1245kfree(pgt_info);1246}12471248u64 hl_mmu_dr_get_phys_hop0_addr(struct hl_ctx *ctx)1249{1250return ctx->hdev->asic_prop.mmu_pgt_addr +1251(ctx->asid * ctx->hdev->asic_prop.dmmu.hop_table_size);1252}12531254u64 hl_mmu_dr_get_hop0_addr(struct hl_ctx *ctx)1255{1256return (u64) (uintptr_t) ctx->hdev->mmu_priv.dr.mmu_shadow_hop0 +1257(ctx->asid * ctx->hdev->asic_prop.dmmu.hop_table_size);1258}12591260u64 hl_mmu_dr_get_phys_addr(struct hl_ctx *ctx, u64 shadow_addr)1261{1262u64 page_mask = ctx->hdev->asic_prop.dmmu.hop_table_size - 1;1263u64 shadow_hop_addr = shadow_addr & (~page_mask);1264u64 pte_offset = shadow_addr & page_mask;1265u64 phys_hop_addr;12661267if (shadow_hop_addr != hl_mmu_dr_get_hop0_addr(ctx))1268phys_hop_addr = hl_mmu_dr_get_pgt_info(ctx, shadow_hop_addr)->phys_addr;1269else1270phys_hop_addr = hl_mmu_dr_get_phys_hop0_addr(ctx);12711272return phys_hop_addr + pte_offset;1273}12741275void hl_mmu_dr_write_pte(struct hl_ctx *ctx, u64 shadow_pte_addr, u64 val)1276{1277u64 phys_val = hl_mmu_dr_get_phys_addr(ctx, val);12781279ctx->hdev->asic_funcs->write_pte(ctx->hdev, hl_mmu_dr_get_phys_addr(ctx, shadow_pte_addr),1280phys_val);12811282*(u64 *) (uintptr_t) shadow_pte_addr = val;1283}12841285void hl_mmu_dr_write_final_pte(struct hl_ctx *ctx, u64 shadow_pte_addr, u64 val)1286{1287ctx->hdev->asic_funcs->write_pte(ctx->hdev,1288hl_mmu_dr_get_phys_addr(ctx, shadow_pte_addr), val);1289*(u64 *) (uintptr_t) shadow_pte_addr = val;1290}12911292void hl_mmu_dr_clear_pte(struct hl_ctx *ctx, u64 pte_addr)1293{1294hl_mmu_dr_write_final_pte(ctx, pte_addr, 0);1295}12961297void hl_mmu_dr_get_pte(struct hl_ctx *ctx, u64 hop_addr)1298{1299hl_mmu_dr_get_pgt_info(ctx, hop_addr)->num_of_ptes++;1300}13011302int hl_mmu_dr_put_pte(struct hl_ctx *ctx, u64 hop_addr)1303{1304struct pgt_info *pgt_info = hl_mmu_dr_get_pgt_info(ctx, hop_addr);1305int num_of_ptes_left;13061307pgt_info->num_of_ptes--;13081309/*1310* Need to save the number of ptes left because hl_mmu_free_hop might free1311* the pgt_info1312*/1313num_of_ptes_left = pgt_info->num_of_ptes;1314if (!num_of_ptes_left)1315hl_mmu_dr_free_pgt_node(ctx, pgt_info);13161317return num_of_ptes_left;1318}13191320u64 hl_mmu_dr_alloc_hop(struct hl_ctx *ctx)1321{1322struct hl_device *hdev = ctx->hdev;1323struct asic_fixed_properties *prop = &hdev->asic_prop;1324struct pgt_info *pgt_info;1325u64 phys_addr, shadow_addr;13261327pgt_info = kmalloc(sizeof(*pgt_info), GFP_KERNEL);1328if (!pgt_info)1329return ULLONG_MAX;13301331phys_addr = (u64) gen_pool_alloc(hdev->mmu_priv.dr.mmu_pgt_pool,1332prop->dmmu.hop_table_size);1333if (!phys_addr) {1334dev_err(hdev->dev, "failed to allocate page\n");1335goto pool_add_err;1336}13371338shadow_addr = (u64) (uintptr_t) kzalloc(prop->dmmu.hop_table_size,1339GFP_KERNEL);1340if (!shadow_addr)1341goto shadow_err;13421343pgt_info->phys_addr = phys_addr;1344pgt_info->shadow_addr = shadow_addr;1345pgt_info->ctx = ctx;1346pgt_info->num_of_ptes = 0;1347hash_add(ctx->mmu_shadow_hash, &pgt_info->node, shadow_addr);13481349return shadow_addr;13501351shadow_err:1352gen_pool_free(hdev->mmu_priv.dr.mmu_pgt_pool,1353phys_addr, prop->dmmu.hop_table_size);1354pool_add_err:1355kfree(pgt_info);13561357return ULLONG_MAX;1358}13591360u64 hl_mmu_dr_get_alloc_next_hop_addr(struct hl_ctx *ctx, u64 curr_pte, bool *is_new_hop)1361{1362u64 hop_addr = hl_mmu_get_next_hop_addr(ctx, curr_pte);13631364if (hop_addr == ULLONG_MAX) {1365hop_addr = hl_mmu_dr_alloc_hop(ctx);1366*is_new_hop = (hop_addr != ULLONG_MAX);1367}13681369return hop_addr;1370}13711372void hl_mmu_dr_flush(struct hl_ctx *ctx)1373{1374/* flush all writes from all cores to reach PCI */1375mb();1376ctx->hdev->asic_funcs->read_pte(ctx->hdev, hl_mmu_dr_get_phys_hop0_addr(ctx));1377}13781379int hl_mmu_dr_init(struct hl_device *hdev)1380{1381struct asic_fixed_properties *prop = &hdev->asic_prop;1382int rc;13831384hdev->mmu_priv.dr.mmu_pgt_pool =1385gen_pool_create(__ffs(prop->dmmu.hop_table_size), -1);13861387if (!hdev->mmu_priv.dr.mmu_pgt_pool) {1388dev_err(hdev->dev, "Failed to create page gen pool\n");1389return -ENOMEM;1390}13911392rc = gen_pool_add(hdev->mmu_priv.dr.mmu_pgt_pool, prop->mmu_pgt_addr +1393prop->dmmu.hop0_tables_total_size,1394prop->dmmu.pgt_size - prop->dmmu.hop0_tables_total_size,1395-1);1396if (rc) {1397dev_err(hdev->dev, "Failed to add memory to page gen pool\n");1398goto err_pool_add;1399}14001401hdev->mmu_priv.dr.mmu_shadow_hop0 = kvcalloc(prop->max_asid,1402prop->dmmu.hop_table_size, GFP_KERNEL);1403if (ZERO_OR_NULL_PTR(hdev->mmu_priv.dr.mmu_shadow_hop0)) {1404rc = -ENOMEM;1405goto err_pool_add;1406}14071408/* MMU H/W init will be done in device hw_init() */14091410return 0;14111412err_pool_add:1413gen_pool_destroy(hdev->mmu_priv.dr.mmu_pgt_pool);14141415return rc;1416}14171418void hl_mmu_dr_fini(struct hl_device *hdev)1419{1420/* MMU H/W fini was already done in device hw_fini() */14211422if (ZERO_OR_NULL_PTR(hdev->mmu_priv.dr.mmu_shadow_hop0))1423return;14241425kvfree(hdev->mmu_priv.dr.mmu_shadow_hop0);1426gen_pool_destroy(hdev->mmu_priv.dr.mmu_pgt_pool);14271428/* Make sure that if we arrive here again without init was1429* called we won't cause kernel panic. This can happen for1430* example if we fail during hard reset code at certain points1431*/1432hdev->mmu_priv.dr.mmu_shadow_hop0 = NULL;1433}143414351436