target-arm queue:

* Emulate the SMMUv3 (IOMMU); one will be created in the 'virt' board
    if the commandline includes "-machine iommu=smmuv3"
  * target/arm: Implement v8M VLLDM and VLSTM
  * hw/arm: Don't fail qtest due to missing SD card in -nodefaults mode
  * Some fixes to silence Coverity false-positives
  * arm: boot: set boot_info starting from first_cpu
    (fixes a technical bug not visible in practice)
  * hw/net/smc91c111: Convert away from old_mmio
  * hw/usb/tusb6010: Convert away from old_mmio
  * hw/char/cmsdk-apb-uart.c: Accept more input after character read
  * target/arm: Make MPUIR write-ignored on OMAP, StrongARM
  * hw/arm/virt: Add linux,pci-domain property
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABCAAGBQJa7J5pAAoJEDwlJe0UNgzeHRUP/j8P4+Ftg8KStJvBiycrorVz
 EwHfkb5fRpHBH8WcjV85dLmk6AAmf9yvFowaxyU+H1Qa2xcchnIysCFXqfjkr0dN
 vloP7+mPTlb4Qhnfpa9KAVbxple91p5OBZCRBwIqTyqdTrZr5Oqw3c0fPGgVH4ze
 f+Oo6sWYQyhvpDmGE2PNLEo6VA3m8TOSOt19RpFW72WbEScAbw7Nart6WRGmzr2r
 BDizPgdQ55OmmPxv24jqy4HxDkpBQUuua74RcTQiQLmbHd2GYvEMnW4nhlntBsNo
 9TRH6xJ+7UqC+zpyE6AEJ8JtQwA47dfZfKqm1hI9AtYDCU0H220J2LHUjUfs/g3G
 2MYoF8XStPpmfBo7WHNP7gdQqOhEbMWrkHlsZ6c4waKhK/aYTLTwy37iPpxH3/iI
 MlceyexY/swUKDhssY1mcMaCszCzdYR534d1ZMZGFjrYcAF2VQcfVtPgCtAv1Icd
 FfjKzpFB0IQmFtkrNLxBoifxM3VvIvAXqktDWJuOdYa2G4MqpPktMHzFrV9f//Xn
 09P0VR46BaGnDyls4k5hiC1gyulVG5CAXUfZeBR8xsy1JrdbNNyp6Jc5ekY68Pa+
 oVDxyLWEYnzBqqtkujTIYVwvnx9KL+iW05mb5fOJ21SH9dt9a5YhFCbrZfB8roKz
 LrWbefXIP2b/4kLl8viJ
 =ZjhH
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20180504-1' into staging

target-arm queue:
 * Emulate the SMMUv3 (IOMMU); one will be created in the 'virt' board
   if the commandline includes "-machine iommu=smmuv3"
 * target/arm: Implement v8M VLLDM and VLSTM
 * hw/arm: Don't fail qtest due to missing SD card in -nodefaults mode
 * Some fixes to silence Coverity false-positives
 * arm: boot: set boot_info starting from first_cpu
   (fixes a technical bug not visible in practice)
 * hw/net/smc91c111: Convert away from old_mmio
 * hw/usb/tusb6010: Convert away from old_mmio
 * hw/char/cmsdk-apb-uart.c: Accept more input after character read
 * target/arm: Make MPUIR write-ignored on OMAP, StrongARM
 * hw/arm/virt: Add linux,pci-domain property

# gpg: Signature made Fri 04 May 2018 18:54:49 BST
# gpg:                using RSA key 3C2525ED14360CDE
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>"
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>"
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>"
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83  15CF 3C25 25ED 1436 0CDE

* remotes/pmaydell/tags/pull-target-arm-20180504-1: (24 commits)
  hw/arm/virt: Introduce the iommu option
  hw/arm/virt-acpi-build: Add smmuv3 node in IORT table
  hw/arm/virt: Add SMMUv3 to the virt board
  target/arm/kvm: Translate the MSI doorbell in kvm_arch_fixup_msi_route
  hw/arm/smmuv3: Abort on vfio or vhost case
  hw/arm/smmuv3: Implement translate callback
  hw/arm/smmuv3: Event queue recording helper
  hw/arm/smmuv3: Implement MMIO write operations
  hw/arm/smmuv3: Queue helpers
  hw/arm/smmuv3: Wired IRQ and GERROR helpers
  hw/arm/smmuv3: Skeleton
  hw/arm/smmu-common: VMSAv8-64 page table walk
  hw/arm/smmu-common: IOMMU memory region and address space setup
  hw/arm/smmu-common: smmu base device and datatypes
  target/arm: Implement v8M VLLDM and VLSTM
  hw/arm: Don't fail qtest due to missing SD card in -nodefaults mode
  target/arm: Tidy condition in disas_simd_two_reg_misc
  target/arm: Tidy conditions in handle_vec_simd_shri
  arm: boot: set boot_info starting from first_cpu
  hw/net/smc91c111: Convert away from old_mmio
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2018-05-04 18:58:39 +01:00
commit 853f8ca13c
25 changed files with 2868 additions and 67 deletions

View File

@ -8,3 +8,4 @@ CONFIG_DDC=y
CONFIG_DPCD=y
CONFIG_XLNX_ZYNQMP=y
CONFIG_XLNX_ZYNQMP_ARM=y
CONFIG_ARM_SMMUV3=y

View File

@ -35,3 +35,4 @@ obj-$(CONFIG_MPS2) += mps2-tz.o
obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o
obj-$(CONFIG_IOTKIT) += iotkit.o
obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o mcimx7d-sabre.o
obj-$(CONFIG_ARM_SMMUV3) += smmu-common.o smmuv3.o

View File

@ -1170,7 +1170,7 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
}
info->is_linux = is_linux;
for (cs = CPU(cpu); cs; cs = CPU_NEXT(cs)) {
for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) {
ARM_CPU(cs)->env.boot_info = info;
}
}

View File

@ -30,6 +30,7 @@
#include "hw/arm/soc_dma.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
#include "sysemu/qtest.h"
#include "qemu/range.h"
#include "hw/sysbus.h"
#include "qemu/cutils.h"
@ -3987,12 +3988,11 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory,
omap_findclk(s, "dpll3"));
dinfo = drive_get(IF_SD, 0, 0);
if (!dinfo) {
error_report("missing SecureDigital device");
exit(1);
if (!dinfo && !qtest_enabled()) {
warn_report("missing SecureDigital device");
}
s->mmc = omap_mmc_init(0xfffb7800, system_memory,
blk_by_legacy_dinfo(dinfo),
dinfo ? blk_by_legacy_dinfo(dinfo) : NULL,
qdev_get_gpio_in(s->ih[1], OMAP_INT_OQN),
&s->drq[OMAP_DMA_MMC_TX],
omap_findclk(s, "mmc_ck"));

View File

@ -25,6 +25,7 @@
#include "cpu.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
#include "sysemu/qtest.h"
#include "hw/boards.h"
#include "hw/hw.h"
#include "hw/arm/arm.h"
@ -2486,12 +2487,11 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem,
s->drq[OMAP24XX_DMA_GPMC]);
dinfo = drive_get(IF_SD, 0, 0);
if (!dinfo) {
error_report("missing SecureDigital device");
exit(1);
if (!dinfo && !qtest_enabled()) {
warn_report("missing SecureDigital device");
}
s->mmc = omap2_mmc_init(omap_l4tao(s->l4, 9),
blk_by_legacy_dinfo(dinfo),
dinfo ? blk_by_legacy_dinfo(dinfo) : NULL,
qdev_get_gpio_in(s->ih[0], OMAP_INT_24XX_MMC_IRQ),
&s->drq[OMAP24XX_DMA_MMC1_TX],
omap_findclk(s, "mmc_fclk"), omap_findclk(s, "mmc_iclk"));

View File

@ -21,6 +21,7 @@
#include "chardev/char-fe.h"
#include "sysemu/block-backend.h"
#include "sysemu/blockdev.h"
#include "sysemu/qtest.h"
#include "qemu/cutils.h"
static struct {
@ -2095,12 +2096,11 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space,
s->gpio = pxa2xx_gpio_init(0x40e00000, s->cpu, s->pic, 121);
dinfo = drive_get(IF_SD, 0, 0);
if (!dinfo) {
error_report("missing SecureDigital device");
exit(1);
if (!dinfo && !qtest_enabled()) {
warn_report("missing SecureDigital device");
}
s->mmc = pxa2xx_mmci_init(address_space, 0x41100000,
blk_by_legacy_dinfo(dinfo),
dinfo ? blk_by_legacy_dinfo(dinfo) : NULL,
qdev_get_gpio_in(s->pic, PXA2XX_PIC_MMC),
qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_MMCI),
qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_MMCI));
@ -2220,12 +2220,11 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size)
s->gpio = pxa2xx_gpio_init(0x40e00000, s->cpu, s->pic, 85);
dinfo = drive_get(IF_SD, 0, 0);
if (!dinfo) {
error_report("missing SecureDigital device");
exit(1);
if (!dinfo && !qtest_enabled()) {
warn_report("missing SecureDigital device");
}
s->mmc = pxa2xx_mmci_init(address_space, 0x41100000,
blk_by_legacy_dinfo(dinfo),
dinfo ? blk_by_legacy_dinfo(dinfo) : NULL,
qdev_get_gpio_in(s->pic, PXA2XX_PIC_MMC),
qdev_get_gpio_in(s->dma, PXA2XX_RX_RQ_MMCI),
qdev_get_gpio_in(s->dma, PXA2XX_TX_RQ_MMCI));

372
hw/arm/smmu-common.c Normal file
View File

@ -0,0 +1,372 @@
/*
* 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.
*
* Author: Prem Mallappa <pmallapp@broadcom.com>
*
*/
#include "qemu/osdep.h"
#include "sysemu/sysemu.h"
#include "exec/address-spaces.h"
#include "trace.h"
#include "exec/target_page.h"
#include "qom/cpu.h"
#include "hw/qdev-properties.h"
#include "qapi/error.h"
#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 = baseaddr + index * sizeof(*pte);
/* TODO: guarantee 64-bit single-copy atomicity */
ret = dma_memory_read(&address_space_memory, addr,
(uint8_t *)pte, sizeof(*pte));
if (ret != MEMTX_OK) {
info->type = SMMU_PTW_ERR_WALK_EABT;
info->addr = 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 formats
*/
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 block size
* ARM ARM Figure D4-16 VMSAv8-64 level0, level1, and level 2 descriptor formats
*/
static inline hwaddr get_block_pte_address(uint64_t pte, int level,
int granule_sz, uint64_t *bsz)
{
int n = (granule_sz - 3) * (4 - level) + 3;
*bsz = 1 << n;
return PTE_ADDRESS(pte, n);
}
SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova)
{
bool tbi = extract64(iova, 55, 1) ? TBI1(cfg->tbi) : TBI0(cfg->tbi);
uint8_t tbi_byte = 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 = cfg->stage;
SMMUTransTableInfo *tt = select_tt(cfg, iova);
uint8_t level, granule_sz, inputsize, stride;
if (!tt || tt->disabled) {
info->type = SMMU_PTW_ERR_TRANSLATION;
goto error;
}
granule_sz = tt->granule_sz;
stride = granule_sz - 3;
inputsize = 64 - tt->tsz;
level = 4 - (inputsize - 4) / stride;
indexmask = (1ULL << (inputsize - (stride * (4 - level)))) - 1;
baseaddr = extract64(tt->ttb, 0, 48);
baseaddr &= ~indexmask;
tlbe->iova = iova;
tlbe->addr_mask = (1 << granule_sz) - 1;
while (level <= 3) {
uint64_t subpage_size = 1ULL << level_shift(level, granule_sz);
uint64_t mask = subpage_size - 1;
uint32_t offset = iova_level_offset(iova, inputsize, level, granule_sz);
uint64_t pte;
dma_addr_t pte_addr = 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 = SMMU_PTW_ERR_TRANSLATION;
goto error;
}
if (is_page_pte(pte, level)) {
uint64_t gpa = get_page_pte_address(pte, granule_sz);
ap = PTE_AP(pte);
if (is_permission_fault(ap, perm)) {
info->type = SMMU_PTW_ERR_PERMISSION;
goto error;
}
tlbe->translated_addr = gpa + (iova & mask);
tlbe->perm = 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 = get_block_pte_address(pte, level, granule_sz,
&block_size);
ap = PTE_AP(pte);
if (is_permission_fault(ap, perm)) {
info->type = 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 = gpa + (iova & mask);
tlbe->perm = PTE_AP_TO_PERM(ap);
return 0;
}
/* table pte */
ap = PTE_APTABLE(pte);
if (is_permission_fault(ap, perm)) {
info->type = SMMU_PTW_ERR_PERMISSION;
goto error;
}
baseaddr = get_table_pte_address(pte, granule_sz);
level++;
}
info->type = SMMU_PTW_ERR_TRANSLATION;
error:
tlbe->perm = 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 perm,
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);
}
/**
* The bus number is used for lookup when SID based invalidation occurs.
* In that case we lazily populate the SMMUPciBus array from the bus hash
* table. At the time the SMMUPciBus is created (smmu_find_add_as), the bus
* numbers may not be always initialized yet.
*/
SMMUPciBus *smmu_find_smmu_pcibus(SMMUState *s, uint8_t bus_num)
{
SMMUPciBus *smmu_pci_bus = s->smmu_pcibus_by_bus_num[bus_num];
if (!smmu_pci_bus) {
GHashTableIter iter;
g_hash_table_iter_init(&iter, s->smmu_pcibus_by_busptr);
while (g_hash_table_iter_next(&iter, NULL, (void **)&smmu_pci_bus)) {
if (pci_bus_num(smmu_pci_bus->bus) == bus_num) {
s->smmu_pcibus_by_bus_num[bus_num] = smmu_pci_bus;
return smmu_pci_bus;
}
}
}
return smmu_pci_bus;
}
static AddressSpace *smmu_find_add_as(PCIBus *bus, void *opaque, int devfn)
{
SMMUState *s = opaque;
SMMUPciBus *sbus = g_hash_table_lookup(s->smmu_pcibus_by_busptr, bus);
SMMUDevice *sdev;
if (!sbus) {
sbus = g_malloc0(sizeof(SMMUPciBus) +
sizeof(SMMUDevice *) * SMMU_PCI_DEVFN_MAX);
sbus->bus = bus;
g_hash_table_insert(s->smmu_pcibus_by_busptr, bus, sbus);
}
sdev = sbus->pbdev[devfn];
if (!sdev) {
char *name = g_strdup_printf("%s-%d-%d",
s->mrtypename,
pci_bus_num(bus), devfn);
sdev = sbus->pbdev[devfn] = g_new0(SMMUDevice, 1);
sdev->smmu = s;
sdev->bus = bus;
sdev->devfn = devfn;
memory_region_init_iommu(&sdev->iommu, sizeof(sdev->iommu),
s->mrtypename,
OBJECT(s), name, 1ULL << SMMU_MAX_VA_BITS);
address_space_init(&sdev->as,
MEMORY_REGION(&sdev->iommu), name);
trace_smmu_add_mr(name);
g_free(name);
}
return &sdev->as;
}
static void smmu_base_realize(DeviceState *dev, Error **errp)
{
SMMUState *s = ARM_SMMU(dev);
SMMUBaseClass *sbc = ARM_SMMU_GET_CLASS(dev);
Error *local_err = NULL;
sbc->parent_realize(dev, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
s->smmu_pcibus_by_busptr = g_hash_table_new(NULL, NULL);
if (s->primary_bus) {
pci_setup_iommu(s->primary_bus, smmu_find_add_as, s);
} else {
error_setg(errp, "SMMU is not attached to any PCI bus!");
}
}
static void smmu_base_reset(DeviceState *dev)
{
/* will be filled later on */
}
static Property smmu_dev_properties[] = {
DEFINE_PROP_UINT8("bus_num", SMMUState, bus_num, 0),
DEFINE_PROP_LINK("primary-bus", SMMUState, primary_bus, "PCI", PCIBus *),
DEFINE_PROP_END_OF_LIST(),
};
static void smmu_base_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SMMUBaseClass *sbc = ARM_SMMU_CLASS(klass);
dc->props = smmu_dev_properties;
device_class_set_parent_realize(dc, smmu_base_realize,
&sbc->parent_realize);
dc->reset = smmu_base_reset;
}
static const TypeInfo smmu_base_info = {
.name = TYPE_ARM_SMMU,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(SMMUState),
.class_data = NULL,
.class_size = sizeof(SMMUBaseClass),
.class_init = smmu_base_class_init,
.abstract = true,
};
static void smmu_base_register_types(void)
{
type_register_static(&smmu_base_info);
}
type_init(smmu_base_register_types)

99
hw/arm/smmu-internal.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#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 == 3) && \
((pte & ARM_LPAE_PTE_TYPE_MASK) == ARM_LPAE_L3_PTE_TYPE_RESERVED))
#define is_block_pte(pte, level) \
((level < 3) && \
((pte & ARM_LPAE_PTE_TYPE_MASK) == ARM_LPAE_PTE_TYPE_BLOCK))
#define is_table_pte(pte, level) \
((level < 3) && \
((pte & ARM_LPAE_PTE_TYPE_MASK) == ARM_LPAE_PTE_TYPE_TABLE))
#define is_page_pte(pte, level) \
((level == 3) && \
((pte & ARM_LPAE_PTE_TYPE_MASK) == 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, gsz)) &
MAKE_64BIT_MASK(0, gsz - 3);
}
#endif

621
hw/arm/smmuv3-internal.h Normal file
View File

@ -0,0 +1,621 @@
/*
* ARM SMMUv3 support - Internal API
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef HW_ARM_SMMU_V3_INTERNAL_H
#define HW_ARM_SMMU_V3_INTERNAL_H
#include "hw/arm/smmu-common.h"
/* MMIO Registers */
REG32(IDR0, 0x0)
FIELD(IDR0, S1P, 1 , 1)
FIELD(IDR0, TTF, 2 , 2)
FIELD(IDR0, COHACC, 4 , 1)
FIELD(IDR0, ASID16, 12, 1)
FIELD(IDR0, TTENDIAN, 21, 2)
FIELD(IDR0, STALL_MODEL, 24, 2)
FIELD(IDR0, TERM_MODEL, 26, 1)
FIELD(IDR0, STLEVEL, 27, 2)
REG32(IDR1, 0x4)
FIELD(IDR1, SIDSIZE, 0 , 6)
FIELD(IDR1, EVENTQS, 16, 5)
FIELD(IDR1, CMDQS, 21, 5)
#define SMMU_IDR1_SIDSIZE 16
#define SMMU_CMDQS 19
#define SMMU_EVENTQS 19
REG32(IDR2, 0x8)
REG32(IDR3, 0xc)
REG32(IDR4, 0x10)
REG32(IDR5, 0x14)
FIELD(IDR5, OAS, 0, 3);
FIELD(IDR5, GRAN4K, 4, 1);
FIELD(IDR5, GRAN16K, 5, 1);
FIELD(IDR5, GRAN64K, 6, 1);
#define SMMU_IDR5_OAS 4
REG32(IIDR, 0x1c)
REG32(CR0, 0x20)
FIELD(CR0, SMMU_ENABLE, 0, 1)
FIELD(CR0, EVENTQEN, 2, 1)
FIELD(CR0, CMDQEN, 3, 1)
#define SMMU_CR0_RESERVED 0xFFFFFC20
REG32(CR0ACK, 0x24)
REG32(CR1, 0x28)
REG32(CR2, 0x2c)
REG32(STATUSR, 0x40)
REG32(IRQ_CTRL, 0x50)
FIELD(IRQ_CTRL, GERROR_IRQEN, 0, 1)
FIELD(IRQ_CTRL, PRI_IRQEN, 1, 1)
FIELD(IRQ_CTRL, EVENTQ_IRQEN, 2, 1)
REG32(IRQ_CTRL_ACK, 0x54)
REG32(GERROR, 0x60)
FIELD(GERROR, CMDQ_ERR, 0, 1)
FIELD(GERROR, EVENTQ_ABT_ERR, 2, 1)
FIELD(GERROR, PRIQ_ABT_ERR, 3, 1)
FIELD(GERROR, MSI_CMDQ_ABT_ERR, 4, 1)
FIELD(GERROR, MSI_EVENTQ_ABT_ERR, 5, 1)
FIELD(GERROR, MSI_PRIQ_ABT_ERR, 6, 1)
FIELD(GERROR, MSI_GERROR_ABT_ERR, 7, 1)
FIELD(GERROR, MSI_SFM_ERR, 8, 1)
REG32(GERRORN, 0x64)
#define A_GERROR_IRQ_CFG0 0x68 /* 64b */
REG32(GERROR_IRQ_CFG1, 0x70)
REG32(GERROR_IRQ_CFG2, 0x74)
#define A_STRTAB_BASE 0x80 /* 64b */
#define SMMU_BASE_ADDR_MASK 0xffffffffffe0
REG32(STRTAB_BASE_CFG, 0x88)
FIELD(STRTAB_BASE_CFG, FMT, 16, 2)
FIELD(STRTAB_BASE_CFG, SPLIT, 6 , 5)
FIELD(STRTAB_BASE_CFG, LOG2SIZE, 0 , 6)
#define A_CMDQ_BASE 0x90 /* 64b */
REG32(CMDQ_PROD, 0x98)
REG32(CMDQ_CONS, 0x9c)
FIELD(CMDQ_CONS, ERR, 24, 7)
#define A_EVENTQ_BASE 0xa0 /* 64b */
REG32(EVENTQ_PROD, 0xa8)
REG32(EVENTQ_CONS, 0xac)
#define A_EVENTQ_IRQ_CFG0 0xb0 /* 64b */
REG32(EVENTQ_IRQ_CFG1, 0xb8)
REG32(EVENTQ_IRQ_CFG2, 0xbc)
#define A_IDREGS 0xfd0
static inline int smmu_enabled(SMMUv3State *s)
{
return FIELD_EX32(s->cr[0], CR0, SMMU_ENABLE);
}
/* Command Queue Entry */
typedef struct Cmd {
uint32_t word[4];
} Cmd;
/* Event Queue Entry */
typedef struct Evt {
uint32_t word[8];
} Evt;
static inline uint32_t smmuv3_idreg(int regoffset)
{
/*
* Return the value of the Primecell/Corelink ID registers at the
* specified offset from the first ID register.
* These value indicate an ARM implementation of MMU600 p1
*/
static const uint8_t smmuv3_ids[] = {
0x04, 0, 0, 0, 0x84, 0xB4, 0xF0, 0x10, 0x0D, 0xF0, 0x05, 0xB1
};
return smmuv3_ids[regoffset / 4];
}
static inline bool smmuv3_eventq_irq_enabled(SMMUv3State *s)
{
return FIELD_EX32(s->irq_ctrl, IRQ_CTRL, EVENTQ_IRQEN);
}
static inline bool smmuv3_gerror_irq_enabled(SMMUv3State *s)
{
return FIELD_EX32(s->irq_ctrl, IRQ_CTRL, GERROR_IRQEN);
}
/* Queue Handling */
#define Q_BASE(q) ((q)->base & SMMU_BASE_ADDR_MASK)
#define WRAP_MASK(q) (1 << (q)->log2size)
#define INDEX_MASK(q) (((1 << (q)->log2size)) - 1)
#define WRAP_INDEX_MASK(q) ((1 << ((q)->log2size + 1)) - 1)
#define Q_CONS(q) ((q)->cons & INDEX_MASK(q))
#define Q_PROD(q) ((q)->prod & INDEX_MASK(q))
#define Q_CONS_ENTRY(q) (Q_BASE(q) + (q)->entry_size * Q_CONS(q))
#define Q_PROD_ENTRY(q) (Q_BASE(q) + (q)->entry_size * Q_PROD(q))
#define Q_CONS_WRAP(q) (((q)->cons & WRAP_MASK(q)) >> (q)->log2size)
#define Q_PROD_WRAP(q) (((q)->prod & WRAP_MASK(q)) >> (q)->log2size)
static inline bool smmuv3_q_full(SMMUQueue *q)
{
return ((q->cons ^ q->prod) & WRAP_INDEX_MASK(q)) == WRAP_MASK(q);
}
static inline bool smmuv3_q_empty(SMMUQueue *q)
{
return (q->cons & WRAP_INDEX_MASK(q)) == (q->prod & WRAP_INDEX_MASK(q));
}
static inline void queue_prod_incr(SMMUQueue *q)
{
q->prod = (q->prod + 1) & WRAP_INDEX_MASK(q);
}
static inline void queue_cons_incr(SMMUQueue *q)
{
/*
* We have to use deposit for the CONS registers to preserve
* the ERR field in the high bits.
*/
q->cons = deposit32(q->cons, 0, q->log2size + 1, q->cons + 1);
}
static inline bool smmuv3_cmdq_enabled(SMMUv3State *s)
{
return FIELD_EX32(s->cr[0], CR0, CMDQEN);
}
static inline bool smmuv3_eventq_enabled(SMMUv3State *s)
{
return FIELD_EX32(s->cr[0], CR0, EVENTQEN);
}
static inline void smmu_write_cmdq_err(SMMUv3State *s, uint32_t err_type)
{
s->cmdq.cons = FIELD_DP32(s->cmdq.cons, CMDQ_CONS, ERR, err_type);
}
/* Commands */
typedef enum SMMUCommandType {
SMMU_CMD_NONE = 0x00,
SMMU_CMD_PREFETCH_CONFIG ,
SMMU_CMD_PREFETCH_ADDR,
SMMU_CMD_CFGI_STE,
SMMU_CMD_CFGI_STE_RANGE,
SMMU_CMD_CFGI_CD,
SMMU_CMD_CFGI_CD_ALL,
SMMU_CMD_CFGI_ALL,
SMMU_CMD_TLBI_NH_ALL = 0x10,
SMMU_CMD_TLBI_NH_ASID,
SMMU_CMD_TLBI_NH_VA,
SMMU_CMD_TLBI_NH_VAA,
SMMU_CMD_TLBI_EL3_ALL = 0x18,
SMMU_CMD_TLBI_EL3_VA = 0x1a,
SMMU_CMD_TLBI_EL2_ALL = 0x20,
SMMU_CMD_TLBI_EL2_ASID,
SMMU_CMD_TLBI_EL2_VA,
SMMU_CMD_TLBI_EL2_VAA,
SMMU_CMD_TLBI_S12_VMALL = 0x28,
SMMU_CMD_TLBI_S2_IPA = 0x2a,
SMMU_CMD_TLBI_NSNH_ALL = 0x30,
SMMU_CMD_ATC_INV = 0x40,
SMMU_CMD_PRI_RESP,
SMMU_CMD_RESUME = 0x44,
SMMU_CMD_STALL_TERM,
SMMU_CMD_SYNC,
} SMMUCommandType;
static const char *cmd_stringify[] = {
[SMMU_CMD_PREFETCH_CONFIG] = "SMMU_CMD_PREFETCH_CONFIG",
[SMMU_CMD_PREFETCH_ADDR] = "SMMU_CMD_PREFETCH_ADDR",
[SMMU_CMD_CFGI_STE] = "SMMU_CMD_CFGI_STE",
[SMMU_CMD_CFGI_STE_RANGE] = "SMMU_CMD_CFGI_STE_RANGE",
[SMMU_CMD_CFGI_CD] = "SMMU_CMD_CFGI_CD",
[SMMU_CMD_CFGI_CD_ALL] = "SMMU_CMD_CFGI_CD_ALL",
[SMMU_CMD_CFGI_ALL] = "SMMU_CMD_CFGI_ALL",
[SMMU_CMD_TLBI_NH_ALL] = "SMMU_CMD_TLBI_NH_ALL",
[SMMU_CMD_TLBI_NH_ASID] = "SMMU_CMD_TLBI_NH_ASID",
[SMMU_CMD_TLBI_NH_VA] = "SMMU_CMD_TLBI_NH_VA",
[SMMU_CMD_TLBI_NH_VAA] = "SMMU_CMD_TLBI_NH_VAA",
[SMMU_CMD_TLBI_EL3_ALL] = "SMMU_CMD_TLBI_EL3_ALL",
[SMMU_CMD_TLBI_EL3_VA] = "SMMU_CMD_TLBI_EL3_VA",
[SMMU_CMD_TLBI_EL2_ALL] = "SMMU_CMD_TLBI_EL2_ALL",
[SMMU_CMD_TLBI_EL2_ASID] = "SMMU_CMD_TLBI_EL2_ASID",
[SMMU_CMD_TLBI_EL2_VA] = "SMMU_CMD_TLBI_EL2_VA",
[SMMU_CMD_TLBI_EL2_VAA] = "SMMU_CMD_TLBI_EL2_VAA",
[SMMU_CMD_TLBI_S12_VMALL] = "SMMU_CMD_TLBI_S12_VMALL",
[SMMU_CMD_TLBI_S2_IPA] = "SMMU_CMD_TLBI_S2_IPA",
[SMMU_CMD_TLBI_NSNH_ALL] = "SMMU_CMD_TLBI_NSNH_ALL",
[SMMU_CMD_ATC_INV] = "SMMU_CMD_ATC_INV",
[SMMU_CMD_PRI_RESP] = "SMMU_CMD_PRI_RESP",
[SMMU_CMD_RESUME] = "SMMU_CMD_RESUME",
[SMMU_CMD_STALL_TERM] = "SMMU_CMD_STALL_TERM",
[SMMU_CMD_SYNC] = "SMMU_CMD_SYNC",
};
static inline const char *smmu_cmd_string(SMMUCommandType type)
{
if (type > SMMU_CMD_NONE && type < ARRAY_SIZE(cmd_stringify)) {
return cmd_stringify[type] ? cmd_stringify[type] : "UNKNOWN";
} else {
return "INVALID";
}
}
/* CMDQ fields */
typedef enum {
SMMU_CERROR_NONE = 0,
SMMU_CERROR_ILL,
SMMU_CERROR_ABT,
SMMU_CERROR_ATC_INV_SYNC,
} SMMUCmdError;
enum { /* Command completion notification */
CMD_SYNC_SIG_NONE,
CMD_SYNC_SIG_IRQ,
CMD_SYNC_SIG_SEV,
};
#define CMD_TYPE(x) extract32((x)->word[0], 0 , 8)
#define CMD_SSEC(x) extract32((x)->word[0], 10, 1)
#define CMD_SSV(x) extract32((x)->word[0], 11, 1)
#define CMD_RESUME_AC(x) extract32((x)->word[0], 12, 1)
#define CMD_RESUME_AB(x) extract32((x)->word[0], 13, 1)
#define CMD_SYNC_CS(x) extract32((x)->word[0], 12, 2)
#define CMD_SSID(x) extract32((x)->word[0], 12, 20)
#define CMD_SID(x) ((x)->word[1])
#define CMD_VMID(x) extract32((x)->word[1], 0 , 16)
#define CMD_ASID(x) extract32((x)->word[1], 16, 16)
#define CMD_RESUME_STAG(x) extract32((x)->word[2], 0 , 16)
#define CMD_RESP(x) extract32((x)->word[2], 11, 2)
#define CMD_LEAF(x) extract32((x)->word[2], 0 , 1)
#define CMD_STE_RANGE(x) extract32((x)->word[2], 0 , 5)
#define CMD_ADDR(x) ({ \
uint64_t high = (uint64_t)(x)->word[3]; \
uint64_t low = extract32((x)->word[2], 12, 20); \
uint64_t addr = high << 32 | (low << 12); \
addr; \
})
#define SMMU_FEATURE_2LVL_STE (1 << 0)
/* Events */
typedef enum SMMUEventType {
SMMU_EVT_OK = 0x00,
SMMU_EVT_F_UUT ,
SMMU_EVT_C_BAD_STREAMID ,
SMMU_EVT_F_STE_FETCH ,
SMMU_EVT_C_BAD_STE ,
SMMU_EVT_F_BAD_ATS_TREQ ,
SMMU_EVT_F_STREAM_DISABLED ,
SMMU_EVT_F_TRANS_FORBIDDEN ,
SMMU_EVT_C_BAD_SUBSTREAMID ,
SMMU_EVT_F_CD_FETCH ,
SMMU_EVT_C_BAD_CD ,
SMMU_EVT_F_WALK_EABT ,
SMMU_EVT_F_TRANSLATION = 0x10,
SMMU_EVT_F_ADDR_SIZE ,
SMMU_EVT_F_ACCESS ,
SMMU_EVT_F_PERMISSION ,
SMMU_EVT_F_TLB_CONFLICT = 0x20,
SMMU_EVT_F_CFG_CONFLICT ,
SMMU_EVT_E_PAGE_REQ = 0x24,
} SMMUEventType;
static const char *event_stringify[] = {
[SMMU_EVT_OK] = "SMMU_EVT_OK",
[SMMU_EVT_F_UUT] = "SMMU_EVT_F_UUT",
[SMMU_EVT_C_BAD_STREAMID] = "SMMU_EVT_C_BAD_STREAMID",
[SMMU_EVT_F_STE_FETCH] = "SMMU_EVT_F_STE_FETCH",
[SMMU_EVT_C_BAD_STE] = "SMMU_EVT_C_BAD_STE",
[SMMU_EVT_F_BAD_ATS_TREQ] = "SMMU_EVT_F_BAD_ATS_TREQ",
[SMMU_EVT_F_STREAM_DISABLED] = "SMMU_EVT_F_STREAM_DISABLED",
[SMMU_EVT_F_TRANS_FORBIDDEN] = "SMMU_EVT_F_TRANS_FORBIDDEN",
[SMMU_EVT_C_BAD_SUBSTREAMID] = "SMMU_EVT_C_BAD_SUBSTREAMID",
[SMMU_EVT_F_CD_FETCH] = "SMMU_EVT_F_CD_FETCH",
[SMMU_EVT_C_BAD_CD] = "SMMU_EVT_C_BAD_CD",
[SMMU_EVT_F_WALK_EABT] = "SMMU_EVT_F_WALK_EABT",
[SMMU_EVT_F_TRANSLATION] = "SMMU_EVT_F_TRANSLATION",
[SMMU_EVT_F_ADDR_SIZE] = "SMMU_EVT_F_ADDR_SIZE",
[SMMU_EVT_F_ACCESS] = "SMMU_EVT_F_ACCESS",
[SMMU_EVT_F_PERMISSION] = "SMMU_EVT_F_PERMISSION",
[SMMU_EVT_F_TLB_CONFLICT] = "SMMU_EVT_F_TLB_CONFLICT",
[SMMU_EVT_F_CFG_CONFLICT] = "SMMU_EVT_F_CFG_CONFLICT",
[SMMU_EVT_E_PAGE_REQ] = "SMMU_EVT_E_PAGE_REQ",
};
static inline const char *smmu_event_string(SMMUEventType type)
{
if (type < ARRAY_SIZE(event_stringify)) {
return event_stringify[type] ? event_stringify[type] : "UNKNOWN";
} else {
return "INVALID";
}
}
/* Encode an event record */
typedef struct SMMUEventInfo {
SMMUEventType type;
uint32_t sid;
bool recorded;
bool record_trans_faults;
union {
struct {
uint32_t ssid;
bool ssv;
dma_addr_t addr;
bool rnw;
bool pnu;
bool ind;
} f_uut;
struct SSIDInfo {
uint32_t ssid;
bool ssv;
} c_bad_streamid;
struct SSIDAddrInfo {
uint32_t ssid;
bool ssv;
dma_addr_t addr;
} f_ste_fetch;
struct SSIDInfo c_bad_ste;
struct {
dma_addr_t addr;
bool rnw;
} f_transl_forbidden;
struct {
uint32_t ssid;
} c_bad_substream;
struct SSIDAddrInfo f_cd_fetch;
struct SSIDInfo c_bad_cd;
struct FullInfo {
bool stall;
uint16_t stag;
uint32_t ssid;
bool ssv;
bool s2;
dma_addr_t addr;
bool rnw;
bool pnu;
bool ind;
uint8_t class;
dma_addr_t addr2;
} f_walk_eabt;
struct FullInfo f_translation;
struct FullInfo f_addr_size;
struct FullInfo f_access;
struct FullInfo f_permission;
struct SSIDInfo f_cfg_conflict;
/**
* not supported yet:
* F_BAD_ATS_TREQ
* F_BAD_ATS_TREQ
* F_TLB_CONFLICT
* E_PAGE_REQUEST
* IMPDEF_EVENTn
*/
} u;
} SMMUEventInfo;
/* EVTQ fields */
#define EVT_Q_OVERFLOW (1 << 31)
#define EVT_SET_TYPE(x, v) deposit32((x)->word[0], 0 , 8 , v)
#define EVT_SET_SSV(x, v) deposit32((x)->word[0], 11, 1 , v)
#define EVT_SET_SSID(x, v) deposit32((x)->word[0], 12, 20, v)
#define EVT_SET_SID(x, v) ((x)->word[1] = v)
#define EVT_SET_STAG(x, v) deposit32((x)->word[2], 0 , 16, v)
#define EVT_SET_STALL(x, v) deposit32((x)->word[2], 31, 1 , v)
#define EVT_SET_PNU(x, v) deposit32((x)->word[3], 1 , 1 , v)
#define EVT_SET_IND(x, v) deposit32((x)->word[3], 2 , 1 , v)
#define EVT_SET_RNW(x, v) deposit32((x)->word[3], 3 , 1 , v)
#define EVT_SET_S2(x, v) deposit32((x)->word[3], 7 , 1 , v)
#define EVT_SET_CLASS(x, v) deposit32((x)->word[3], 8 , 2 , v)
#define EVT_SET_ADDR(x, addr) \
do { \
(x)->word[5] = (uint32_t)(addr >> 32); \
(x)->word[4] = (uint32_t)(addr & 0xffffffff); \
} while (0)
#define EVT_SET_ADDR2(x, addr) \
do { \
deposit32((x)->word[7], 3, 29, addr >> 16); \
deposit32((x)->word[7], 0, 16, addr & 0xffff);\
} while (0)
void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *event);
/* Configuration Data */
/* STE Level 1 Descriptor */
typedef struct STEDesc {
uint32_t word[2];
} STEDesc;
/* CD Level 1 Descriptor */
typedef struct CDDesc {
uint32_t word[2];
} CDDesc;
/* Stream Table Entry(STE) */
typedef struct STE {
uint32_t word[16];
} STE;
/* Context Descriptor(CD) */
typedef struct CD {
uint32_t word[16];
} CD;
/* STE fields */
#define STE_VALID(x) extract32((x)->word[0], 0, 1)
#define STE_CONFIG(x) extract32((x)->word[0], 1, 3)
#define STE_CFG_S1_ENABLED(config) (config & 0x1)
#define STE_CFG_S2_ENABLED(config) (config & 0x2)
#define STE_CFG_ABORT(config) (!(config & 0x4))
#define STE_CFG_BYPASS(config) (config == 0x4)
#define STE_S1FMT(x) extract32((x)->word[0], 4 , 2)
#define STE_S1CDMAX(x) extract32((x)->word[1], 27, 5)
#define STE_S1STALLD(x) extract32((x)->word[2], 27, 1)
#define STE_EATS(x) extract32((x)->word[2], 28, 2)
#define STE_STRW(x) extract32((x)->word[2], 30, 2)
#define STE_S2VMID(x) extract32((x)->word[4], 0 , 16)
#define STE_S2T0SZ(x) extract32((x)->word[5], 0 , 6)
#define STE_S2SL0(x) extract32((x)->word[5], 6 , 2)
#define STE_S2TG(x) extract32((x)->word[5], 14, 2)
#define STE_S2PS(x) extract32((x)->word[5], 16, 3)
#define STE_S2AA64(x) extract32((x)->word[5], 19, 1)
#define STE_S2HD(x) extract32((x)->word[5], 24, 1)
#define STE_S2HA(x) extract32((x)->word[5], 25, 1)
#define STE_S2S(x) extract32((x)->word[5], 26, 1)
#define STE_CTXPTR(x) \
({ \
unsigned long addr; \
addr = (uint64_t)extract32((x)->word[1], 0, 16) << 32; \
addr |= (uint64_t)((x)->word[0] & 0xffffffc0); \
addr; \
})
#define STE_S2TTB(x) \
({ \
unsigned long addr; \
addr = (uint64_t)extract32((x)->word[7], 0, 16) << 32; \
addr |= (uint64_t)((x)->word[6] & 0xfffffff0); \
addr; \
})
static inline int oas2bits(int oas_field)
{
switch (oas_field) {
case 0:
return 32;
case 1:
return 36;
case 2:
return 40;
case 3:
return 42;
case 4:
return 44;
case 5:
return 48;
}
return -1;
}
static inline int pa_range(STE *ste)
{
int oas_field = MIN(STE_S2PS(ste), SMMU_IDR5_OAS);
if (!STE_S2AA64(ste)) {
return 40;
}
return oas2bits(oas_field);
}
#define MAX_PA(ste) ((1 << pa_range(ste)) - 1)
/* CD fields */
#define CD_VALID(x) extract32((x)->word[0], 30, 1)
#define CD_ASID(x) extract32((x)->word[1], 16, 16)
#define CD_TTB(x, sel) \
({ \
uint64_t hi, lo; \
hi = extract32((x)->word[(sel) * 2 + 3], 0, 19); \
hi <<= 32; \
lo = (x)->word[(sel) * 2 + 2] & ~0xfULL; \
hi | lo; \
})
#define CD_TSZ(x, sel) extract32((x)->word[0], (16 * (sel)) + 0, 6)
#define CD_TG(x, sel) extract32((x)->word[0], (16 * (sel)) + 6, 2)
#define CD_EPD(x, sel) extract32((x)->word[0], (16 * (sel)) + 14, 1)
#define CD_ENDI(x) extract32((x)->word[0], 15, 1)
#define CD_IPS(x) extract32((x)->word[1], 0 , 3)
#define CD_TBI(x) extract32((x)->word[1], 6 , 2)
#define CD_HD(x) extract32((x)->word[1], 10 , 1)
#define CD_HA(x) extract32((x)->word[1], 11 , 1)
#define CD_S(x) extract32((x)->word[1], 12, 1)
#define CD_R(x) extract32((x)->word[1], 13, 1)
#define CD_A(x) extract32((x)->word[1], 14, 1)
#define CD_AARCH64(x) extract32((x)->word[1], 9 , 1)
#define CDM_VALID(x) ((x)->word[0] & 0x1)
static inline int is_cd_valid(SMMUv3State *s, STE *ste, CD *cd)
{
return CD_VALID(cd);
}
/**
* tg2granule - Decodes the CD translation granule size field according
* to the ttbr in use
* @bits: TG0/1 fields
* @ttbr: ttbr index in use
*/
static inline int tg2granule(int bits, int ttbr)
{
switch (bits) {
case 0:
return ttbr ? 0 : 12;
case 1:
return ttbr ? 14 : 16;
case 2:
return ttbr ? 12 : 14;
case 3:
return ttbr ? 16 : 0;
default:
return 0;
}
}
static inline uint64_t l1std_l2ptr(STEDesc *desc)
{
uint64_t hi, lo;
hi = desc->word[1];
lo = desc->word[0] & ~0x1fULL;
return hi << 32 | lo;
}
#define L1STD_SPAN(stm) (extract32((stm)->word[0], 0, 4))
#endif

1191
hw/arm/smmuv3.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2,3 +2,40 @@
# hw/arm/virt-acpi-build.c
virt_acpi_setup(void) "No fw cfg or ACPI disabled. Bailing out."
# hw/arm/smmu-common.c
smmu_add_mr(const char *name) "%s"
smmu_page_walk(int stage, uint64_t baseaddr, int first_level, uint64_t start, uint64_t end) "stage=%d, baseaddr=0x%"PRIx64", first level=%d, start=0x%"PRIx64", end=0x%"PRIx64
smmu_lookup_table(int level, uint64_t baseaddr, int granule_sz, uint64_t start, uint64_t end, int flags, uint64_t subpage_size) "level=%d baseaddr=0x%"PRIx64" granule=%d, start=0x%"PRIx64" end=0x%"PRIx64" flags=%d subpage_size=0x%"PRIx64
smmu_ptw_level(int level, uint64_t iova, size_t subpage_size, uint64_t baseaddr, uint32_t offset, uint64_t pte) "level=%d iova=0x%"PRIx64" subpage_sz=0x%zx baseaddr=0x%"PRIx64" offset=%d => pte=0x%"PRIx64
smmu_ptw_invalid_pte(int stage, int level, uint64_t baseaddr, uint64_t pteaddr, uint32_t offset, uint64_t pte) "stage=%d level=%d base@=0x%"PRIx64" pte@=0x%"PRIx64" offset=%d pte=0x%"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=%d level=%d iova=0x%"PRIx64" base@=0x%"PRIx64" pte@=0x%"PRIx64" pte=0x%"PRIx64" page address = 0x%"PRIx64
smmu_ptw_block_pte(int stage, int level, uint64_t baseaddr, uint64_t pteaddr, uint64_t pte, uint64_t iova, uint64_t gpa, int bsize_mb) "stage=%d level=%d base@=0x%"PRIx64" pte@=0x%"PRIx64" pte=0x%"PRIx64" iova=0x%"PRIx64" block address = 0x%"PRIx64" block size = %d MiB"
smmu_get_pte(uint64_t baseaddr, int index, uint64_t pteaddr, uint64_t pte) "baseaddr=0x%"PRIx64" index=0x%x, pteaddr=0x%"PRIx64", pte=0x%"PRIx64
#hw/arm/smmuv3.c
smmuv3_read_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x(%d)"
smmuv3_trigger_irq(int irq) "irq=%d"
smmuv3_write_gerror(uint32_t toggled, uint32_t gerror) "toggled=0x%x, new GERROR=0x%x"
smmuv3_write_gerrorn(uint32_t acked, uint32_t gerrorn) "acked=0x%x, new GERRORN=0x%x"
smmuv3_unhandled_cmd(uint32_t type) "Unhandled command type=%d"
smmuv3_cmdq_consume(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "prod=%d cons=%d prod.wrap=%d cons.wrap=%d"
smmuv3_cmdq_opcode(const char *opcode) "<--- %s"
smmuv3_cmdq_consume_out(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "prod:%d, cons:%d, prod_wrap:%d, cons_wrap:%d "
smmuv3_cmdq_consume_error(const char *cmd_name, uint8_t cmd_error) "Error on %s command execution: %d"
smmuv3_update(bool is_empty, uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "q empty:%d prod:%d cons:%d p.wrap:%d p.cons:%d"
smmuv3_update_check_cmd(int error) "cmdq not enabled or error :0x%x"
smmuv3_write_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x(%d)"
smmuv3_write_mmio_idr(uint64_t addr, uint64_t val) "write to RO/Unimpl reg 0x%"PRIx64" val64:0x%"PRIx64
smmuv3_write_mmio_evtq_cons_bef_clear(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "Before clearing interrupt prod:0x%x cons:0x%x prod.w:%d cons.w:%d"
smmuv3_write_mmio_evtq_cons_after_clear(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "after clearing interrupt prod:0x%x cons:0x%x prod.w:%d cons.w:%d"
smmuv3_record_event(const char *type, uint32_t sid) "%s sid=%d"
smmuv3_find_ste(uint16_t sid, uint32_t features, uint16_t sid_split) "SID:0x%x features:0x%x, sid_split:0x%x"
smmuv3_find_ste_2lvl(uint64_t strtab_base, uint64_t l1ptr, int l1_ste_offset, uint64_t l2ptr, int l2_ste_offset, int max_l2_ste) "strtab_base:0x%"PRIx64" l1ptr:0x%"PRIx64" l1_off:0x%x, l2ptr:0x%"PRIx64" l2_off:0x%x max_l2_ste:%d"
smmuv3_get_ste(uint64_t addr) "STE addr: 0x%"PRIx64
smmuv3_translate_bypass(const char *n, uint16_t sid, uint64_t addr, bool is_write) "%s sid=%d bypass iova:0x%"PRIx64" is_write=%d"
smmuv3_translate_in(uint16_t sid, int pci_bus_num, uint64_t strtab_base) "SID:0x%x bus:%d strtab_base:0x%"PRIx64
smmuv3_get_cd(uint64_t addr) "CD addr: 0x%"PRIx64
smmuv3_translate(const char *n, uint16_t sid, uint64_t iova, uint64_t translated, int perm) "%s sid=%d iova=0x%"PRIx64" translated=0x%"PRIx64" perm=0x%x"
smmuv3_decode_cd(uint32_t oas) "oas=%d"
smmuv3_decode_cd_tt(int i, uint32_t tsz, uint64_t ttb, uint32_t granule_sz) "TT[%d]:tsz:%d ttb:0x%"PRIx64" granule_sz:%d"

View File

@ -393,19 +393,26 @@ build_rsdp(GArray *rsdp_table, BIOSLinker *linker, unsigned xsdt_tbl_offset)
}
static void
build_iort(GArray *table_data, BIOSLinker *linker)
build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms)
{
int iort_start = table_data->len;
int nb_nodes, iort_start = table_data->len;
AcpiIortIdMapping *idmap;
AcpiIortItsGroup *its;
AcpiIortTable *iort;
size_t node_size, iort_length;
AcpiIortSmmu3 *smmu;
size_t node_size, iort_length, smmu_offset = 0;
AcpiIortRC *rc;
iort = acpi_data_push(table_data, sizeof(*iort));
if (vms->iommu == VIRT_IOMMU_SMMUV3) {
nb_nodes = 3; /* RC, ITS, SMMUv3 */
} else {
nb_nodes = 2; /* RC, ITS */
}
iort_length = sizeof(*iort);
iort->node_count = cpu_to_le32(2); /* RC and ITS nodes */
iort->node_count = cpu_to_le32(nb_nodes);
iort->node_offset = cpu_to_le32(sizeof(*iort));
/* ITS group node */
@ -418,6 +425,34 @@ build_iort(GArray *table_data, BIOSLinker *linker)
its->its_count = cpu_to_le32(1);
its->identifiers[0] = 0; /* MADT translation_id */
if (vms->iommu == VIRT_IOMMU_SMMUV3) {
int irq = vms->irqmap[VIRT_SMMU];
/* SMMUv3 node */
smmu_offset = iort->node_offset + node_size;
node_size = sizeof(*smmu) + sizeof(*idmap);
iort_length += node_size;
smmu = acpi_data_push(table_data, node_size);
smmu->type = ACPI_IORT_NODE_SMMU_V3;
smmu->length = cpu_to_le16(node_size);
smmu->mapping_count = cpu_to_le32(1);
smmu->mapping_offset = cpu_to_le32(sizeof(*smmu));
smmu->base_address = cpu_to_le64(vms->memmap[VIRT_SMMU].base);
smmu->event_gsiv = cpu_to_le32(irq);
smmu->pri_gsiv = cpu_to_le32(irq + 1);
smmu->gerr_gsiv = cpu_to_le32(irq + 2);
smmu->sync_gsiv = cpu_to_le32(irq + 3);
/* Identity RID mapping covering the whole input RID range */
idmap = &smmu->id_mapping_array[0];
idmap->input_base = 0;
idmap->id_count = cpu_to_le32(0xFFFF);
idmap->output_base = 0;
/* output IORT node is the ITS group node (the first node) */
idmap->output_reference = cpu_to_le32(iort->node_offset);
}
/* Root Complex Node */
node_size = sizeof(*rc) + sizeof(*idmap);
iort_length += node_size;
@ -438,8 +473,14 @@ build_iort(GArray *table_data, BIOSLinker *linker)
idmap->input_base = 0;
idmap->id_count = cpu_to_le32(0xFFFF);
idmap->output_base = 0;
/* output IORT node is the ITS group node (the first node) */
idmap->output_reference = cpu_to_le32(iort->node_offset);
if (vms->iommu == VIRT_IOMMU_SMMUV3) {
/* output IORT node is the smmuv3 node */
idmap->output_reference = cpu_to_le32(smmu_offset);
} else {
/* output IORT node is the ITS group node (the first node) */
idmap->output_reference = cpu_to_le32(iort->node_offset);
}
iort->length = cpu_to_le32(iort_length);
@ -777,7 +818,7 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables)
if (its_class_name() && !vmc->no_its) {
acpi_add_table(table_offsets, tables_blob);
build_iort(tables_blob, tables->linker);
build_iort(tables_blob, tables->linker, vms);
}
/* XSDT is pointed to by RSDP */

View File

@ -58,6 +58,7 @@
#include "hw/smbios/smbios.h"
#include "qapi/visitor.h"
#include "standard-headers/linux/input.h"
#include "hw/arm/smmuv3.h"
#define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
@ -141,6 +142,7 @@ static const MemMapEntry a15memmap[] = {
[VIRT_FW_CFG] = { 0x09020000, 0x00000018 },
[VIRT_GPIO] = { 0x09030000, 0x00001000 },
[VIRT_SECURE_UART] = { 0x09040000, 0x00001000 },
[VIRT_SMMU] = { 0x09050000, 0x00020000 },
[VIRT_MMIO] = { 0x0a000000, 0x00000200 },
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
[VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
@ -161,6 +163,7 @@ static const int a15irqmap[] = {
[VIRT_SECURE_UART] = 8,
[VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */
[VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */
[VIRT_SMMU] = 74, /* ...to 74 + NUM_SMMU_IRQS - 1 */
[VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1 */
};
@ -942,7 +945,57 @@ static void create_pcie_irq_map(const VirtMachineState *vms,
0x7 /* PCI irq */);
}
static void create_pcie(const VirtMachineState *vms, qemu_irq *pic)
static void create_smmu(const VirtMachineState *vms, qemu_irq *pic,
PCIBus *bus)
{
char *node;
const char compat[] = "arm,smmu-v3";
int irq = vms->irqmap[VIRT_SMMU];
int i;
hwaddr base = vms->memmap[VIRT_SMMU].base;
hwaddr size = vms->memmap[VIRT_SMMU].size;
const char irq_names[] = "eventq\0priq\0cmdq-sync\0gerror";
DeviceState *dev;
if (vms->iommu != VIRT_IOMMU_SMMUV3 || !vms->iommu_phandle) {
return;
}
dev = qdev_create(NULL, "arm-smmuv3");
object_property_set_link(OBJECT(dev), OBJECT(bus), "primary-bus",
&error_abort);
qdev_init_nofail(dev);
sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base);
for (i = 0; i < NUM_SMMU_IRQS; i++) {
sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, pic[irq + i]);
}
node = g_strdup_printf("/smmuv3@%" PRIx64, base);
qemu_fdt_add_subnode(vms->fdt, node);
qemu_fdt_setprop(vms->fdt, node, "compatible", compat, sizeof(compat));
qemu_fdt_setprop_sized_cells(vms->fdt, node, "reg", 2, base, 2, size);
qemu_fdt_setprop_cells(vms->fdt, node, "interrupts",
GIC_FDT_IRQ_TYPE_SPI, irq , GIC_FDT_IRQ_FLAGS_EDGE_LO_HI,
GIC_FDT_IRQ_TYPE_SPI, irq + 1, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI,
GIC_FDT_IRQ_TYPE_SPI, irq + 2, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI,
GIC_FDT_IRQ_TYPE_SPI, irq + 3, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI);
qemu_fdt_setprop(vms->fdt, node, "interrupt-names", irq_names,
sizeof(irq_names));
qemu_fdt_setprop_cell(vms->fdt, node, "clocks", vms->clock_phandle);
qemu_fdt_setprop_string(vms->fdt, node, "clock-names", "apb_pclk");
qemu_fdt_setprop(vms->fdt, node, "dma-coherent", NULL, 0);
qemu_fdt_setprop_cell(vms->fdt, node, "#iommu-cells", 1);
qemu_fdt_setprop_cell(vms->fdt, node, "phandle", vms->iommu_phandle);
g_free(node);
}
static void create_pcie(VirtMachineState *vms, qemu_irq *pic)
{
hwaddr base_mmio = vms->memmap[VIRT_PCIE_MMIO].base;
hwaddr size_mmio = vms->memmap[VIRT_PCIE_MMIO].size;
@ -1023,6 +1076,7 @@ static void create_pcie(const VirtMachineState *vms, qemu_irq *pic)
qemu_fdt_setprop_string(vms->fdt, nodename, "device_type", "pci");
qemu_fdt_setprop_cell(vms->fdt, nodename, "#address-cells", 3);
qemu_fdt_setprop_cell(vms->fdt, nodename, "#size-cells", 2);
qemu_fdt_setprop_cell(vms->fdt, nodename, "linux,pci-domain", 0);
qemu_fdt_setprop_cells(vms->fdt, nodename, "bus-range", 0,
nr_pcie_buses - 1);
qemu_fdt_setprop(vms->fdt, nodename, "dma-coherent", NULL, 0);
@ -1055,6 +1109,15 @@ static void create_pcie(const VirtMachineState *vms, qemu_irq *pic)
qemu_fdt_setprop_cell(vms->fdt, nodename, "#interrupt-cells", 1);
create_pcie_irq_map(vms, vms->gic_phandle, irq, nodename);
if (vms->iommu) {
vms->iommu_phandle = qemu_fdt_alloc_phandle(vms->fdt);
create_smmu(vms, pic, pci->bus);
qemu_fdt_setprop_cells(vms->fdt, nodename, "iommu-map",
0x0, vms->iommu_phandle, 0x0, 0x10000);
}
g_free(nodename);
}
@ -1498,6 +1561,34 @@ static void virt_set_gic_version(Object *obj, const char *value, Error **errp)
}
}
static char *virt_get_iommu(Object *obj, Error **errp)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
switch (vms->iommu) {
case VIRT_IOMMU_NONE:
return g_strdup("none");
case VIRT_IOMMU_SMMUV3:
return g_strdup("smmuv3");
default:
g_assert_not_reached();
}
}
static void virt_set_iommu(Object *obj, const char *value, Error **errp)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
if (!strcmp(value, "smmuv3")) {
vms->iommu = VIRT_IOMMU_SMMUV3;
} else if (!strcmp(value, "none")) {
vms->iommu = VIRT_IOMMU_NONE;
} else {
error_setg(errp, "Invalid iommu value");
error_append_hint(errp, "Valid values are none, smmuv3.\n");
}
}
static CpuInstanceProperties
virt_cpu_index_to_props(MachineState *ms, unsigned cpu_index)
{
@ -1630,6 +1721,14 @@ static void virt_2_12_instance_init(Object *obj)
NULL);
}
/* Default disallows iommu instantiation */
vms->iommu = VIRT_IOMMU_NONE;
object_property_add_str(obj, "iommu", virt_get_iommu, virt_set_iommu, NULL);
object_property_set_description(obj, "iommu",
"Set the IOMMU type. "
"Valid values are none and smmuv3",
NULL);
vms->memmap = a15memmap;
vms->irqmap = a15irqmap;
}

