aboutsummaryrefslogtreecommitdiff
path: root/hw/arm/smmuv3.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/arm/smmuv3.c')
-rw-r--r--hw/arm/smmuv3.c135
1 files changed, 130 insertions, 5 deletions
diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index 70b8f295aa..df704c26f8 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -544,6 +544,58 @@ static int smmuv3_decode_config(IOMMUMemoryRegion *mr, SMMUTransCfg *cfg,
return decode_cd(cfg, &cd, event);
}
+/**
+ * smmuv3_get_config - Look up for a cached copy of configuration data for
+ * @sdev and on cache miss performs a configuration structure decoding from
+ * guest RAM.
+ *
+ * @sdev: SMMUDevice handle
+ * @event: output event info
+ *
+ * The configuration cache contains data resulting from both STE and CD
+ * decoding under the form of an SMMUTransCfg struct. The hash table is indexed
+ * by the SMMUDevice handle.
+ */
+static SMMUTransCfg *smmuv3_get_config(SMMUDevice *sdev, SMMUEventInfo *event)
+{
+ SMMUv3State *s = sdev->smmu;
+ SMMUState *bc = &s->smmu_state;
+ SMMUTransCfg *cfg;
+
+ cfg = g_hash_table_lookup(bc->configs, sdev);
+ if (cfg) {
+ sdev->cfg_cache_hits++;
+ trace_smmuv3_config_cache_hit(smmu_get_sid(sdev),
+ sdev->cfg_cache_hits, sdev->cfg_cache_misses,
+ 100 * sdev->cfg_cache_hits /
+ (sdev->cfg_cache_hits + sdev->cfg_cache_misses));
+ } else {
+ sdev->cfg_cache_misses++;
+ trace_smmuv3_config_cache_miss(smmu_get_sid(sdev),
+ sdev->cfg_cache_hits, sdev->cfg_cache_misses,
+ 100 * sdev->cfg_cache_hits /
+ (sdev->cfg_cache_hits + sdev->cfg_cache_misses));
+ cfg = g_new0(SMMUTransCfg, 1);
+
+ if (!smmuv3_decode_config(&sdev->iommu, cfg, event)) {
+ g_hash_table_insert(bc->configs, sdev, cfg);
+ } else {
+ g_free(cfg);
+ cfg = NULL;
+ }
+ }
+ return cfg;
+}
+
+static void smmuv3_flush_config(SMMUDevice *sdev)
+{
+ SMMUv3State *s = sdev->smmu;
+ SMMUState *bc = &s->smmu_state;
+
+ trace_smmuv3_config_cache_inv(smmu_get_sid(sdev));
+ g_hash_table_remove(bc->configs, sdev);
+}
+
static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
IOMMUAccessFlags flag, int iommu_idx)
{
@@ -553,7 +605,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
SMMUEventInfo event = {.type = SMMU_EVT_NONE, .sid = sid};
SMMUPTWEventInfo ptw_info = {};
SMMUTranslationStatus status;
- SMMUTransCfg cfg = {};
+ SMMUTransCfg *cfg = NULL;
IOMMUTLBEntry entry = {
.target_as = &address_space_memory,
.iova = addr,
@@ -562,27 +614,30 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
.perm = IOMMU_NONE,
};
+ qemu_mutex_lock(&s->mutex);
+
if (!smmu_enabled(s)) {
status = SMMU_TRANS_DISABLE;
goto epilogue;
}
- if (smmuv3_decode_config(mr, &cfg, &event)) {
+ cfg = smmuv3_get_config(sdev, &event);
+ if (!cfg) {
status = SMMU_TRANS_ERROR;
goto epilogue;
}
- if (cfg.aborted) {
+ if (cfg->aborted) {
status = SMMU_TRANS_ABORT;
goto epilogue;
}
- if (cfg.bypassed) {
+ if (cfg->bypassed) {
status = SMMU_TRANS_BYPASS;
goto epilogue;
}
- if (smmu_ptw(&cfg, addr, flag, &entry, &ptw_info)) {
+ if (smmu_ptw(cfg, addr, flag, &entry, &ptw_info)) {
switch (ptw_info.type) {
case SMMU_PTW_ERR_WALK_EABT:
event.type = SMMU_EVT_F_WALK_EABT;
@@ -628,6 +683,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
}
epilogue:
+ qemu_mutex_unlock(&s->mutex);
switch (status) {
case SMMU_TRANS_SUCCESS:
entry.perm = flag;
@@ -664,6 +720,7 @@ epilogue:
static int smmuv3_cmdq_consume(SMMUv3State *s)
{
+ SMMUState *bs = ARM_SMMU(s);
SMMUCmdError cmd_error = SMMU_CERROR_NONE;
SMMUQueue *q = &s->cmdq;
SMMUCommandType type = 0;
@@ -698,6 +755,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s)
trace_smmuv3_cmdq_opcode(smmu_cmd_string(type));
+ qemu_mutex_lock(&s->mutex);
switch (type) {
case SMMU_CMD_SYNC:
if (CMD_SYNC_CS(&cmd) & CMD_SYNC_SIG_IRQ) {
@@ -706,10 +764,74 @@ static int smmuv3_cmdq_consume(SMMUv3State *s)
break;
case SMMU_CMD_PREFETCH_CONFIG:
case SMMU_CMD_PREFETCH_ADDR:
+ break;
case SMMU_CMD_CFGI_STE:
+ {
+ uint32_t sid = CMD_SID(&cmd);
+ IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, sid);
+ SMMUDevice *sdev;
+
+ if (CMD_SSEC(&cmd)) {
+ cmd_error = SMMU_CERROR_ILL;
+ break;
+ }
+
+ if (!mr) {
+ break;
+ }
+
+ trace_smmuv3_cmdq_cfgi_ste(sid);
+ sdev = container_of(mr, SMMUDevice, iommu);
+ smmuv3_flush_config(sdev);
+
+ break;
+ }
case SMMU_CMD_CFGI_STE_RANGE: /* same as SMMU_CMD_CFGI_ALL */
+ {
+ uint32_t start = CMD_SID(&cmd), end, i;
+ uint8_t range = CMD_STE_RANGE(&cmd);
+
+ if (CMD_SSEC(&cmd)) {
+ cmd_error = SMMU_CERROR_ILL;
+ break;
+ }
+
+ end = start + (1 << (range + 1)) - 1;
+ trace_smmuv3_cmdq_cfgi_ste_range(start, end);
+
+ for (i = start; i <= end; i++) {
+ IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, i);
+ SMMUDevice *sdev;
+
+ if (!mr) {
+ continue;
+ }
+ sdev = container_of(mr, SMMUDevice, iommu);
+ smmuv3_flush_config(sdev);
+ }
+ break;
+ }
case SMMU_CMD_CFGI_CD:
case SMMU_CMD_CFGI_CD_ALL:
+ {
+ uint32_t sid = CMD_SID(&cmd);
+ IOMMUMemoryRegion *mr = smmu_iommu_mr(bs, sid);
+ SMMUDevice *sdev;
+
+ if (CMD_SSEC(&cmd)) {
+ cmd_error = SMMU_CERROR_ILL;
+ break;
+ }
+
+ if (!mr) {
+ break;
+ }
+
+ trace_smmuv3_cmdq_cfgi_cd(sid);
+ sdev = container_of(mr, SMMUDevice, iommu);
+ smmuv3_flush_config(sdev);
+ break;
+ }
case SMMU_CMD_TLBI_NH_ALL:
case SMMU_CMD_TLBI_NH_ASID:
case SMMU_CMD_TLBI_NH_VA:
@@ -735,6 +857,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s)
"Illegal command type: %d\n", CMD_TYPE(&cmd));
break;
}
+ qemu_mutex_unlock(&s->mutex);
if (cmd_error) {
break;
}
@@ -1114,6 +1237,8 @@ static void smmu_realize(DeviceState *d, Error **errp)
return;
}
+ qemu_mutex_init(&s->mutex);
+
memory_region_init_io(&sys->iomem, OBJECT(s),
&smmu_mem_ops, sys, TYPE_ARM_SMMUV3, 0x20000);