Path: blob/master/drivers/accel/habanalabs/common/mmu/mmu_v2.c
26488 views
// SPDX-License-Identifier: GPL-2.012/*3* Copyright 2016-2020 HabanaLabs, Ltd.4* All Rights Reserved.5*/67#include "../habanalabs.h"8#include "../../include/hw_ip/mmu/mmu_general.h"9#include "../../include/hw_ip/mmu/mmu_v2_0.h"1011#include <linux/slab.h>1213/**14* hl_mmu_v2_ctx_init() - initialize a context for using the MMU module.15* @ctx: pointer to the context structure to initialize.16*17* Initialize a mutex to protect the concurrent mapping flow, a hash to hold all18* page tables hops related to this context.19* Return: 0 on success, non-zero otherwise.20*/21static int hl_mmu_v2_ctx_init(struct hl_ctx *ctx)22{23hash_init(ctx->mmu_shadow_hash);2425return 0;26}2728/*29* hl_mmu_v2_ctx_fini - disable a ctx from using the mmu module30*31* @ctx: pointer to the context structure32*33* This function does the following:34* - Free any pgts which were not freed yet35* - Free the mutex36* - Free DRAM default page mapping hops37*/38static void hl_mmu_v2_ctx_fini(struct hl_ctx *ctx)39{40struct hl_device *hdev = ctx->hdev;41struct pgt_info *pgt_info;42struct hlist_node *tmp;43int i;4445if (!hash_empty(ctx->mmu_shadow_hash))46dev_err(hdev->dev, "ctx %d is freed while it has pgts in use\n",47ctx->asid);4849hash_for_each_safe(ctx->mmu_shadow_hash, i, tmp, pgt_info, node) {50dev_err_ratelimited(hdev->dev,51"pgt_info of addr 0x%llx of asid %d was not destroyed, num_ptes: %d\n",52pgt_info->phys_addr, ctx->asid, pgt_info->num_of_ptes);53hl_mmu_dr_free_pgt_node(ctx, pgt_info);54}55}5657static int hl_mmu_v2_unmap(struct hl_ctx *ctx, u64 virt_addr, bool is_dram_addr)58{59u64 hop_addr[MMU_ARCH_6_HOPS] = { 0 }, hop_pte_addr[MMU_ARCH_6_HOPS] = { 0 }, curr_pte,60scrambled_virt_addr;61struct asic_fixed_properties *prop = &ctx->hdev->asic_prop;62struct hl_device *hdev = ctx->hdev;63struct hl_mmu_properties *mmu_prop;64bool is_huge = false;65int i, hop_last;6667/* device resident in V2 are allowed only for HMMU */68if (!is_dram_addr)69return -EINVAL;7071mmu_prop = &prop->dmmu;7273hop_last = mmu_prop->num_hops - 1;7475scrambled_virt_addr = hdev->asic_funcs->scramble_addr(hdev, virt_addr);7677hop_addr[0] = hl_mmu_dr_get_hop0_addr(ctx);78hop_pte_addr[0] = hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, 0,79hop_addr[0], scrambled_virt_addr);80if (hop_pte_addr[0] == U64_MAX)81return -EFAULT;8283curr_pte = *(u64 *) (uintptr_t) hop_pte_addr[0];8485for (i = 1 ; i < mmu_prop->num_hops ; i++) {86hop_addr[i] = hl_mmu_get_next_hop_addr(ctx, curr_pte);87if (hop_addr[i] == ULLONG_MAX)88goto not_mapped;8990hop_pte_addr[i] = hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, i,91hop_addr[i], scrambled_virt_addr);92if (hop_pte_addr[i] == U64_MAX)93return -EFAULT;9495curr_pte = *(u64 *) (uintptr_t) hop_pte_addr[i];9697if ((i <= hop_last) && (curr_pte & mmu_prop->last_mask)) {98hop_last = i;99is_huge = true;100break;101}102}103104if (is_dram_addr && !is_huge) {105dev_err(hdev->dev, "DRAM unmapping should use huge pages only\n");106return -EFAULT;107}108109if (!(curr_pte & PAGE_PRESENT_MASK))110goto not_mapped;111112for (i = hop_last ; i > 0 ; i--) {113hl_mmu_dr_clear_pte(ctx, hop_pte_addr[i]);114if (hl_mmu_dr_put_pte(ctx, hop_addr[i]))115goto mapped;116}117hl_mmu_dr_clear_pte(ctx, hop_pte_addr[0]);118119mapped:120return 0;121122not_mapped:123dev_err(hdev->dev, "virt addr 0x%llx is not mapped to phys addr\n",124virt_addr);125126return -EINVAL;127}128129static int hl_mmu_v2_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr,130u32 page_size, bool is_dram_addr)131{132u64 hop_addr[MMU_ARCH_6_HOPS] = { 0 }, hop_pte_addr[MMU_ARCH_6_HOPS] = { 0 },133curr_pte = 0, scrambled_virt_addr, scrambled_phys_addr;134struct asic_fixed_properties *prop = &ctx->hdev->asic_prop;135bool hop_new[MMU_ARCH_6_HOPS] = { false };136struct hl_device *hdev = ctx->hdev;137struct hl_mmu_properties *mmu_prop;138int rc, i, hop_last;139140/* device resident in V2 are allowed only for HMMU */141if (!is_dram_addr)142return -EINVAL;143144mmu_prop = &prop->dmmu;145146hop_last = mmu_prop->num_hops - 1;147148scrambled_virt_addr = hdev->asic_funcs->scramble_addr(hdev, virt_addr);149scrambled_phys_addr = hdev->asic_funcs->scramble_addr(hdev, phys_addr);150151/* First hop is preallocated therefore it is treated differently */152hop_addr[0] = hl_mmu_dr_get_hop0_addr(ctx);153hop_pte_addr[0] = hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, 0,154hop_addr[0], scrambled_virt_addr);155curr_pte = *(u64 *) (uintptr_t) hop_pte_addr[0];156157/* Handle hop1 to hop_last */158for (i = 1 ; i <= hop_last ; i++) {159hop_addr[i] = hl_mmu_dr_get_alloc_next_hop_addr(ctx, curr_pte, &hop_new[i]);160if (hop_addr[i] == ULLONG_MAX) {161rc = -ENOMEM;162goto err;163}164165hop_pte_addr[i] = hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, i,166hop_addr[i], scrambled_virt_addr);167if (hop_pte_addr[i] == U64_MAX) {168rc = -EINVAL;169goto err;170}171172if (!hop_pte_addr[i]) {173rc = -EINVAL;174goto err;175}176177curr_pte = *(u64 *) (uintptr_t) hop_pte_addr[i];178}179180if (curr_pte & PAGE_PRESENT_MASK) {181dev_err(hdev->dev,182"mapping already exists for virt_addr 0x%llx\n",183virt_addr);184185for (i = 0 ; i <= hop_last ; i++)186dev_dbg(hdev->dev, "hop%d pte: 0x%llx (0x%llx)\n",187i, *(u64 *) (uintptr_t) hop_pte_addr[i],188hop_pte_addr[i]);189190rc = -EINVAL;191goto err;192}193194curr_pte = (scrambled_phys_addr & HOP_PHYS_ADDR_MASK)195| mmu_prop->last_mask | PAGE_PRESENT_MASK;196197/* Write the PTEs */198hl_mmu_dr_write_final_pte(ctx, hop_pte_addr[hop_last], curr_pte);199200/* for each new hop, add its address to the table of previous-hop */201for (i = 1 ; i <= hop_last ; i++) {202if (hop_new[i]) {203curr_pte = (hop_addr[i] & HOP_PHYS_ADDR_MASK) | PAGE_PRESENT_MASK;204hl_mmu_dr_write_pte(ctx, hop_pte_addr[i - 1], curr_pte);205206if (i - 1)207hl_mmu_dr_get_pte(ctx, hop_addr[i - 1]);208}209}210hl_mmu_dr_get_pte(ctx, hop_addr[hop_last]);211212return 0;213214err:215for (i = 1 ; i <= hop_last ; i++)216if (hop_new[i] && (hop_addr[i] != U64_MAX))217hl_mmu_dr_free_hop(ctx, hop_addr[i]);218219return rc;220}221222/*223* hl_mmu_v2_swap_out - marks all mapping of the given ctx as swapped out224*225* @ctx: pointer to the context structure226*227*/228static void hl_mmu_v2_swap_out(struct hl_ctx *ctx)229{230231}232233/*234* hl_mmu_v2_swap_in - marks all mapping of the given ctx as swapped in235*236* @ctx: pointer to the context structure237*238*/239static void hl_mmu_v2_swap_in(struct hl_ctx *ctx)240{241242}243244static int hl_mmu_v2_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr, struct hl_mmu_hop_info *hops)245{246struct asic_fixed_properties *prop = &ctx->hdev->asic_prop;247struct hl_device *hdev = ctx->hdev;248struct hl_mmu_properties *mmu_prop;249bool is_dram_addr;250int i;251252is_dram_addr = hl_mem_area_inside_range(virt_addr, prop->dmmu.page_size,253prop->dmmu.start_addr,254prop->dmmu.end_addr);255256/* device resident in V2 are allowed only for HMMU */257if (!is_dram_addr)258return -EINVAL;259260mmu_prop = &prop->dmmu;261hops->range_type = HL_VA_RANGE_TYPE_DRAM;262263hops->scrambled_vaddr = hdev->asic_funcs->scramble_addr(hdev, virt_addr);264265hops->hop_info[0].hop_addr = hl_mmu_dr_get_phys_hop0_addr(ctx);266hops->hop_info[0].hop_pte_addr = hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, 0,267hops->hop_info[0].hop_addr,268hops->scrambled_vaddr);269if (hops->hop_info[0].hop_pte_addr == U64_MAX)270return -EFAULT;271272hops->hop_info[0].hop_pte_val = hdev->asic_funcs->read_pte(hdev,273hops->hop_info[0].hop_pte_addr);274if (hops->hop_info[0].hop_pte_val == U64_MAX)275return -EFAULT;276277for (i = 1 ; i < mmu_prop->num_hops ; i++) {278hops->hop_info[i].hop_addr =279hl_mmu_get_next_hop_addr(ctx, hops->hop_info[i - 1].hop_pte_val);280if (hops->hop_info[i].hop_addr == ULLONG_MAX)281return -EFAULT;282283hops->hop_info[i].hop_pte_addr =284hl_mmu_get_hop_pte_phys_addr(ctx, mmu_prop, i,285hops->hop_info[i].hop_addr,286hops->scrambled_vaddr);287if (hops->hop_info[i].hop_pte_addr == U64_MAX)288return -EFAULT;289290hops->hop_info[i].hop_pte_val =291hdev->asic_funcs->read_pte(hdev,292hops->hop_info[i].hop_pte_addr);293294if (!(hops->hop_info[i].hop_pte_val & PAGE_PRESENT_MASK))295return -EFAULT;296297if (hops->hop_info[i].hop_pte_val & mmu_prop->last_mask)298break;299}300301/* if passed over all hops then no last hop was found */302if (i == mmu_prop->num_hops)303return -EFAULT;304305if (!(hops->hop_info[i].hop_pte_val & PAGE_PRESENT_MASK))306return -EFAULT;307308if (hops->scrambled_vaddr != virt_addr)309hops->unscrambled_paddr = hdev->asic_funcs->descramble_addr310(hdev, hops->hop_info[i].hop_pte_val);311else312hops->unscrambled_paddr = hops->hop_info[i].hop_pte_val;313314hops->used_hops = i + 1;315316return 0;317}318319/*320* hl_mmu_v2_prepare - prepare mmu_if for working with mmu v2321*322* @hdev: pointer to the device structure323* @mmu_if: pointer to the mmu interface structure324*/325void hl_mmu_v2_set_funcs(struct hl_device *hdev, struct hl_mmu_funcs *mmu)326{327mmu->init = hl_mmu_dr_init;328mmu->fini = hl_mmu_dr_fini;329mmu->ctx_init = hl_mmu_v2_ctx_init;330mmu->ctx_fini = hl_mmu_v2_ctx_fini;331mmu->map = hl_mmu_v2_map;332mmu->unmap = hl_mmu_v2_unmap;333mmu->flush = hl_mmu_dr_flush;334mmu->swap_out = hl_mmu_v2_swap_out;335mmu->swap_in = hl_mmu_v2_swap_in;336mmu->get_tlb_info = hl_mmu_v2_get_tlb_info;337}338339340