View File

@ -157,6 +157,7 @@ static uint64_t uart_read(void *opaque, hwaddr offset, unsigned size)
r = s->rxbuf;
s->state &= ~R_STATE_RXFULL_MASK;
cmsdk_apb_uart_update(s);
qemu_chr_fe_accept_input(&s->chr);
break;
case A_STATE:
r = s->state;

View File

@ -625,37 +625,33 @@ static uint32_t smc91c111_readb(void *opaque, hwaddr offset)
return 0;
}
static void smc91c111_writew(void *opaque, hwaddr offset,
uint32_t value)
static uint64_t smc91c111_readfn(void *opaque, hwaddr addr, unsigned size)
{
smc91c111_writeb(opaque, offset, value & 0xff);
smc91c111_writeb(opaque, offset + 1, value >> 8);
int i;
uint32_t val = 0;
for (i = 0; i < size; i++) {
val |= smc91c111_readb(opaque, addr + i) << (i * 8);
}
return val;
}
static void smc91c111_writel(void *opaque, hwaddr offset,
uint32_t value)
static void smc91c111_writefn(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
int i = 0;
/* 32-bit writes to offset 0xc only actually write to the bank select
register (offset 0xe) */
if (offset != 0xc)
smc91c111_writew(opaque, offset, value & 0xffff);
smc91c111_writew(opaque, offset + 2, value >> 16);
}
* register (offset 0xe), so skip the first two bytes we would write.
*/
if (addr == 0xc && size == 4) {
i += 2;
}
static uint32_t smc91c111_readw(void *opaque, hwaddr offset)
{
uint32_t val;
val = smc91c111_readb(opaque, offset);
val |= smc91c111_readb(opaque, offset + 1) << 8;
return val;
}
static uint32_t smc91c111_readl(void *opaque, hwaddr offset)
{
uint32_t val;
val = smc91c111_readw(opaque, offset);
val |= smc91c111_readw(opaque, offset + 2) << 16;
return val;
for (; i < size; i++) {
smc91c111_writeb(opaque, addr + i,
extract32(value, i * 8, 8));
}
}
static int smc91c111_can_receive_nc(NetClientState *nc)
@ -747,10 +743,10 @@ static const MemoryRegionOps smc91c111_mem_ops = {
/* The special case for 32 bit writes to 0xc means we can't just
* set .impl.min/max_access_size to 1, unfortunately
*/
.old_mmio = {
.read = { smc91c111_readb, smc91c111_readw, smc91c111_readl, },
.write = { smc91c111_writeb, smc91c111_writew, smc91c111_writel, },
},
.read = smc91c111_readfn,
.write = smc91c111_writefn,
.valid.min_access_size = 1,
.valid.max_access_size = 4,
.endianness = DEVICE_NATIVE_ENDIAN,
};

