hw/arm/smmuv3: Fix SMMU_CMD_CFGI_STE_RANGE handling

If the whole SID range (32b) is invalidated (SMMU_CMD_CFGI_ALL),
@end overflows and we fail to handle the command properly.

Once this gets fixed, the current code really is awkward in the
sense it loops over the whole range instead of removing the
currently cached configs through a hash table lookup.

Fix both the overflow and the lookup.

Signed-off-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20210309102742.30442-7-eric.auger@redhat.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Eric Auger 2021-03-09 11:27:41 +01:00 committed by Peter Maydell
parent 6d9cd115b9
commit 1194140b7f
2 changed files with 25 additions and 14 deletions

View File

@ -104,4 +104,9 @@ typedef struct SMMUIOTLBPageInvInfo {
uint64_t mask;
} SMMUIOTLBPageInvInfo;
typedef struct SMMUSIDRange {
uint32_t start;
uint32_t end;
} SMMUSIDRange;
#endif

View File

@ -32,6 +32,7 @@
#include "hw/arm/smmuv3.h"
#include "smmuv3-internal.h"
#include "smmu-internal.h"
/**
* smmuv3_trigger_irq - pulse @irq if enabled and update
@ -895,6 +896,20 @@ static void smmuv3_s1_range_inval(SMMUState *s, Cmd *cmd)
}
}
static gboolean
smmuv3_invalidate_ste(gpointer key, gpointer value, gpointer user_data)
{
SMMUDevice *sdev = (SMMUDevice *)key;
uint32_t sid = smmu_get_sid(sdev);
SMMUSIDRange *sid_range = (SMMUSIDRange *)user_data;
if (sid < sid_range->start || sid > sid_range->end) {
return false;
}
trace_smmuv3_config_cache_inv(sid);
return true;
}
static int smmuv3_cmdq_consume(SMMUv3State *s)
{
SMMUState *bs = ARM_SMMU(s);
@ -965,27 +980,18 @@ static int smmuv3_cmdq_consume(SMMUv3State *s)
}
case SMMU_CMD_CFGI_STE_RANGE: /* same as SMMU_CMD_CFGI_ALL */
{
uint32_t start = CMD_SID(&cmd), end, i;
uint32_t start = CMD_SID(&cmd);
uint8_t range = CMD_STE_RANGE(&cmd);
uint64_t end = start + (1ULL << (range + 1)) - 1;
SMMUSIDRange sid_range = {start, end};
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);
}
g_hash_table_foreach_remove(bs->configs, smmuv3_invalidate_ste,
&sid_range);
break;
}
case SMMU_CMD_CFGI_CD: