aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/arm/smmuv3-internal.h10
-rw-r--r--hw/arm/smmuv3.c182
-rw-r--r--include/hw/arm/smmu-common.h1
-rw-r--r--include/hw/arm/smmuv3.h3
4 files changed, 186 insertions, 10 deletions
diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h
index 183d5ac..6d1c1ed 100644
--- a/hw/arm/smmuv3-internal.h
+++ b/hw/arm/smmuv3-internal.h
@@ -526,9 +526,13 @@ typedef struct CD {
#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_S2ENDI(x) extract32((x)->word[5], 20, 1)
+#define STE_S2AFFD(x) extract32((x)->word[5], 21, 1)
+#define STE_S2HD(x) extract32((x)->word[5], 23, 1)
+#define STE_S2HA(x) extract32((x)->word[5], 24, 1)
+#define STE_S2S(x) extract32((x)->word[5], 25, 1)
+#define STE_S2R(x) extract32((x)->word[5], 26, 1)
+
#define STE_CTXPTR(x) \
({ \
unsigned long addr; \
diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index 4e90343..989afb1 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -33,6 +33,9 @@
#include "smmuv3-internal.h"
#include "smmu-internal.h"
+#define PTW_RECORD_FAULT(cfg) (((cfg)->stage == 1) ? (cfg)->record_faults : \
+ (cfg)->s2cfg.record_faults)
+
/**
* smmuv3_trigger_irq - pulse @irq if enabled and update
* GERROR register in case of GERROR interrupt
@@ -329,11 +332,142 @@ static int smmu_get_cd(SMMUv3State *s, STE *ste, uint32_t ssid,
return 0;
}
+/*
+ * Max valid value is 39 when SMMU_IDR3.STT == 0.
+ * In architectures after SMMUv3.0:
+ * - If STE.S2TG selects a 4KB or 16KB granule, the minimum valid value for this
+ * field is MAX(16, 64-IAS)
+ * - If STE.S2TG selects a 64KB granule, the minimum valid value for this field
+ * is (64-IAS).
+ * As we only support AA64, IAS = OAS.
+ */
+static bool s2t0sz_valid(SMMUTransCfg *cfg)
+{
+ if (cfg->s2cfg.tsz > 39) {
+ return false;
+ }
+
+ if (cfg->s2cfg.granule_sz == 16) {
+ return (cfg->s2cfg.tsz >= 64 - oas2bits(SMMU_IDR5_OAS));
+ }
+
+ return (cfg->s2cfg.tsz >= MAX(64 - oas2bits(SMMU_IDR5_OAS), 16));
+}
+
+/*
+ * Return true if s2 page table config is valid.
+ * This checks with the configured start level, ias_bits and granularity we can
+ * have a valid page table as described in ARM ARM D8.2 Translation process.
+ * The idea here is to see for the highest possible number of IPA bits, how
+ * many concatenated tables we would need, if it is more than 16, then this is
+ * not possible.
+ */
+static bool s2_pgtable_config_valid(uint8_t sl0, uint8_t t0sz, uint8_t gran)
+{
+ int level = get_start_level(sl0, gran);
+ uint64_t ipa_bits = 64 - t0sz;
+ uint64_t max_ipa = (1ULL << ipa_bits) - 1;
+ int nr_concat = pgd_concat_idx(level, gran, max_ipa) + 1;
+
+ return nr_concat <= VMSA_MAX_S2_CONCAT;
+}
+
+static int decode_ste_s2_cfg(SMMUTransCfg *cfg, STE *ste)
+{
+ cfg->stage = 2;
+
+ if (STE_S2AA64(ste) == 0x0) {
+ qemu_log_mask(LOG_UNIMP,
+ "SMMUv3 AArch32 tables not supported\n");
+ g_assert_not_reached();
+ }
+
+ switch (STE_S2TG(ste)) {
+ case 0x0: /* 4KB */
+ cfg->s2cfg.granule_sz = 12;
+ break;
+ case 0x1: /* 64KB */
+ cfg->s2cfg.granule_sz = 16;
+ break;
+ case 0x2: /* 16KB */
+ cfg->s2cfg.granule_sz = 14;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "SMMUv3 bad STE S2TG: %x\n", STE_S2TG(ste));
+ goto bad_ste;
+ }
+
+ cfg->s2cfg.vttb = STE_S2TTB(ste);
+
+ cfg->s2cfg.sl0 = STE_S2SL0(ste);
+ /* FEAT_TTST not supported. */
+ if (cfg->s2cfg.sl0 == 0x3) {
+ qemu_log_mask(LOG_UNIMP, "SMMUv3 S2SL0 = 0x3 has no meaning!\n");
+ goto bad_ste;
+ }
+
+ /* For AA64, The effective S2PS size is capped to the OAS. */
+ cfg->s2cfg.eff_ps = oas2bits(MIN(STE_S2PS(ste), SMMU_IDR5_OAS));
+ /*
+ * It is ILLEGAL for the address in S2TTB to be outside the range
+ * described by the effective S2PS value.
+ */
+ if (cfg->s2cfg.vttb & ~(MAKE_64BIT_MASK(0, cfg->s2cfg.eff_ps))) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "SMMUv3 S2TTB too large 0x%" PRIx64
+ ", effective PS %d bits\n",
+ cfg->s2cfg.vttb, cfg->s2cfg.eff_ps);
+ goto bad_ste;
+ }
+
+ cfg->s2cfg.tsz = STE_S2T0SZ(ste);
+
+ if (!s2t0sz_valid(cfg)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "SMMUv3 bad STE S2T0SZ = %d\n",
+ cfg->s2cfg.tsz);
+ goto bad_ste;
+ }
+
+ if (!s2_pgtable_config_valid(cfg->s2cfg.sl0, cfg->s2cfg.tsz,
+ cfg->s2cfg.granule_sz)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "SMMUv3 STE stage 2 config not valid!\n");
+ goto bad_ste;
+ }
+
+ /* Only LE supported(IDR0.TTENDIAN). */
+ if (STE_S2ENDI(ste)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "SMMUv3 STE_S2ENDI only supports LE!\n");
+ goto bad_ste;
+ }
+
+ cfg->s2cfg.affd = STE_S2AFFD(ste);
+
+ cfg->s2cfg.record_faults = STE_S2R(ste);
+ /* As stall is not supported. */
+ if (STE_S2S(ste)) {
+ qemu_log_mask(LOG_UNIMP, "SMMUv3 Stall not implemented!\n");
+ goto bad_ste;
+ }
+
+ /* This is still here as stage 2 has not been fully enabled yet. */
+ qemu_log_mask(LOG_UNIMP, "SMMUv3 does not support stage 2 yet\n");
+ goto bad_ste;
+
+ return 0;
+
+bad_ste:
+ return -EINVAL;
+}
+
/* Returns < 0 in case of invalid STE, 0 otherwise */
static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg,
STE *ste, SMMUEventInfo *event)
{
uint32_t config;
+ int ret;
if (!STE_VALID(ste)) {
if (!event->inval_ste_allowed) {
@@ -354,11 +488,39 @@ static int decode_ste(SMMUv3State *s, SMMUTransCfg *cfg,
return 0;
}
- if (STE_CFG_S2_ENABLED(config)) {
- qemu_log_mask(LOG_UNIMP, "SMMUv3 does not support stage 2 yet\n");
+ /*
+ * If a stage is enabled in SW while not advertised, throw bad ste
+ * according to user manual(IHI0070E) "5.2 Stream Table Entry".
+ */
+ if (!STAGE1_SUPPORTED(s) && STE_CFG_S1_ENABLED(config)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "SMMUv3 S1 used but not supported.\n");
+ goto bad_ste;
+ }
+ if (!STAGE2_SUPPORTED(s) && STE_CFG_S2_ENABLED(config)) {
+ qemu_log_mask(LOG_GUEST_ERROR, "SMMUv3 S2 used but not supported.\n");
goto bad_ste;
}
+ if (STAGE2_SUPPORTED(s)) {
+ /* VMID is considered even if s2 is disabled. */
+ cfg->s2cfg.vmid = STE_S2VMID(ste);
+ } else {
+ /* Default to -1 */
+ cfg->s2cfg.vmid = -1;
+ }
+
+ if (STE_CFG_S2_ENABLED(config)) {
+ /*
+ * Stage-1 OAS defaults to OAS even if not enabled as it would be used
+ * in input address check for stage-2.
+ */
+ cfg->oas = oas2bits(SMMU_IDR5_OAS);
+ ret = decode_ste_s2_cfg(cfg, ste);
+ if (ret) {
+ goto bad_ste;
+ }
+ }
+
if (STE_S1CDMAX(ste) != 0) {
qemu_log_mask(LOG_UNIMP,
"SMMUv3 does not support multiple context descriptors yet\n");
@@ -702,7 +864,13 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
if (cached_entry) {
if ((flag & IOMMU_WO) && !(cached_entry->entry.perm & IOMMU_WO)) {
status = SMMU_TRANS_ERROR;
- if (cfg->record_faults) {
+ /*
+ * We know that the TLB only contains either stage-1 or stage-2 as
+ * nesting is not supported. So it is sufficient to check the
+ * translation stage to know the TLB stage for now.
+ */
+ event.u.f_walk_eabt.s2 = (cfg->stage == 2);
+ if (PTW_RECORD_FAULT(cfg)) {
event.type = SMMU_EVT_F_PERMISSION;
event.u.f_permission.addr = addr;
event.u.f_permission.rnw = flag & 0x1;
@@ -728,28 +896,28 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
event.u.f_walk_eabt.addr2 = ptw_info.addr;
break;
case SMMU_PTW_ERR_TRANSLATION:
- if (cfg->record_faults) {
+ if (PTW_RECORD_FAULT(cfg)) {
event.type = SMMU_EVT_F_TRANSLATION;
event.u.f_translation.addr = addr;
event.u.f_translation.rnw = flag & 0x1;
}
break;
case SMMU_PTW_ERR_ADDR_SIZE:
- if (cfg->record_faults) {
+ if (PTW_RECORD_FAULT(cfg)) {
event.type = SMMU_EVT_F_ADDR_SIZE;
event.u.f_addr_size.addr = addr;
event.u.f_addr_size.rnw = flag & 0x1;
}
break;
case SMMU_PTW_ERR_ACCESS:
- if (cfg->record_faults) {
+ if (PTW_RECORD_FAULT(cfg)) {
event.type = SMMU_EVT_F_ACCESS;
event.u.f_access.addr = addr;
event.u.f_access.rnw = flag & 0x1;
}
break;
case SMMU_PTW_ERR_PERMISSION:
- if (cfg->record_faults) {
+ if (PTW_RECORD_FAULT(cfg)) {
event.type = SMMU_EVT_F_PERMISSION;
event.u.f_permission.addr = addr;
event.u.f_permission.rnw = flag & 0x1;
diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h
index 97cea8e..4f1405d 100644
--- a/include/hw/arm/smmu-common.h
+++ b/include/hw/arm/smmu-common.h
@@ -29,6 +29,7 @@
/* VMSAv8-64 Translation constants and functions */
#define VMSA_LEVELS 4
+#define VMSA_MAX_S2_CONCAT 16
#define VMSA_STRIDE(gran) ((gran) - VMSA_LEVELS + 1)
#define VMSA_BIT_LVL(isz, strd, lvl) ((isz) - (strd) * \
diff --git a/include/hw/arm/smmuv3.h b/include/hw/arm/smmuv3.h
index a0c0264..6031d7d 100644
--- a/include/hw/arm/smmuv3.h
+++ b/include/hw/arm/smmuv3.h
@@ -83,4 +83,7 @@ struct SMMUv3Class {
#define TYPE_ARM_SMMUV3 "arm-smmuv3"
OBJECT_DECLARE_TYPE(SMMUv3State, SMMUv3Class, ARM_SMMUV3)
+#define STAGE1_SUPPORTED(s) FIELD_EX32(s->idr[0], IDR0, S1P)
+#define STAGE2_SUPPORTED(s) FIELD_EX32(s->idr[0], IDR0, S2P)
+
#endif