View File

@ -641,11 +641,43 @@ static void tusb_async_writew(void *opaque, hwaddr addr,
}
}
static uint64_t tusb_async_readfn(void *opaque, hwaddr addr, unsigned size)
{
switch (size) {
case 1:
return tusb_async_readb(opaque, addr);
case 2:
return tusb_async_readh(opaque, addr);
case 4:
return tusb_async_readw(opaque, addr);
default:
g_assert_not_reached();
}
}
static void tusb_async_writefn(void *opaque, hwaddr addr,
uint64_t value, unsigned size)
{
switch (size) {
case 1:
tusb_async_writeb(opaque, addr, value);
break;
case 2:
tusb_async_writeh(opaque, addr, value);
break;
case 4:
tusb_async_writew(opaque, addr, value);
break;
default:
g_assert_not_reached();
}
}
static const MemoryRegionOps tusb_async_ops = {
.old_mmio = {
.read = { tusb_async_readb, tusb_async_readh, tusb_async_readw, },
.write = { tusb_async_writeb, tusb_async_writeh, tusb_async_writew, },
},
.read = tusb_async_readfn,
.write = tusb_async_writefn,
.valid.min_access_size = 1,
.valid.max_access_size = 4,
.endianness = DEVICE_NATIVE_ENDIAN,
};

