From nobody Thu Dec 18 19:31:19 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=linaro.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1525455048764277.24382309582984; Fri, 4 May 2018 10:30:48 -0700 (PDT) Received: from localhost ([::1]:35693 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fEeXf-0002Jz-VY for importer@patchew.org; Fri, 04 May 2018 13:30:48 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:51793) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1fEeJG-0005jn-3R for qemu-devel@nongnu.org; Fri, 04 May 2018 13:15:56 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1fEeJD-0000em-V1 for qemu-devel@nongnu.org; Fri, 04 May 2018 13:15:54 -0400 Received: from orth.archaic.org.uk ([2001:8b0:1d0::2]:41488) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1fEeJD-0000cH-JE for qemu-devel@nongnu.org; Fri, 04 May 2018 13:15:51 -0400 Received: from pm215 by orth.archaic.org.uk with local (Exim 4.89) (envelope-from ) id 1fEeJC-000353-Ik for qemu-devel@nongnu.org; Fri, 04 May 2018 18:15:50 +0100 From: Peter Maydell To: qemu-devel@nongnu.org Date: Fri, 4 May 2018 18:15:29 +0100 Message-Id: <20180504171540.25813-14-peter.maydell@linaro.org> X-Mailer: git-send-email 2.17.0 In-Reply-To: <20180504171540.25813-1-peter.maydell@linaro.org> References: <20180504171540.25813-1-peter.maydell@linaro.org> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2001:8b0:1d0::2 Subject: [Qemu-devel] [PULL 13/24] hw/arm/smmu-common: VMSAv8-64 page table walk X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: Eric Auger This patch implements the page table walk for VMSAv8-64. Signed-off-by: Eric Auger Signed-off-by: Prem Mallappa Message-id: 1524665762-31355-4-git-send-email-eric.auger@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/smmu-internal.h | 99 ++++++++++++++++ include/hw/arm/smmu-common.h | 14 +++ hw/arm/smmu-common.c | 222 +++++++++++++++++++++++++++++++++++ hw/arm/trace-events | 9 +- 4 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 hw/arm/smmu-internal.h diff --git a/hw/arm/smmu-internal.h b/hw/arm/smmu-internal.h new file mode 100644 index 0000000000..7794d6d394 --- /dev/null +++ b/hw/arm/smmu-internal.h @@ -0,0 +1,99 @@ +/* + * ARM SMMU support - Internal API + * + * Copyright (c) 2017 Red Hat, Inc. + * Copyright (C) 2014-2016 Broadcom Corporation + * Written by Prem Mallappa, Eric Auger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef HW_ARM_SMMU_INTERNAL_H +#define HW_ARM_SMMU_INTERNAL_H + +#define TBI0(tbi) ((tbi) & 0x1) +#define TBI1(tbi) ((tbi) & 0x2 >> 1) + +/* PTE Manipulation */ + +#define ARM_LPAE_PTE_TYPE_SHIFT 0 +#define ARM_LPAE_PTE_TYPE_MASK 0x3 + +#define ARM_LPAE_PTE_TYPE_BLOCK 1 +#define ARM_LPAE_PTE_TYPE_TABLE 3 + +#define ARM_LPAE_L3_PTE_TYPE_RESERVED 1 +#define ARM_LPAE_L3_PTE_TYPE_PAGE 3 + +#define ARM_LPAE_PTE_VALID (1 << 0) + +#define PTE_ADDRESS(pte, shift) \ + (extract64(pte, shift, 47 - shift + 1) << shift) + +#define is_invalid_pte(pte) (!(pte & ARM_LPAE_PTE_VALID)) + +#define is_reserved_pte(pte, level) \ + ((level =3D=3D 3) && = \ + ((pte & ARM_LPAE_PTE_TYPE_MASK) =3D=3D ARM_LPAE_L3_PTE_TYPE_RESERVED)) + +#define is_block_pte(pte, level) \ + ((level < 3) && \ + ((pte & ARM_LPAE_PTE_TYPE_MASK) =3D=3D ARM_LPAE_PTE_TYPE_BLOCK)) + +#define is_table_pte(pte, level) \ + ((level < 3) && \ + ((pte & ARM_LPAE_PTE_TYPE_MASK) =3D=3D ARM_LPAE_PTE_TYPE_TABLE)) + +#define is_page_pte(pte, level) \ + ((level =3D=3D 3) && = \ + ((pte & ARM_LPAE_PTE_TYPE_MASK) =3D=3D ARM_LPAE_L3_PTE_TYPE_PAGE)) + +/* access permissions */ + +#define PTE_AP(pte) \ + (extract64(pte, 6, 2)) + +#define PTE_APTABLE(pte) \ + (extract64(pte, 61, 2)) + +/* + * TODO: At the moment all transactions are considered as privileged (EL1) + * as IOMMU translation callback does not pass user/priv attributes. + */ +#define is_permission_fault(ap, perm) \ + (((perm) & IOMMU_WO) && ((ap) & 0x2)) + +#define PTE_AP_TO_PERM(ap) \ + (IOMMU_ACCESS_FLAG(true, !((ap) & 0x2))) + +/* Level Indexing */ + +static inline int level_shift(int level, int granule_sz) +{ + return granule_sz + (3 - level) * (granule_sz - 3); +} + +static inline uint64_t level_page_mask(int level, int granule_sz) +{ + return ~(MAKE_64BIT_MASK(0, level_shift(level, granule_sz))); +} + +static inline +uint64_t iova_level_offset(uint64_t iova, int inputsize, + int level, int gsz) +{ + return ((iova & MAKE_64BIT_MASK(0, inputsize)) >> level_shift(level, g= sz)) & + MAKE_64BIT_MASK(0, gsz - 3); +} + +#endif diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index 8b947774b0..c41eb5c3b0 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -128,4 +128,18 @@ static inline uint16_t smmu_get_sid(SMMUDevice *sdev) { return PCI_BUILD_BDF(pci_bus_num(sdev->bus), sdev->devfn); } + +/** + * smmu_ptw - Perform the page table walk for a given iova / access flags + * pair, according to @cfg translation config + */ +int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags perm, + IOMMUTLBEntry *tlbe, SMMUPTWEventInfo *info); + +/** + * select_tt - compute which translation table shall be used according to + * the input iova and translation config and return the TT specific info + */ +SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova); + #endif /* HW_ARM_SMMU_COMMON */ diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 3d64bcfac2..01c7be82b6 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -27,6 +27,228 @@ =20 #include "qemu/error-report.h" #include "hw/arm/smmu-common.h" +#include "smmu-internal.h" + +/* VMSAv8-64 Translation */ + +/** + * get_pte - Get the content of a page table entry located at + * @base_addr[@index] + */ +static int get_pte(dma_addr_t baseaddr, uint32_t index, uint64_t *pte, + SMMUPTWEventInfo *info) +{ + int ret; + dma_addr_t addr =3D baseaddr + index * sizeof(*pte); + + /* TODO: guarantee 64-bit single-copy atomicity */ + ret =3D dma_memory_read(&address_space_memory, addr, + (uint8_t *)pte, sizeof(*pte)); + + if (ret !=3D MEMTX_OK) { + info->type =3D SMMU_PTW_ERR_WALK_EABT; + info->addr =3D addr; + return -EINVAL; + } + trace_smmu_get_pte(baseaddr, index, addr, *pte); + return 0; +} + +/* VMSAv8-64 Translation Table Format Descriptor Decoding */ + +/** + * get_page_pte_address - returns the L3 descriptor output address, + * ie. the page frame + * ARM ARM spec: Figure D4-17 VMSAv8-64 level 3 descriptor format + */ +static inline hwaddr get_page_pte_address(uint64_t pte, int granule_sz) +{ + return PTE_ADDRESS(pte, granule_sz); +} + +/** + * get_table_pte_address - return table descriptor output address, + * ie. address of next level table + * ARM ARM Figure D4-16 VMSAv8-64 level0, level1, and level 2 descriptor f= ormats + */ +static inline hwaddr get_table_pte_address(uint64_t pte, int granule_sz) +{ + return PTE_ADDRESS(pte, granule_sz); +} + +/** + * get_block_pte_address - return block descriptor output address and bloc= k size + * ARM ARM Figure D4-16 VMSAv8-64 level0, level1, and level 2 descriptor f= ormats + */ +static inline hwaddr get_block_pte_address(uint64_t pte, int level, + int granule_sz, uint64_t *bsz) +{ + int n =3D (granule_sz - 3) * (4 - level) + 3; + + *bsz =3D 1 << n; + return PTE_ADDRESS(pte, n); +} + +SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova) +{ + bool tbi =3D extract64(iova, 55, 1) ? TBI1(cfg->tbi) : TBI0(cfg->tbi); + uint8_t tbi_byte =3D tbi * 8; + + if (cfg->tt[0].tsz && + !extract64(iova, 64 - cfg->tt[0].tsz, cfg->tt[0].tsz - tbi_byte)) { + /* there is a ttbr0 region and we are in it (high bits all zero) */ + return &cfg->tt[0]; + } else if (cfg->tt[1].tsz && + !extract64(iova, 64 - cfg->tt[1].tsz, cfg->tt[1].tsz - tbi_byte= )) { + /* there is a ttbr1 region and we are in it (high bits all one) */ + return &cfg->tt[1]; + } else if (!cfg->tt[0].tsz) { + /* ttbr0 region is "everything not in the ttbr1 region" */ + return &cfg->tt[0]; + } else if (!cfg->tt[1].tsz) { + /* ttbr1 region is "everything not in the ttbr0 region" */ + return &cfg->tt[1]; + } + /* in the gap between the two regions, this is a Translation fault */ + return NULL; +} + +/** + * smmu_ptw_64 - VMSAv8-64 Walk of the page tables for a given IOVA + * @cfg: translation config + * @iova: iova to translate + * @perm: access type + * @tlbe: IOMMUTLBEntry (out) + * @info: handle to an error info + * + * Return 0 on success, < 0 on error. In case of error, @info is filled + * and tlbe->perm is set to IOMMU_NONE. + * Upon success, @tlbe is filled with translated_addr and entry + * permission rights. + */ +static int smmu_ptw_64(SMMUTransCfg *cfg, + dma_addr_t iova, IOMMUAccessFlags perm, + IOMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) +{ + dma_addr_t baseaddr, indexmask; + int stage =3D cfg->stage; + SMMUTransTableInfo *tt =3D select_tt(cfg, iova); + uint8_t level, granule_sz, inputsize, stride; + + if (!tt || tt->disabled) { + info->type =3D SMMU_PTW_ERR_TRANSLATION; + goto error; + } + + granule_sz =3D tt->granule_sz; + stride =3D granule_sz - 3; + inputsize =3D 64 - tt->tsz; + level =3D 4 - (inputsize - 4) / stride; + indexmask =3D (1ULL << (inputsize - (stride * (4 - level)))) - 1; + baseaddr =3D extract64(tt->ttb, 0, 48); + baseaddr &=3D ~indexmask; + + tlbe->iova =3D iova; + tlbe->addr_mask =3D (1 << granule_sz) - 1; + + while (level <=3D 3) { + uint64_t subpage_size =3D 1ULL << level_shift(level, granule_sz); + uint64_t mask =3D subpage_size - 1; + uint32_t offset =3D iova_level_offset(iova, inputsize, level, gran= ule_sz); + uint64_t pte; + dma_addr_t pte_addr =3D baseaddr + offset * sizeof(pte); + uint8_t ap; + + if (get_pte(baseaddr, offset, &pte, info)) { + goto error; + } + trace_smmu_ptw_level(level, iova, subpage_size, + baseaddr, offset, pte); + + if (is_invalid_pte(pte) || is_reserved_pte(pte, level)) { + trace_smmu_ptw_invalid_pte(stage, level, baseaddr, + pte_addr, offset, pte); + info->type =3D SMMU_PTW_ERR_TRANSLATION; + goto error; + } + + if (is_page_pte(pte, level)) { + uint64_t gpa =3D get_page_pte_address(pte, granule_sz); + + ap =3D PTE_AP(pte); + if (is_permission_fault(ap, perm)) { + info->type =3D SMMU_PTW_ERR_PERMISSION; + goto error; + } + + tlbe->translated_addr =3D gpa + (iova & mask); + tlbe->perm =3D PTE_AP_TO_PERM(ap); + trace_smmu_ptw_page_pte(stage, level, iova, + baseaddr, pte_addr, pte, gpa); + return 0; + } + if (is_block_pte(pte, level)) { + uint64_t block_size; + hwaddr gpa =3D get_block_pte_address(pte, level, granule_sz, + &block_size); + + ap =3D PTE_AP(pte); + if (is_permission_fault(ap, perm)) { + info->type =3D SMMU_PTW_ERR_PERMISSION; + goto error; + } + + trace_smmu_ptw_block_pte(stage, level, baseaddr, + pte_addr, pte, iova, gpa, + block_size >> 20); + + tlbe->translated_addr =3D gpa + (iova & mask); + tlbe->perm =3D PTE_AP_TO_PERM(ap); + return 0; + } + + /* table pte */ + ap =3D PTE_APTABLE(pte); + + if (is_permission_fault(ap, perm)) { + info->type =3D SMMU_PTW_ERR_PERMISSION; + goto error; + } + baseaddr =3D get_table_pte_address(pte, granule_sz); + level++; + } + + info->type =3D SMMU_PTW_ERR_TRANSLATION; + +error: + tlbe->perm =3D IOMMU_NONE; + return -EINVAL; +} + +/** + * smmu_ptw - Walk the page tables for an IOVA, according to @cfg + * + * @cfg: translation configuration + * @iova: iova to translate + * @perm: tentative access type + * @tlbe: returned entry + * @info: ptw event handle + * + * return 0 on success + */ +inline int smmu_ptw(SMMUTransCfg *cfg, dma_addr_t iova, IOMMUAccessFlags p= erm, + IOMMUTLBEntry *tlbe, SMMUPTWEventInfo *info) +{ + if (!cfg->aa64) { + /* + * This code path is not entered as we check this while decoding + * the configuration data in the derived SMMU model. + */ + g_assert_not_reached(); + } + + return smmu_ptw_64(cfg, iova, perm, tlbe, info); +} =20 /** * The bus number is used for lookup when SID based invalidation occurs. diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 8e8b53c95d..524964a9e6 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -4,4 +4,11 @@ virt_acpi_setup(void) "No fw cfg or ACPI disabled. Bailing out." =20 # hw/arm/smmu-common.c -smmu_add_mr(const char *name) "%s" \ No newline at end of file +smmu_add_mr(const char *name) "%s" +smmu_page_walk(int stage, uint64_t baseaddr, int first_level, uint64_t sta= rt, uint64_t end) "stage=3D%d, baseaddr=3D0x%"PRIx64", first level=3D%d, st= art=3D0x%"PRIx64", end=3D0x%"PRIx64 +smmu_lookup_table(int level, uint64_t baseaddr, int granule_sz, uint64_t s= tart, uint64_t end, int flags, uint64_t subpage_size) "level=3D%d baseaddr= =3D0x%"PRIx64" granule=3D%d, start=3D0x%"PRIx64" end=3D0x%"PRIx64" flags=3D= %d subpage_size=3D0x%"PRIx64 +smmu_ptw_level(int level, uint64_t iova, size_t subpage_size, uint64_t bas= eaddr, uint32_t offset, uint64_t pte) "level=3D%d iova=3D0x%"PRIx64" subpag= e_sz=3D0x%lx baseaddr=3D0x%"PRIx64" offset=3D%d =3D> pte=3D0x%"PRIx64 +smmu_ptw_invalid_pte(int stage, int level, uint64_t baseaddr, uint64_t pte= addr, uint32_t offset, uint64_t pte) "stage=3D%d level=3D%d base@=3D0x%"PRI= x64" pte@=3D0x%"PRIx64" offset=3D%d pte=3D0x%"PRIx64 +smmu_ptw_page_pte(int stage, int level, uint64_t iova, uint64_t baseaddr,= uint64_t pteaddr, uint64_t pte, uint64_t address) "stage=3D%d level=3D%d i= ova=3D0x%"PRIx64" base@=3D0x%"PRIx64" pte@=3D0x%"PRIx64" pte=3D0x%"PRIx64" = page address =3D 0x%"PRIx64 +smmu_ptw_block_pte(int stage, int level, uint64_t baseaddr, uint64_t ptead= dr, uint64_t pte, uint64_t iova, uint64_t gpa, int bsize_mb) "stage=3D%d le= vel=3D%d base@=3D0x%"PRIx64" pte@=3D0x%"PRIx64" pte=3D0x%"PRIx64" iova=3D0x= %"PRIx64" block address =3D 0x%"PRIx64" block size =3D %d MiB" +smmu_get_pte(uint64_t baseaddr, int index, uint64_t pteaddr, uint64_t pte)= "baseaddr=3D0x%"PRIx64" index=3D0x%x, pteaddr=3D0x%"PRIx64", pte=3D0x%"PRI= x64 --=20 2.17.0