From nobody Tue Feb 10 04:12:43 2026 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.zoho.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; Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1494697656860879.6361921676285; Sat, 13 May 2017 10:47:36 -0700 (PDT) Received: from localhost ([::1]:58435 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1d9b8h-0002XU-Bn for importer@patchew.org; Sat, 13 May 2017 13:47:35 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:57539) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1d9b4y-0008E6-Sr for qemu-devel@nongnu.org; Sat, 13 May 2017 13:43:47 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1d9b4v-0003qT-Mn for qemu-devel@nongnu.org; Sat, 13 May 2017 13:43:44 -0400 Received: from mx1.redhat.com ([209.132.183.28]:59580) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1d9b4p-0003m1-GY; Sat, 13 May 2017 13:43:35 -0400 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 44CE1A0904; Sat, 13 May 2017 17:43:34 +0000 (UTC) Received: from localhost.localdomain.com (ovpn-116-214.ams2.redhat.com [10.36.116.214]) by smtp.corp.redhat.com (Postfix) with ESMTP id 9030417971; Sat, 13 May 2017 17:43:30 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 44CE1A0904 Authentication-Results: ext-mx10.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx10.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=eric.auger@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com 44CE1A0904 From: Eric Auger To: eric.auger.pro@gmail.com, eric.auger@redhat.com, peter.maydell@linaro.org, edgar.iglesias@gmail.com, qemu-arm@nongnu.org, qemu-devel@nongnu.org, prem.mallappa@gmail.com Date: Sat, 13 May 2017 19:43:15 +0200 Message-Id: <1494697399-352-2-git-send-email-eric.auger@redhat.com> In-Reply-To: <1494697399-352-1-git-send-email-eric.auger@redhat.com> References: <1494697399-352-1-git-send-email-eric.auger@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.39]); Sat, 13 May 2017 17:43:34 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] [fuzzy] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [RFC v4 1/5] hw/arm/smmu-common: smmu base class 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: , Cc: mohun106@gmail.com, drjones@redhat.com, tcain@qti.qualcomm.com, Radha.Chintakuntla@cavium.com, Sunil.Goutham@cavium.com, christoffer.dall@linaro.org 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" Introduces the base device and class for the ARM smmu. Implements VMSAv8-64 table lookup and translation. VMSAv8-32 is not yet implemented. For VFIO integration we will need to notify mapping changes of an input range and skipped unmapped regions. table walk helper allows. Signed-off-by: Eric Auger Signed-off-by: Prem Mallappa --- v3 -> v4: - reworked page table walk to prepare for VFIO integration (capability to scan a range of IOVA). Same function is used for translate for a single iova. This is largely inspired from intel_iommu.c - as the translate function was not straightforward to me, I tried to stick more closely to the VMSA spec. - remove support of nested stage (kernel driver does not support it anyway) - introduce smmu-internal.h to put page table definitions - added smmu_find_as_from_bus_num - SMMU_PCI_BUS_MAX and SMMU_PCI_DEVFN_MAX in smmu-common header - new fields in SMMUState: - iommu_ops, smmu_as_by_busptr, smmu_as_by_bus_num - use error_report and trace events - add aa64[] field in SMMUTransCfg v3: - moved the base code in a separate patch to ease the review. - clearer separation between base class and smmuv3 class - translate_* only implemented as class methods --- default-configs/aarch64-softmmu.mak | 1 + hw/arm/Makefile.objs | 1 + hw/arm/smmu-common.c | 419 ++++++++++++++++++++++++++++++++= ++++ hw/arm/smmu-internal.h | 97 +++++++++ hw/arm/trace-events | 12 ++ include/hw/arm/smmu-common.h | 125 +++++++++++ 6 files changed, 655 insertions(+) create mode 100644 hw/arm/smmu-common.c create mode 100644 hw/arm/smmu-internal.h create mode 100644 include/hw/arm/smmu-common.h diff --git a/default-configs/aarch64-softmmu.mak b/default-configs/aarch64-= softmmu.mak index 2449483..83a2932 100644 --- a/default-configs/aarch64-softmmu.mak +++ b/default-configs/aarch64-softmmu.mak @@ -7,3 +7,4 @@ CONFIG_AUX=3Dy CONFIG_DDC=3Dy CONFIG_DPCD=3Dy CONFIG_XLNX_ZYNQMP=3Dy +CONFIG_ARM_SMMUV3=3Dy diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 4c5c4ee..6c7d4af 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -18,3 +18,4 @@ obj-$(CONFIG_FSL_IMX25) +=3D fsl-imx25.o imx25_pdk.o obj-$(CONFIG_FSL_IMX31) +=3D fsl-imx31.o kzm.o obj-$(CONFIG_FSL_IMX6) +=3D fsl-imx6.o sabrelite.o obj-$(CONFIG_ASPEED_SOC) +=3D aspeed_soc.o aspeed.o +obj-$(CONFIG_ARM_SMMUV3) +=3D smmu-common.o diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c new file mode 100644 index 0000000..49ed1c2 --- /dev/null +++ b/hw/arm/smmu-common.c @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2014-2016 Broadcom Corporation + * Copyright (c) 2017 Red Hat, Inc. + * 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 + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, U= SA. + * + * Author: Prem Mallappa + * + */ + +#include "qemu/osdep.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" +#include "trace.h" +#include "qemu/error-report.h" +#include "hw/arm/smmu-common.h" +#include "smmu-internal.h" + +inline MemTxResult smmu_read_sysmem(dma_addr_t addr, void *buf, dma_addr_t= len, + bool secure) +{ + MemTxAttrs attrs =3D {.unspecified =3D 1, .secure =3D secure}; + + switch (len) { + case 4: + *(uint32_t *)buf =3D ldl_le_phys(&address_space_memory, addr); + break; + case 8: + *(uint64_t *)buf =3D ldq_le_phys(&address_space_memory, addr); + break; + default: + return address_space_rw(&address_space_memory, addr, + attrs, buf, len, false); + } + return MEMTX_OK; +} + +inline void +smmu_write_sysmem(dma_addr_t addr, void *buf, dma_addr_t len, bool secure) +{ + MemTxAttrs attrs =3D {.unspecified =3D 1, .secure =3D secure}; + + switch (len) { + case 4: + stl_le_phys(&address_space_memory, addr, *(uint32_t *)buf); + break; + case 8: + stq_le_phys(&address_space_memory, addr, *(uint64_t *)buf); + break; + default: + address_space_rw(&address_space_memory, addr, + attrs, buf, len, true); + } +} + +/*************************/ +/* VMSAv8-64 Translation */ +/*************************/ + +/** + * get_pte - Get the content of a page table entry located in + * @base_addr[@index] + */ +static uint64_t get_pte(dma_addr_t baseaddr, uint32_t index) +{ + uint64_t pte; + + if (smmu_read_sysmem(baseaddr + index * sizeof(pte), + &pte, sizeof(pte), false)) { + error_report("can't read pte at address=3D0x%"PRIx64, + baseaddr + index * sizeof(pte)); + pte =3D (uint64_t)-1; + return pte; + } + trace_smmu_get_pte(baseaddr, index, baseaddr + index * sizeof(pte), pt= e); + /* TODO: handle endianness */ + return pte; +} + +/* VMSAv8-64 Translation Table Format Descriptor Decoding */ + +#define PTE_ADDRESS(pte, shift) (extract64(pte, shift, 47 - shift) << shif= t) + +/** + * 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 + * ARM ARM Figure D4-16 VMSAv8-64 level0, level1, and level 2 descriptor f= ormats + */ +static hwaddr get_block_pte_address(uint64_t pte, int level, int granule_s= z) +{ + int n; + + switch (granule_sz) { + case 12: + if (level =3D=3D 1) { + n =3D 30; + } else if (level =3D=3D 2) { + n =3D 21; + } else { + goto error_out; + } + break; + case 14: + if (level =3D=3D 2) { + n =3D 25; + } else { + goto error_out; + } + break; + case 16: + if (level =3D=3D 2) { + n =3D 29; + } else { + goto error_out; + } + break; + default: + goto error_out; + } + return PTE_ADDRESS(pte, n); + +error_out: + + error_report("unexpected granule_sz=3D%d/level=3D%d for block pte", + granule_sz, level); + return (hwaddr)-1; +} + +/** + * smmu_page_walk_level_64 - Walk an IOVA range from a specific level + * @baseaddr: table base address corresponding to @level + * @level: level + * @cfg: translation config + * @start: end of the IOVA range + * @end: end of the IOVA range + * @hook_fn: the hook that to be called for each detected area + * @private: private data for the hook function + * @read: whether parent level has read permission + * @write: whether parent level has write permission + * @must_translate: indicates whether each iova of the range + * must be translated or whether failure is allowed + * @notify_unmap: whether we should notify invalid entries + * + * Return 0 on success, < 0 on errors not related to translation + * process, > 1 on errors related to translation process (only + * if must_translate is set) + */ +static int +smmu_page_walk_level_64(dma_addr_t baseaddr, int level, + SMMUTransCfg *cfg, uint64_t start, uint64_t end, + smmu_page_walk_hook hook_fn, void *private, + bool read, bool write, bool must_translate, + bool notify_unmap) +{ + uint64_t subpage_size, subpage_mask, pte, iova =3D start; + bool read_cur, write_cur, entry_valid; + int ret, granule_sz; + IOMMUTLBEntry entry; + + granule_sz =3D cfg->granule_sz; + + subpage_size =3D 1ULL << level_shift(level, granule_sz); + subpage_mask =3D level_page_mask(level, granule_sz); + + trace_smmu_page_walk_level_in(level, baseaddr, granule_sz, + start, end, subpage_size); + + while (iova < end) { + dma_addr_t next_table_baseaddr; + uint64_t iova_next; + uint32_t offset; + + iova_next =3D (iova & subpage_mask) + subpage_size; + + offset =3D iova_level_offset(iova, level, granule_sz); + pte =3D get_pte(baseaddr, offset); + + trace_smmu_page_walk_level(level, iova, baseaddr, offset, pte); + + if (pte =3D=3D (uint64_t)-1) { + if (must_translate) { + return SMMU_TRANS_ERR_WALK_EXT_ABRT; + } + goto next; + } + if (is_invalid_pte(pte) || is_reserved_pte(pte, level)) { + trace_smmu_page_walk_level_res_invalid_pte(baseaddr, offset, p= te); + if (must_translate) { + return SMMU_TRANS_ERR_WALK_EXT_ABRT; + } + goto next; + } + + read_cur =3D read; /* TODO */ + write_cur =3D write; /* TODO */ + entry_valid =3D read_cur | write_cur; /* TODO */ + + if (is_page_pte(pte, level)) { + entry.target_as =3D &address_space_memory; + entry.iova =3D iova & subpage_mask; + /* NOTE: this is only meaningful if entry_valid =3D=3D true */ + entry.translated_addr =3D get_page_pte_address(pte, granule_sz= ); + entry.addr_mask =3D ~subpage_mask; + entry.perm =3D IOMMU_ACCESS_FLAG(read_cur, write_cur); + trace_smmu_page_walk_level_page_pte(pte, entry.translated_addr= ); + if (!entry_valid && !notify_unmap) { + printf("%s entry_valid=3D%d notify_unmap=3D%d\n", __func__, + entry_valid, notify_unmap); + goto next; + } + if (hook_fn) { + ret =3D hook_fn(&entry, private); + if (ret) { + return ret; + } + } + goto next; + } + if (is_block_pte(pte, level)) { + trace_smmu_page_walk_level_block_pte(pte, + get_block_pte_address(pte, level, granule_sz)); + if (must_translate) { + return SMMU_TRANS_ERR_WALK_EXT_ABRT; + } + printf("%s BLOCK PTE not handled yet\n", __func__); + goto next; + } + /* table pte */ + next_table_baseaddr =3D get_table_pte_address(pte, granule_sz); + trace_smmu_page_walk_level_table_pte(pte, next_table_baseaddr); + ret =3D smmu_page_walk_level_64(next_table_baseaddr, level + 1, cf= g, + iova, MIN(iova_next, end), + hook_fn, private, read_cur, write_cu= r, + must_translate, notify_unmap); + if (!ret) { + return ret; + } + +next: + iova =3D iova_next; + } + + return SMMU_TRANS_ERR_NONE; +} + +/** + * smmu_page_walk_64 - walk a specific IOVA range from the initial + * lookup level, and call the hook for each valid entry + * + * @cfg: translation config + * @start: start of the IOVA range + * @end: end of the IOVA range + * @must_translate: indicates whether each iova of the range + * must be translated or whether failure is allowed + * @hook_fn: the hook that to be called for each detected area + * @private: private data for the hook function + */ +static int +smmu_page_walk_64(SMMUTransCfg *cfg, uint64_t start, uint64_t end, + bool must_translate, smmu_page_walk_hook hook_fn, + void *private) +{ + dma_addr_t ttbr; + int stage =3D cfg->stage; + int initial_level; + + if (stage !=3D 1) { + error_report("%s stage 2 not yet supported", __func__); + return -1; /* TODO */ + } + + /* TODO check start/end */ + + ttbr =3D extract64(cfg->ttbr, 0, 48); + initial_level =3D initial_lookup_level(cfg->tsz, cfg->granule_sz); + + trace_smmu_page_walk(stage, cfg->ttbr, initial_level, start, end); + + if (initial_level < 0) { + return -1; /* TODO */ + } + + return smmu_page_walk_level_64(ttbr, initial_level, cfg, start, end, + hook_fn, private, + true /* read */, true /* write */, + must_translate, false /* notify_unmap *= /); +} + +static int set_translated_address(IOMMUTLBEntry *entry, void *private) +{ + SMMUTransCfg *cfg =3D (SMMUTransCfg *)private; + size_t offset =3D cfg->input - entry->iova; + + cfg->output =3D entry->translated_addr + offset; + + trace_smmu_set_translated_address(cfg->input, cfg->output); + return 0; +} + +static int +smmu_translate_64(SMMUTransCfg *cfg, uint32_t *pagesize, + uint32_t *perm, bool is_write) +{ + int ret; + + ret =3D smmu_page_walk_64(cfg, cfg->input, cfg->input + 1, + true /* must_translate */, + set_translated_address, cfg); + *pagesize =3D 1 << cfg->granule_sz; + return ret; +} + +/*************************/ +/* VMSAv8-32 Translation */ +/*************************/ + +static int +smmu_page_walk_32(SMMUTransCfg *cfg, uint64_t start, uint64_t end, + bool must_translate, smmu_page_walk_hook hook_fn, + void *private) +{ + error_report("VMSAv8-32 translation is not yet implemented"); + abort(); +} + +static int smmu_translate_32(SMMUTransCfg *cfg, uint32_t *pagesize, + uint32_t *perm, bool is_write) +{ + error_report("VMSAv8-32 translation is not yet implemented"); + abort(); +} + +/******************/ +/* Infrastructure */ +/******************/ + +SMMUPciBus *smmu_find_as_from_bus_num(SMMUState *s, uint8_t bus_num) +{ + SMMUPciBus *smmu_pci_bus =3D s->smmu_as_by_bus_num[bus_num]; + + if (!smmu_pci_bus) { + GHashTableIter iter; + + g_hash_table_iter_init(&iter, s->smmu_as_by_busptr); + while (g_hash_table_iter_next(&iter, NULL, (void **)&smmu_pci_bus)= ) { + if (pci_bus_num(smmu_pci_bus->bus) =3D=3D bus_num) { + s->smmu_as_by_bus_num[bus_num] =3D smmu_pci_bus; + return smmu_pci_bus; + } + } + } + return smmu_pci_bus; +} + +static void smmu_base_instance_init(Object *obj) +{ + /* Nothing much to do here as of now */ +} + +static void smmu_base_class_init(ObjectClass *klass, void *data) +{ + SMMUBaseClass *sbc =3D SMMU_DEVICE_CLASS(klass); + + sbc->translate_64 =3D smmu_translate_64; + sbc->page_walk_64 =3D smmu_page_walk_64; + + sbc->translate_32 =3D smmu_translate_32; + sbc->page_walk_32 =3D smmu_page_walk_32; +} + +static const TypeInfo smmu_base_info =3D { + .name =3D TYPE_SMMU_DEV_BASE, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(SMMUState), + .instance_init =3D smmu_base_instance_init, + .class_data =3D NULL, + .class_size =3D sizeof(SMMUBaseClass), + .class_init =3D smmu_base_class_init, + .abstract =3D true, +}; + +static void smmu_base_register_types(void) +{ + type_register_static(&smmu_base_info); +} + +type_init(smmu_base_register_types) + diff --git a/hw/arm/smmu-internal.h b/hw/arm/smmu-internal.h new file mode 100644 index 0000000..5e890bb --- /dev/null +++ b/hw/arm/smmu-internal.h @@ -0,0 +1,97 @@ +/* + * ARM SMMU support - Internal API + * + * Copyright (c) 2017 Red Hat, Inc. + * Written by Eric Auger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 . + */ + +#define ARM_LPAE_MAX_ADDR_BITS 48 +#define ARM_LPAE_MAX_LEVELS 4 + +/* Page table bits */ + +#ifndef HW_ARM_SMMU_INTERNAL_H +#define HW_ARM_SMMU_INTERNAL_H + +#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_RESERVED 1 +#define ARM_LPAE_PTE_TYPE_TABLE 3 +#define ARM_LPAE_PTE_TYPE_PAGE 3 + +#define ARM_LPAE_PTE_VALID (1 << 0) + +static inline bool is_invalid_pte(uint64_t pte) +{ + return !(pte & ARM_LPAE_PTE_VALID); +} + +static inline bool is_reserved_pte(uint64_t pte, int level) +{ + return ((level =3D=3D 3) && + ((pte & ARM_LPAE_PTE_TYPE_MASK) =3D=3D ARM_LPAE_PTE_TYPE_RESER= VED)); +} + +static inline bool is_block_pte(uint64_t pte, int level) +{ + return ((level < 3) && + ((pte & ARM_LPAE_PTE_TYPE_MASK) =3D=3D ARM_LPAE_PTE_TYPE_BLOCK= )); +} + +static inline bool is_table_pte(uint64_t pte, int level) +{ + return ((level < 3) && + ((pte & ARM_LPAE_PTE_TYPE_MASK) =3D=3D ARM_LPAE_PTE_TYPE_TABLE= )); +} + +static inline bool is_page_pte(uint64_t pte, int level) +{ + return ((level =3D=3D 3) && + ((pte & ARM_LPAE_PTE_TYPE_MASK) =3D=3D ARM_LPAE_PTE_TYPE_PAGE)= ); +} + +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 ~((1ULL << level_shift(level, granule_sz)) - 1); +} + +/** + * TODO: handle the case where the level resolves less than + * granule_sz -3 IA bits. + */ +static inline +uint64_t iova_level_offset(uint64_t iova, int level, int granule_sz) +{ + return (iova >> level_shift(level, granule_sz)) & + ((1ULL << (granule_sz - 3)) - 1); +} + +/* TODO: check this for stage 2 and table concatenation */ +static inline int initial_lookup_level(int tnsz, int granule_sz) +{ + return 4 - (64 - tnsz - 4) / (granule_sz - 3); +} + + + +#endif diff --git a/hw/arm/trace-events b/hw/arm/trace-events index d5f33a2..1d53ad0 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -2,3 +2,15 @@ =20 # hw/arm/virt-acpi-build.c virt_acpi_setup(void) "No fw cfg or ACPI disabled. Bailing out." + +# hw/arm/smmu-common.c + +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_page_walk_level_in(int level, uint64_t baseaddr, int granule_sz, uint= 64_t start, uint64_t end, uint64_t subpage_size) "level=3D%d baseaddr=3D0x%= "PRIx64" granule=3D%d, start=3D0x%"PRIx64" end=3D0x%"PRIx64", subpage_size= =3D0x%lx" +smmu_page_walk_level(int level, uint64_t iova, uint64_t baseaddr, uint32_t= offset, uint64_t pte) "level=3D%d iova=3D0x%lx baseaddr=3D0x%"PRIx64" offs= et=3D0x%x =3D> pte=3D0x%lx" +smmu_page_walk_level_res_invalid_pte(uint64_t baseaddr, uint32_t offset, u= int64_t pte) "baseaddr=3D0x%"PRIx64" offset=3D0x%x pte=3D0x%lx" +smmu_page_walk_level_page_pte(uint64_t pte, uint64_t address) "pte=3D0x%"P= RIx64" page address =3D 0x%"PRIx64 +smmu_page_walk_level_block_pte(uint64_t pte, uint64_t address) "pte=3D0x%"= PRIx64" block address =3D 0x%"PRIx64 +smmu_page_walk_level_table_pte(uint64_t pte, uint64_t address) "pte=3D0x%"= PRIx64" next table address =3D 0x%"PRIx64 +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 +smmu_set_translated_address(hwaddr iova, hwaddr va) "iova =3D 0x%"PRIx64" = -> pa =3D 0x%"PRIx64 diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h new file mode 100644 index 0000000..836c916 --- /dev/null +++ b/include/hw/arm/smmu-common.h @@ -0,0 +1,125 @@ +/* + * ARM SMMU Support + * + * Copyright (C) 2015-2016 Broadcom Corporation + * Copyright (c) 2017 Red Hat, Inc. + * 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 as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 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 + * GNU 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_COMMON_H +#define HW_ARM_SMMU_COMMON_H + +#include +#include "hw/pci/pci.h" + +#define SMMU_PCI_BUS_MAX 256 +#define SMMU_PCI_DEVFN_MAX 256 + +typedef enum { + SMMU_TRANS_ERR_NONE =3D 0x0, + SMMU_TRANS_ERR_WALK_EXT_ABRT =3D 0x1, /* Translation walk external ab= ort */ + SMMU_TRANS_ERR_TRANS =3D 0x10, /* Translation fault */ + SMMU_TRANS_ERR_ADDR_SZ, /* Address Size fault */ + SMMU_TRANS_ERR_ACCESS, /* Access fault */ + SMMU_TRANS_ERR_PERM, /* Permission fault */ + SMMU_TRANS_ERR_TLB_CONFLICT =3D 0x20, /* TLB Conflict */ +} SMMUTransErr; + +/* + * Generic structure populated by derived SMMU devices + * after decoding the configuration information and used as + * input to the page table walk + */ +typedef struct SMMUTransCfg { + hwaddr input; /* input address */ + hwaddr output; /* Output address */ + int stage; /* translation stage */ + uint32_t oas; /* output address width */ + uint32_t tsz; /* input range, ie. 2^(64 -tnsz)*/ + uint64_t ttbr; /* TTBR address */ + uint32_t granule_sz; /* granule page shift */ + bool aa64; /* arch64 or aarch32 translation table */ +} SMMUTransCfg; + +typedef struct SMMUDevice { + void *smmu; + PCIBus *bus; + int devfn; + MemoryRegion iommu; + AddressSpace as; +} SMMUDevice; + +typedef struct SMMUNotifierNode { + SMMUDevice *sdev; + QLIST_ENTRY(SMMUNotifierNode) next; +} SMMUNotifierNode; + +typedef struct SMMUPciBus { + PCIBus *bus; + SMMUDevice *pbdev[0]; /* Parent array is sparse, so dynamically allo= c */ +} SMMUPciBus; + +typedef struct SMMUState { + /* */ + SysBusDevice dev; + + MemoryRegion iomem; + + MemoryRegionIOMMUOps iommu_ops; + GHashTable *smmu_as_by_busptr; + SMMUPciBus *smmu_as_by_bus_num[SMMU_PCI_BUS_MAX]; + QLIST_HEAD(, SMMUNotifierNode) notifiers_list; + +} SMMUState; + +typedef int (*smmu_page_walk_hook)(IOMMUTLBEntry *entry, void *private); + +typedef struct { + /* */ + SysBusDeviceClass parent_class; + + /* public */ + int (*translate_32)(SMMUTransCfg *cfg, uint32_t *pagesize, + uint32_t *perm, bool is_write); + int (*translate_64)(SMMUTransCfg *cfg, uint32_t *pagesize, + uint32_t *perm, bool is_write); + int (*page_walk_32)(SMMUTransCfg *cfg, uint64_t start, uint64_t end, + bool must_translate, smmu_page_walk_hook hook_fn, + void *private); + int (*page_walk_64)(SMMUTransCfg *cfg, uint64_t start, uint64_t end, + bool must_translate, smmu_page_walk_hook hook_fn, + void *private); +} SMMUBaseClass; + +#define TYPE_SMMU_DEV_BASE "smmu-base" +#define SMMU_SYS_DEV(obj) OBJECT_CHECK(SMMUState, (obj), TYPE_SMMU_DEV_BAS= E) +#define SMMU_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SMMUBaseClass, (obj), TYPE_SMMU_DEV_BASE) +#define SMMU_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(SMMUBaseClass, (klass), TYPE_SMMU_DEV_BASE) + +MemTxResult smmu_read_sysmem(dma_addr_t addr, void *buf, + dma_addr_t len, bool secure); +void smmu_write_sysmem(dma_addr_t addr, void *buf, dma_addr_t len, bool se= cure); + +SMMUPciBus *smmu_find_as_from_bus_num(SMMUState *s, uint8_t bus_num); + +static inline uint16_t smmu_get_sid(SMMUDevice *sdev) +{ + return ((pci_bus_num(sdev->bus) & 0xff) << 8) | sdev->devfn; +} + +#endif /* HW_ARM_SMMU_COMMON */ --=20 2.5.5