View File

@ -628,6 +628,21 @@ struct AcpiIortItsGroup {
} QEMU_PACKED;
typedef struct AcpiIortItsGroup AcpiIortItsGroup;
struct AcpiIortSmmu3 {
ACPI_IORT_NODE_HEADER_DEF
uint64_t base_address;
uint32_t flags;
uint32_t reserved2;
uint64_t vatos_address;
uint32_t model;
uint32_t event_gsiv;
uint32_t pri_gsiv;
uint32_t gerr_gsiv;
uint32_t sync_gsiv;
AcpiIortIdMapping id_mapping_array[0];
} QEMU_PACKED;
typedef struct AcpiIortSmmu3 AcpiIortSmmu3;
struct AcpiIortRC {
ACPI_IORT_NODE_HEADER_DEF
AcpiIortMemoryAccess memory_properties;

View File

@ -0,0 +1,145 @@
/*
* 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 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.
*
*/
#ifndef HW_ARM_SMMU_COMMON_H
#define HW_ARM_SMMU_COMMON_H
#include "hw/sysbus.h"
#include "hw/pci/pci.h"
#define SMMU_PCI_BUS_MAX 256
#define SMMU_PCI_DEVFN_MAX 256
#define SMMU_MAX_VA_BITS 48
/*
* Page table walk error types
*/
typedef enum {
SMMU_PTW_ERR_NONE,
SMMU_PTW_ERR_WALK_EABT, /* Translation walk external abort */
SMMU_PTW_ERR_TRANSLATION, /* Translation fault */
SMMU_PTW_ERR_ADDR_SIZE, /* Address Size fault */
SMMU_PTW_ERR_ACCESS, /* Access fault */
SMMU_PTW_ERR_PERMISSION, /* Permission fault */
} SMMUPTWEventType;
typedef struct SMMUPTWEventInfo {
SMMUPTWEventType type;
dma_addr_t addr; /* fetched address that induced an abort, if any */
} SMMUPTWEventInfo;
typedef struct SMMUTransTableInfo {
bool disabled; /* is the translation table disabled? */
uint64_t ttb; /* TT base address */
uint8_t tsz; /* input range, ie. 2^(64 -tsz)*/
uint8_t granule_sz; /* granule page shift */
} SMMUTransTableInfo;
/*
* Generic structure populated by derived SMMU devices
* after decoding the configuration information and used as
* input to the page table walk
*/
typedef struct SMMUTransCfg {
int stage; /* translation stage */
bool aa64; /* arch64 or aarch32 translation table */
bool disabled; /* smmu is disabled */
bool bypassed; /* translation is bypassed */
bool aborted; /* translation is aborted */
uint64_t ttb; /* TT base address */
uint8_t oas; /* output address width */
uint8_t tbi; /* Top Byte Ignore */
uint16_t asid;
SMMUTransTableInfo tt[2];
} SMMUTransCfg;
typedef struct SMMUDevice {
void *smmu;
PCIBus *bus;
int devfn;
IOMMUMemoryRegion 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 alloc */
} SMMUPciBus;
typedef struct SMMUState {
/* <private> */
SysBusDevice dev;
const char *mrtypename;
MemoryRegion iomem;
GHashTable *smmu_pcibus_by_busptr;
GHashTable *configs; /* cache for configuration data */
GHashTable *iotlb;
SMMUPciBus *smmu_pcibus_by_bus_num[SMMU_PCI_BUS_MAX];
PCIBus *pci_bus;
QLIST_HEAD(, SMMUNotifierNode) notifiers_list;
uint8_t bus_num;
PCIBus *primary_bus;
} SMMUState;
typedef struct {
/* <private> */
SysBusDeviceClass parent_class;
/*< public >*/
DeviceRealize parent_realize;
} SMMUBaseClass;
#define TYPE_ARM_SMMU "arm-smmu"
#define ARM_SMMU(obj) OBJECT_CHECK(SMMUState, (obj), TYPE_ARM_SMMU)
#define ARM_SMMU_CLASS(klass) \
OBJECT_CLASS_CHECK(SMMUBaseClass, (klass), TYPE_ARM_SMMU)
#define ARM_SMMU_GET_CLASS(obj) \
OBJECT_GET_CLASS(SMMUBaseClass, (obj), TYPE_ARM_SMMU)
/* Return the SMMUPciBus handle associated to a PCI bus number */
SMMUPciBus *smmu_find_smmu_pcibus(SMMUState *s, uint8_t bus_num);
/* Return the stream ID of an SMMU device */
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 */

87
include/hw/arm/smmuv3.h Normal file
View File

@ -0,0 +1,87 @@
/*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef HW_ARM_SMMUV3_H
#define HW_ARM_SMMUV3_H
#include "hw/arm/smmu-common.h"
#include "hw/registerfields.h"
#define TYPE_SMMUV3_IOMMU_MEMORY_REGION "smmuv3-iommu-memory-region"
typedef struct SMMUQueue {
uint64_t base; /* base register */
uint32_t prod;
uint32_t cons;
uint8_t entry_size;
uint8_t log2size;
} SMMUQueue;
typedef struct SMMUv3State {
SMMUState smmu_state;
uint32_t features;
uint8_t sid_size;
uint8_t sid_split;
uint32_t idr[6];
uint32_t iidr;
uint32_t cr[3];
uint32_t cr0ack;
uint32_t statusr;
uint32_t irq_ctrl;
uint32_t gerror;
uint32_t gerrorn;
uint64_t gerror_irq_cfg0;
uint32_t gerror_irq_cfg1;
uint32_t gerror_irq_cfg2;
uint64_t strtab_base;
uint32_t strtab_base_cfg;
uint64_t eventq_irq_cfg0;
uint32_t eventq_irq_cfg1;
uint32_t eventq_irq_cfg2;
SMMUQueue eventq, cmdq;
qemu_irq irq[4];
} SMMUv3State;
typedef enum {
SMMU_IRQ_EVTQ,
SMMU_IRQ_PRIQ,
SMMU_IRQ_CMD_SYNC,
SMMU_IRQ_GERROR,
} SMMUIrq;
typedef struct {
/*< private >*/
SMMUBaseClass smmu_base_class;
/*< public >*/
DeviceRealize parent_realize;
DeviceReset parent_reset;
} SMMUv3Class;
#define TYPE_ARM_SMMUV3 "arm-smmuv3"
#define ARM_SMMUV3(obj) OBJECT_CHECK(SMMUv3State, (obj), TYPE_ARM_SMMUV3)
#define ARM_SMMUV3_CLASS(klass) \
OBJECT_CLASS_CHECK(SMMUv3Class, (klass), TYPE_ARM_SMMUV3)
#define ARM_SMMUV3_GET_CLASS(obj) \
OBJECT_GET_CLASS(SMMUv3Class, (obj), TYPE_ARM_SMMUV3)
#endif

