hw/arm/smmuv3: Skeleton
This patch implements a skeleton for the smmuv3 device. Datatypes and register definitions are introduced. The MMIO region, the interrupts and the queue are initialized. Only the MMIO read operation is implemented here. Signed-off-by: Prem Mallappa <prem.mallappa@broadcom.com> Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 1524665762-31355-5-git-send-email-eric.auger@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
93641948d4
commit
10a83cb988
@ -35,4 +35,4 @@ obj-$(CONFIG_MPS2) += mps2-tz.o
|
|||||||
obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o
|
obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o
|
||||||
obj-$(CONFIG_IOTKIT) += iotkit.o
|
obj-$(CONFIG_IOTKIT) += iotkit.o
|
||||||
obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o mcimx7d-sabre.o
|
obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o mcimx7d-sabre.o
|
||||||
obj-$(CONFIG_ARM_SMMUV3) += smmu-common.o
|
obj-$(CONFIG_ARM_SMMUV3) += smmu-common.o smmuv3.o
|
||||||
|
142
hw/arm/smmuv3-internal.h
Normal file
142
hw/arm/smmuv3-internal.h
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* 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)
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
366
hw/arm/smmuv3.c
Normal file
366
hw/arm/smmuv3.c
Normal file
@ -0,0 +1,366 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "hw/boards.h"
|
||||||
|
#include "sysemu/sysemu.h"
|
||||||
|
#include "hw/sysbus.h"
|
||||||
|
#include "hw/qdev-core.h"
|
||||||
|
#include "hw/pci/pci.h"
|
||||||
|
#include "exec/address-spaces.h"
|
||||||
|
#include "trace.h"
|
||||||
|
#include "qemu/log.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
|
||||||
|
#include "hw/arm/smmuv3.h"
|
||||||
|
#include "smmuv3-internal.h"
|
||||||
|
|
||||||
|
static void smmuv3_init_regs(SMMUv3State *s)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* IDR0: stage1 only, AArch64 only, coherent access, 16b ASID,
|
||||||
|
* multi-level stream table
|
||||||
|
*/
|
||||||
|
s->idr[0] = FIELD_DP32(s->idr[0], IDR0, S1P, 1); /* stage 1 supported */
|
||||||
|
s->idr[0] = FIELD_DP32(s->idr[0], IDR0, TTF, 2); /* AArch64 PTW only */
|
||||||
|
s->idr[0] = FIELD_DP32(s->idr[0], IDR0, COHACC, 1); /* IO coherent */
|
||||||
|
s->idr[0] = FIELD_DP32(s->idr[0], IDR0, ASID16, 1); /* 16-bit ASID */
|
||||||
|
s->idr[0] = FIELD_DP32(s->idr[0], IDR0, TTENDIAN, 2); /* little endian */
|
||||||
|
s->idr[0] = FIELD_DP32(s->idr[0], IDR0, STALL_MODEL, 1); /* No stall */
|
||||||
|
/* terminated transaction will always be aborted/error returned */
|
||||||
|
s->idr[0] = FIELD_DP32(s->idr[0], IDR0, TERM_MODEL, 1);
|
||||||
|
/* 2-level stream table supported */
|
||||||
|
s->idr[0] = FIELD_DP32(s->idr[0], IDR0, STLEVEL, 1);
|
||||||
|
|
||||||
|
s->idr[1] = FIELD_DP32(s->idr[1], IDR1, SIDSIZE, SMMU_IDR1_SIDSIZE);
|
||||||
|
s->idr[1] = FIELD_DP32(s->idr[1], IDR1, EVENTQS, SMMU_EVENTQS);
|
||||||
|
s->idr[1] = FIELD_DP32(s->idr[1], IDR1, CMDQS, SMMU_CMDQS);
|
||||||
|
|
||||||
|
/* 4K and 64K granule support */
|
||||||
|
s->idr[5] = FIELD_DP32(s->idr[5], IDR5, GRAN4K, 1);
|
||||||
|
s->idr[5] = FIELD_DP32(s->idr[5], IDR5, GRAN64K, 1);
|
||||||
|
s->idr[5] = FIELD_DP32(s->idr[5], IDR5, OAS, SMMU_IDR5_OAS); /* 44 bits */
|
||||||
|
|
||||||
|
s->cmdq.base = deposit64(s->cmdq.base, 0, 5, SMMU_CMDQS);
|
||||||
|
s->cmdq.prod = 0;
|
||||||
|
s->cmdq.cons = 0;
|
||||||
|
s->cmdq.entry_size = sizeof(struct Cmd);
|
||||||
|
s->eventq.base = deposit64(s->eventq.base, 0, 5, SMMU_EVENTQS);
|
||||||
|
s->eventq.prod = 0;
|
||||||
|
s->eventq.cons = 0;
|
||||||
|
s->eventq.entry_size = sizeof(struct Evt);
|
||||||
|
|
||||||
|
s->features = 0;
|
||||||
|
s->sid_split = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MemTxResult smmu_write_mmio(void *opaque, hwaddr offset, uint64_t data,
|
||||||
|
unsigned size, MemTxAttrs attrs)
|
||||||
|
{
|
||||||
|
/* not yet implemented */
|
||||||
|
return MEMTX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MemTxResult smmu_readll(SMMUv3State *s, hwaddr offset,
|
||||||
|
uint64_t *data, MemTxAttrs attrs)
|
||||||
|
{
|
||||||
|
switch (offset) {
|
||||||
|
case A_GERROR_IRQ_CFG0:
|
||||||
|
*data = s->gerror_irq_cfg0;
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_STRTAB_BASE:
|
||||||
|
*data = s->strtab_base;
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_CMDQ_BASE:
|
||||||
|
*data = s->cmdq.base;
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_EVENTQ_BASE:
|
||||||
|
*data = s->eventq.base;
|
||||||
|
return MEMTX_OK;
|
||||||
|
default:
|
||||||
|
*data = 0;
|
||||||
|
qemu_log_mask(LOG_UNIMP,
|
||||||
|
"%s Unexpected 64-bit access to 0x%"PRIx64" (RAZ)\n",
|
||||||
|
__func__, offset);
|
||||||
|
return MEMTX_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static MemTxResult smmu_readl(SMMUv3State *s, hwaddr offset,
|
||||||
|
uint64_t *data, MemTxAttrs attrs)
|
||||||
|
{
|
||||||
|
switch (offset) {
|
||||||
|
case A_IDREGS ... A_IDREGS + 0x1f:
|
||||||
|
*data = smmuv3_idreg(offset - A_IDREGS);
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_IDR0 ... A_IDR5:
|
||||||
|
*data = s->idr[(offset - A_IDR0) / 4];
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_IIDR:
|
||||||
|
*data = s->iidr;
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_CR0:
|
||||||
|
*data = s->cr[0];
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_CR0ACK:
|
||||||
|
*data = s->cr0ack;
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_CR1:
|
||||||
|
*data = s->cr[1];
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_CR2:
|
||||||
|
*data = s->cr[2];
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_STATUSR:
|
||||||
|
*data = s->statusr;
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_IRQ_CTRL:
|
||||||
|
case A_IRQ_CTRL_ACK:
|
||||||
|
*data = s->irq_ctrl;
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_GERROR:
|
||||||
|
*data = s->gerror;
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_GERRORN:
|
||||||
|
*data = s->gerrorn;
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_GERROR_IRQ_CFG0: /* 64b */
|
||||||
|
*data = extract64(s->gerror_irq_cfg0, 0, 32);
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_GERROR_IRQ_CFG0 + 4:
|
||||||
|
*data = extract64(s->gerror_irq_cfg0, 32, 32);
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_GERROR_IRQ_CFG1:
|
||||||
|
*data = s->gerror_irq_cfg1;
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_GERROR_IRQ_CFG2:
|
||||||
|
*data = s->gerror_irq_cfg2;
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_STRTAB_BASE: /* 64b */
|
||||||
|
*data = extract64(s->strtab_base, 0, 32);
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_STRTAB_BASE + 4: /* 64b */
|
||||||
|
*data = extract64(s->strtab_base, 32, 32);
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_STRTAB_BASE_CFG:
|
||||||
|
*data = s->strtab_base_cfg;
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_CMDQ_BASE: /* 64b */
|
||||||
|
*data = extract64(s->cmdq.base, 0, 32);
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_CMDQ_BASE + 4:
|
||||||
|
*data = extract64(s->cmdq.base, 32, 32);
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_CMDQ_PROD:
|
||||||
|
*data = s->cmdq.prod;
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_CMDQ_CONS:
|
||||||
|
*data = s->cmdq.cons;
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_EVENTQ_BASE: /* 64b */
|
||||||
|
*data = extract64(s->eventq.base, 0, 32);
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_EVENTQ_BASE + 4: /* 64b */
|
||||||
|
*data = extract64(s->eventq.base, 32, 32);
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_EVENTQ_PROD:
|
||||||
|
*data = s->eventq.prod;
|
||||||
|
return MEMTX_OK;
|
||||||
|
case A_EVENTQ_CONS:
|
||||||
|
*data = s->eventq.cons;
|
||||||
|
return MEMTX_OK;
|
||||||
|
default:
|
||||||
|
*data = 0;
|
||||||
|
qemu_log_mask(LOG_UNIMP,
|
||||||
|
"%s unhandled 32-bit access at 0x%"PRIx64" (RAZ)\n",
|
||||||
|
__func__, offset);
|
||||||
|
return MEMTX_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static MemTxResult smmu_read_mmio(void *opaque, hwaddr offset, uint64_t *data,
|
||||||
|
unsigned size, MemTxAttrs attrs)
|
||||||
|
{
|
||||||
|
SMMUState *sys = opaque;
|
||||||
|
SMMUv3State *s = ARM_SMMUV3(sys);
|
||||||
|
MemTxResult r;
|
||||||
|
|
||||||
|
/* CONSTRAINED UNPREDICTABLE choice to have page0/1 be exact aliases */
|
||||||
|
offset &= ~0x10000;
|
||||||
|
|
||||||
|
switch (size) {
|
||||||
|
case 8:
|
||||||
|
r = smmu_readll(s, offset, data, attrs);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
r = smmu_readl(s, offset, data, attrs);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
r = MEMTX_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace_smmuv3_read_mmio(offset, *data, size, r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionOps smmu_mem_ops = {
|
||||||
|
.read_with_attrs = smmu_read_mmio,
|
||||||
|
.write_with_attrs = smmu_write_mmio,
|
||||||
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
||||||
|
.valid = {
|
||||||
|
.min_access_size = 4,
|
||||||
|
.max_access_size = 8,
|
||||||
|
},
|
||||||
|
.impl = {
|
||||||
|
.min_access_size = 4,
|
||||||
|
.max_access_size = 8,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void smmu_init_irq(SMMUv3State *s, SysBusDevice *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(s->irq); i++) {
|
||||||
|
sysbus_init_irq(dev, &s->irq[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void smmu_reset(DeviceState *dev)
|
||||||
|
{
|
||||||
|
SMMUv3State *s = ARM_SMMUV3(dev);
|
||||||
|
SMMUv3Class *c = ARM_SMMUV3_GET_CLASS(s);
|
||||||
|
|
||||||
|
c->parent_reset(dev);
|
||||||
|
|
||||||
|
smmuv3_init_regs(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void smmu_realize(DeviceState *d, Error **errp)
|
||||||
|
{
|
||||||
|
SMMUState *sys = ARM_SMMU(d);
|
||||||
|
SMMUv3State *s = ARM_SMMUV3(sys);
|
||||||
|
SMMUv3Class *c = ARM_SMMUV3_GET_CLASS(s);
|
||||||
|
SysBusDevice *dev = SYS_BUS_DEVICE(d);
|
||||||
|
Error *local_err = NULL;
|
||||||
|
|
||||||
|
c->parent_realize(d, &local_err);
|
||||||
|
if (local_err) {
|
||||||
|
error_propagate(errp, local_err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memory_region_init_io(&sys->iomem, OBJECT(s),
|
||||||
|
&smmu_mem_ops, sys, TYPE_ARM_SMMUV3, 0x20000);
|
||||||
|
|
||||||
|
sys->mrtypename = TYPE_SMMUV3_IOMMU_MEMORY_REGION;
|
||||||
|
|
||||||
|
sysbus_init_mmio(dev, &sys->iomem);
|
||||||
|
|
||||||
|
smmu_init_irq(s, dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_smmuv3_queue = {
|
||||||
|
.name = "smmuv3_queue",
|
||||||
|
.version_id = 1,
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT64(base, SMMUQueue),
|
||||||
|
VMSTATE_UINT32(prod, SMMUQueue),
|
||||||
|
VMSTATE_UINT32(cons, SMMUQueue),
|
||||||
|
VMSTATE_UINT8(log2size, SMMUQueue),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_smmuv3 = {
|
||||||
|
.name = "smmuv3",
|
||||||
|
.version_id = 1,
|
||||||
|
.minimum_version_id = 1,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_UINT32(features, SMMUv3State),
|
||||||
|
VMSTATE_UINT8(sid_size, SMMUv3State),
|
||||||
|
VMSTATE_UINT8(sid_split, SMMUv3State),
|
||||||
|
|
||||||
|
VMSTATE_UINT32_ARRAY(cr, SMMUv3State, 3),
|
||||||
|
VMSTATE_UINT32(cr0ack, SMMUv3State),
|
||||||
|
VMSTATE_UINT32(statusr, SMMUv3State),
|
||||||
|
VMSTATE_UINT32(irq_ctrl, SMMUv3State),
|
||||||
|
VMSTATE_UINT32(gerror, SMMUv3State),
|
||||||
|
VMSTATE_UINT32(gerrorn, SMMUv3State),
|
||||||
|
VMSTATE_UINT64(gerror_irq_cfg0, SMMUv3State),
|
||||||
|
VMSTATE_UINT32(gerror_irq_cfg1, SMMUv3State),
|
||||||
|
VMSTATE_UINT32(gerror_irq_cfg2, SMMUv3State),
|
||||||
|
VMSTATE_UINT64(strtab_base, SMMUv3State),
|
||||||
|
VMSTATE_UINT32(strtab_base_cfg, SMMUv3State),
|
||||||
|
VMSTATE_UINT64(eventq_irq_cfg0, SMMUv3State),
|
||||||
|
VMSTATE_UINT32(eventq_irq_cfg1, SMMUv3State),
|
||||||
|
VMSTATE_UINT32(eventq_irq_cfg2, SMMUv3State),
|
||||||
|
|
||||||
|
VMSTATE_STRUCT(cmdq, SMMUv3State, 0, vmstate_smmuv3_queue, SMMUQueue),
|
||||||
|
VMSTATE_STRUCT(eventq, SMMUv3State, 0, vmstate_smmuv3_queue, SMMUQueue),
|
||||||
|
|
||||||
|
VMSTATE_END_OF_LIST(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void smmuv3_instance_init(Object *obj)
|
||||||
|
{
|
||||||
|
/* Nothing much to do here as of now */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void smmuv3_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
SMMUv3Class *c = ARM_SMMUV3_CLASS(klass);
|
||||||
|
|
||||||
|
dc->vmsd = &vmstate_smmuv3;
|
||||||
|
device_class_set_parent_reset(dc, smmu_reset, &c->parent_reset);
|
||||||
|
c->parent_realize = dc->realize;
|
||||||
|
dc->realize = smmu_realize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void smmuv3_iommu_memory_region_class_init(ObjectClass *klass,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo smmuv3_type_info = {
|
||||||
|
.name = TYPE_ARM_SMMUV3,
|
||||||
|
.parent = TYPE_ARM_SMMU,
|
||||||
|
.instance_size = sizeof(SMMUv3State),
|
||||||
|
.instance_init = smmuv3_instance_init,
|
||||||
|
.class_size = sizeof(SMMUv3Class),
|
||||||
|
.class_init = smmuv3_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const TypeInfo smmuv3_iommu_memory_region_info = {
|
||||||
|
.parent = TYPE_IOMMU_MEMORY_REGION,
|
||||||
|
.name = TYPE_SMMUV3_IOMMU_MEMORY_REGION,
|
||||||
|
.class_init = smmuv3_iommu_memory_region_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void smmuv3_register_types(void)
|
||||||
|
{
|
||||||
|
type_register(&smmuv3_type_info);
|
||||||
|
type_register(&smmuv3_iommu_memory_region_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
type_init(smmuv3_register_types)
|
||||||
|
|
@ -12,3 +12,6 @@ smmu_ptw_invalid_pte(int stage, int level, uint64_t baseaddr, uint64_t pteaddr,
|
|||||||
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_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_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
|
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)"
|
||||||
|
87
include/hw/arm/smmuv3.h
Normal file
87
include/hw/arm/smmuv3.h
Normal 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
|
Loading…
Reference in New Issue
Block a user