View File

@ -38,6 +38,7 @@
#define NUM_GICV2M_SPIS 64
#define NUM_VIRTIO_TRANSPORTS 32
#define NUM_SMMU_IRQS 4
#define ARCH_GICV3_MAINT_IRQ 9
@ -59,6 +60,7 @@ enum {
VIRT_GIC_V2M,
VIRT_GIC_ITS,
VIRT_GIC_REDIST,
VIRT_SMMU,
VIRT_UART,
VIRT_MMIO,
VIRT_RTC,
@ -74,6 +76,12 @@ enum {
VIRT_SECURE_MEM,
};
typedef enum VirtIOMMUType {
VIRT_IOMMU_NONE,
VIRT_IOMMU_SMMUV3,
VIRT_IOMMU_VIRTIO,
} VirtIOMMUType;
typedef struct MemMapEntry {
hwaddr base;
hwaddr size;
@ -97,6 +105,7 @@ typedef struct {
bool its;
bool virt;
int32_t gic_version;
VirtIOMMUType iommu;
struct arm_boot_info bootinfo;
const MemMapEntry *memmap;
const int *irqmap;
@ -106,6 +115,7 @@ typedef struct {
uint32_t clock_phandle;
uint32_t gic_phandle;
uint32_t msi_phandle;
uint32_t iommu_phandle;
int psci_conduit;
} VirtMachineState;

View File

@ -5347,7 +5347,7 @@ void register_cp_regs_for_features(ARMCPU *cpu)
for (r = id_cp_reginfo; r->type != ARM_CP_SENTINEL; r++) {
r->access = PL1_RW;
}
id_tlbtr_reginfo.access = PL1_RW;
id_mpuir_reginfo.access = PL1_RW;
id_tlbtr_reginfo.access = PL1_RW;
}
if (arm_feature(env, ARM_FEATURE_V8)) {

View File

@ -20,8 +20,10 @@
#include "sysemu/kvm.h"
#include "kvm_arm.h"
#include "cpu.h"
#include "trace.h"
#include "internals.h"
#include "hw/arm/arm.h"
#include "hw/pci/pci.h"
#include "exec/memattrs.h"
#include "exec/address-spaces.h"
#include "hw/boards.h"
@ -649,7 +651,41 @@ int kvm_arm_vgic_probe(void)
int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
uint64_t address, uint32_t data, PCIDevice *dev)
{
return 0;
AddressSpace *as = pci_device_iommu_address_space(dev);
hwaddr xlat, len, doorbell_gpa;
MemoryRegionSection mrs;
MemoryRegion *mr;
int ret = 1;
if (as == &address_space_memory) {
return 0;
}
/* MSI doorbell address is translated by an IOMMU */
rcu_read_lock();
mr = address_space_translate(as, address, &xlat, &len, true);
if (!mr) {
goto unlock;
}
mrs = memory_region_find(mr, xlat, 1);
if (!mrs.mr) {
goto unlock;
}
doorbell_gpa = mrs.offset_within_address_space;
memory_region_unref(mrs.mr);
route->u.msi.address_lo = doorbell_gpa;
route->u.msi.address_hi = doorbell_gpa >> 32;
trace_kvm_arm_fixup_msi_route(address, doorbell_gpa);
ret = 0;
unlock:
rcu_read_unlock();
return ret;
}
int kvm_arch_add_msi_route_post(struct kvm_irq_routing_entry *route,

View File

@ -8,3 +8,6 @@ arm_gt_tval_write(int timer, uint64_t value) "gt_tval_write: timer %d value 0x%"
arm_gt_ctl_write(int timer, uint64_t value) "gt_ctl_write: timer %d value 0x%" PRIx64
arm_gt_imask_toggle(int timer, int irqstate) "gt_ctl_write: timer %d IMASK toggle, new irqstate %d"
arm_gt_cntvoff_write(uint64_t value) "gt_cntvoff_write: value 0x%" PRIx64
# target/arm/kvm.c
kvm_arm_fixup_msi_route(uint64_t iova, uint64_t gpa) "MSI iova = 0x%"PRIx64" is translated into 0x%"PRIx64

View File

@ -9019,11 +9019,7 @@ static void handle_vec_simd_shri(DisasContext *s, bool is_q, bool is_u,
unallocated_encoding(s);
return;
}
if (size > 3 && !is_q) {
unallocated_encoding(s);
return;
}
tcg_debug_assert(size <= 3);
if (!fp_access_check(s)) {
return;
@ -11477,7 +11473,11 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn)
/* All 64-bit element operations can be shared with scalar 2misc */
int pass;
for (pass = 0; pass < (is_q ? 2 : 1); pass++) {
/* Coverity claims (size == 3 && !is_q) has been eliminated
* from all paths leading to here.
*/
tcg_debug_assert(is_q);
for (pass = 0; pass < 2; pass++) {
TCGv_i64 tcg_op = tcg_temp_new_i64();
TCGv_i64 tcg_res = tcg_temp_new_i64();

View File

@ -10795,8 +10795,23 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn)
/* Coprocessor. */
if (arm_dc_feature(s, ARM_FEATURE_M)) {
/* We don't currently implement M profile FP support,
* so this entire space should give a NOCP fault.
* so this entire space should give a NOCP fault, with
* the exception of the v8M VLLDM and VLSTM insns, which
* must be NOPs in Secure state and UNDEF in Nonsecure state.
*/
if (arm_dc_feature(s, ARM_FEATURE_V8) &&
(insn & 0xffa00f00) == 0xec200a00) {
/* 0b1110_1100_0x1x_xxxx_xxxx_1010_xxxx_xxxx
* - VLLDM, VLSTM
* We choose to UNDEF if the RAZ bits are non-zero.
*/
if (!s->v8m_secure || (insn & 0x0040f0ff)) {
goto illegal_op;
}
/* Just NOP since FP support is not implemented */
break;
}
/* All other insns: NOCP */
gen_exception_insn(s, 4, EXCP_NOCP, syn_uncategorized(),
default_exception_el(s));
break;