hw/arm/smmuv3: Get prepared for range invalidation
Enhance the smmu_iotlb_inv_iova() helper with range invalidation. This uses the new fields passed in the NH_VA and NH_VAA commands: the size of the range, the level and the granule. As NH_VA and NH_VAA both use those fields, their decoding and handling is factorized in a new smmuv3_s1_range_inval() helper. Signed-off-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 20200728150815.11446-8-eric.auger@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
c0f9ef7037
commit
d52915616c
|
@ -143,15 +143,30 @@ static gboolean smmu_hash_remove_by_asid_iova(gpointer key, gpointer value,
|
||||||
if (info->asid >= 0 && info->asid != SMMU_IOTLB_ASID(iotlb_key)) {
|
if (info->asid >= 0 && info->asid != SMMU_IOTLB_ASID(iotlb_key)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return (info->iova & ~entry->addr_mask) == entry->iova;
|
return ((info->iova & ~entry->addr_mask) == entry->iova) ||
|
||||||
|
((entry->iova & ~info->mask) == info->iova);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void smmu_iotlb_inv_iova(SMMUState *s, int asid, dma_addr_t iova)
|
inline void
|
||||||
|
smmu_iotlb_inv_iova(SMMUState *s, int asid, dma_addr_t iova,
|
||||||
|
uint8_t tg, uint64_t num_pages, uint8_t ttl)
|
||||||
{
|
{
|
||||||
SMMUIOTLBPageInvInfo info = {.asid = asid, .iova = iova};
|
if (ttl && (num_pages == 1)) {
|
||||||
|
SMMUIOTLBKey key = smmu_get_iotlb_key(asid, iova, tg, ttl);
|
||||||
|
|
||||||
trace_smmu_iotlb_inv_iova(asid, iova);
|
g_hash_table_remove(s->iotlb, &key);
|
||||||
g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_asid_iova, &info);
|
} else {
|
||||||
|
/* if tg is not set we use 4KB range invalidation */
|
||||||
|
uint8_t granule = tg ? tg * 2 + 10 : 12;
|
||||||
|
|
||||||
|
SMMUIOTLBPageInvInfo info = {
|
||||||
|
.asid = asid, .iova = iova,
|
||||||
|
.mask = (num_pages * 1 << granule) - 1};
|
||||||
|
|
||||||
|
g_hash_table_foreach_remove(s->iotlb,
|
||||||
|
smmu_hash_remove_by_asid_iova,
|
||||||
|
&info);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid)
|
inline void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid)
|
||||||
|
|
|
@ -298,6 +298,8 @@ enum { /* Command completion notification */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define CMD_TYPE(x) extract32((x)->word[0], 0 , 8)
|
#define CMD_TYPE(x) extract32((x)->word[0], 0 , 8)
|
||||||
|
#define CMD_NUM(x) extract32((x)->word[0], 12 , 5)
|
||||||
|
#define CMD_SCALE(x) extract32((x)->word[0], 20 , 5)
|
||||||
#define CMD_SSEC(x) extract32((x)->word[0], 10, 1)
|
#define CMD_SSEC(x) extract32((x)->word[0], 10, 1)
|
||||||
#define CMD_SSV(x) extract32((x)->word[0], 11, 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_AC(x) extract32((x)->word[0], 12, 1)
|
||||||
|
@ -310,6 +312,8 @@ enum { /* Command completion notification */
|
||||||
#define CMD_RESUME_STAG(x) extract32((x)->word[2], 0 , 16)
|
#define CMD_RESUME_STAG(x) extract32((x)->word[2], 0 , 16)
|
||||||
#define CMD_RESP(x) extract32((x)->word[2], 11, 2)
|
#define CMD_RESP(x) extract32((x)->word[2], 11, 2)
|
||||||
#define CMD_LEAF(x) extract32((x)->word[2], 0 , 1)
|
#define CMD_LEAF(x) extract32((x)->word[2], 0 , 1)
|
||||||
|
#define CMD_TTL(x) extract32((x)->word[2], 8 , 2)
|
||||||
|
#define CMD_TG(x) extract32((x)->word[2], 10, 2)
|
||||||
#define CMD_STE_RANGE(x) extract32((x)->word[2], 0 , 5)
|
#define CMD_STE_RANGE(x) extract32((x)->word[2], 0 , 5)
|
||||||
#define CMD_ADDR(x) ({ \
|
#define CMD_ADDR(x) ({ \
|
||||||
uint64_t high = (uint64_t)(x)->word[3]; \
|
uint64_t high = (uint64_t)(x)->word[3]; \
|
||||||
|
|
|
@ -785,42 +785,49 @@ epilogue:
|
||||||
* @n: notifier to be called
|
* @n: notifier to be called
|
||||||
* @asid: address space ID or negative value if we don't care
|
* @asid: address space ID or negative value if we don't care
|
||||||
* @iova: iova
|
* @iova: iova
|
||||||
|
* @tg: translation granule (if communicated through range invalidation)
|
||||||
|
* @num_pages: number of @granule sized pages (if tg != 0), otherwise 1
|
||||||
*/
|
*/
|
||||||
static void smmuv3_notify_iova(IOMMUMemoryRegion *mr,
|
static void smmuv3_notify_iova(IOMMUMemoryRegion *mr,
|
||||||
IOMMUNotifier *n,
|
IOMMUNotifier *n,
|
||||||
int asid,
|
int asid, dma_addr_t iova,
|
||||||
dma_addr_t iova)
|
uint8_t tg, uint64_t num_pages)
|
||||||
{
|
{
|
||||||
SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu);
|
SMMUDevice *sdev = container_of(mr, SMMUDevice, iommu);
|
||||||
SMMUEventInfo event = {.inval_ste_allowed = true};
|
|
||||||
SMMUTransTableInfo *tt;
|
|
||||||
SMMUTransCfg *cfg;
|
|
||||||
IOMMUTLBEntry entry;
|
IOMMUTLBEntry entry;
|
||||||
|
uint8_t granule = tg;
|
||||||
|
|
||||||
cfg = smmuv3_get_config(sdev, &event);
|
if (!tg) {
|
||||||
if (!cfg) {
|
SMMUEventInfo event = {.inval_ste_allowed = true};
|
||||||
return;
|
SMMUTransCfg *cfg = smmuv3_get_config(sdev, &event);
|
||||||
}
|
SMMUTransTableInfo *tt;
|
||||||
|
|
||||||
if (asid >= 0 && cfg->asid != asid) {
|
if (!cfg) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tt = select_tt(cfg, iova);
|
if (asid >= 0 && cfg->asid != asid) {
|
||||||
if (!tt) {
|
return;
|
||||||
return;
|
}
|
||||||
|
|
||||||
|
tt = select_tt(cfg, iova);
|
||||||
|
if (!tt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
granule = tt->granule_sz;
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.target_as = &address_space_memory;
|
entry.target_as = &address_space_memory;
|
||||||
entry.iova = iova;
|
entry.iova = iova;
|
||||||
entry.addr_mask = (1 << tt->granule_sz) - 1;
|
entry.addr_mask = num_pages * (1 << granule) - 1;
|
||||||
entry.perm = IOMMU_NONE;
|
entry.perm = IOMMU_NONE;
|
||||||
|
|
||||||
memory_region_notify_one(n, &entry);
|
memory_region_notify_one(n, &entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* invalidate an asid/iova tuple in all mr's */
|
/* invalidate an asid/iova range tuple in all mr's */
|
||||||
static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, dma_addr_t iova)
|
static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, dma_addr_t iova,
|
||||||
|
uint8_t tg, uint64_t num_pages)
|
||||||
{
|
{
|
||||||
SMMUDevice *sdev;
|
SMMUDevice *sdev;
|
||||||
|
|
||||||
|
@ -828,28 +835,39 @@ static void smmuv3_inv_notifiers_iova(SMMUState *s, int asid, dma_addr_t iova)
|
||||||
IOMMUMemoryRegion *mr = &sdev->iommu;
|
IOMMUMemoryRegion *mr = &sdev->iommu;
|
||||||
IOMMUNotifier *n;
|
IOMMUNotifier *n;
|
||||||
|
|
||||||
trace_smmuv3_inv_notifiers_iova(mr->parent_obj.name, asid, iova);
|
trace_smmuv3_inv_notifiers_iova(mr->parent_obj.name, asid, iova,
|
||||||
|
tg, num_pages);
|
||||||
|
|
||||||
IOMMU_NOTIFIER_FOREACH(n, mr) {
|
IOMMU_NOTIFIER_FOREACH(n, mr) {
|
||||||
smmuv3_notify_iova(mr, n, asid, iova);
|
smmuv3_notify_iova(mr, n, asid, iova, tg, num_pages);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void smmuv3_s1_range_inval(SMMUState *s, Cmd *cmd)
|
static void smmuv3_s1_range_inval(SMMUState *s, Cmd *cmd)
|
||||||
{
|
{
|
||||||
|
uint8_t scale = 0, num = 0, ttl = 0;
|
||||||
dma_addr_t addr = CMD_ADDR(cmd);
|
dma_addr_t addr = CMD_ADDR(cmd);
|
||||||
uint8_t type = CMD_TYPE(cmd);
|
uint8_t type = CMD_TYPE(cmd);
|
||||||
uint16_t vmid = CMD_VMID(cmd);
|
uint16_t vmid = CMD_VMID(cmd);
|
||||||
bool leaf = CMD_LEAF(cmd);
|
bool leaf = CMD_LEAF(cmd);
|
||||||
|
uint8_t tg = CMD_TG(cmd);
|
||||||
|
hwaddr num_pages = 1;
|
||||||
int asid = -1;
|
int asid = -1;
|
||||||
|
|
||||||
|
if (tg) {
|
||||||
|
scale = CMD_SCALE(cmd);
|
||||||
|
num = CMD_NUM(cmd);
|
||||||
|
ttl = CMD_TTL(cmd);
|
||||||
|
num_pages = (num + 1) * (1 << (scale));
|
||||||
|
}
|
||||||
|
|
||||||
if (type == SMMU_CMD_TLBI_NH_VA) {
|
if (type == SMMU_CMD_TLBI_NH_VA) {
|
||||||
asid = CMD_ASID(cmd);
|
asid = CMD_ASID(cmd);
|
||||||
}
|
}
|
||||||
trace_smmuv3_s1_range_inval(vmid, asid, addr, leaf);
|
trace_smmuv3_s1_range_inval(vmid, asid, addr, tg, num_pages, ttl, leaf);
|
||||||
smmuv3_inv_notifiers_iova(s, asid, addr);
|
smmuv3_inv_notifiers_iova(s, asid, addr, tg, num_pages);
|
||||||
smmu_iotlb_inv_iova(s, asid, addr);
|
smmu_iotlb_inv_iova(s, asid, addr, tg, num_pages, ttl);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int smmuv3_cmdq_consume(SMMUv3State *s)
|
static int smmuv3_cmdq_consume(SMMUv3State *s)
|
||||||
|
|
|
@ -45,11 +45,11 @@ smmuv3_cmdq_cfgi_ste_range(int start, int end) "start=0x%d - end=0x%d"
|
||||||
smmuv3_cmdq_cfgi_cd(uint32_t sid) "streamid = %d"
|
smmuv3_cmdq_cfgi_cd(uint32_t sid) "streamid = %d"
|
||||||
smmuv3_config_cache_hit(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache HIT for sid %d (hits=%d, misses=%d, hit rate=%d)"
|
smmuv3_config_cache_hit(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache HIT for sid %d (hits=%d, misses=%d, hit rate=%d)"
|
||||||
smmuv3_config_cache_miss(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache MISS for sid %d (hits=%d, misses=%d, hit rate=%d)"
|
smmuv3_config_cache_miss(uint32_t sid, uint32_t hits, uint32_t misses, uint32_t perc) "Config cache MISS for sid %d (hits=%d, misses=%d, hit rate=%d)"
|
||||||
smmuv3_s1_range_inval(int vmid, int asid, uint64_t addr, bool leaf) "vmid =%d asid =%d addr=0x%"PRIx64" leaf=%d"
|
smmuv3_s1_range_inval(int vmid, int asid, uint64_t addr, uint8_t tg, uint64_t num_pages, uint8_t ttl, bool leaf) "vmid =%d asid =%d addr=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64" ttl=%d leaf=%d"
|
||||||
smmuv3_cmdq_tlbi_nh(void) ""
|
smmuv3_cmdq_tlbi_nh(void) ""
|
||||||
smmuv3_cmdq_tlbi_nh_asid(uint16_t asid) "asid=%d"
|
smmuv3_cmdq_tlbi_nh_asid(uint16_t asid) "asid=%d"
|
||||||
smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid %d"
|
smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid %d"
|
||||||
smmuv3_notify_flag_add(const char *iommu) "ADD SMMUNotifier node for iommu mr=%s"
|
smmuv3_notify_flag_add(const char *iommu) "ADD SMMUNotifier node for iommu mr=%s"
|
||||||
smmuv3_notify_flag_del(const char *iommu) "DEL SMMUNotifier node for iommu mr=%s"
|
smmuv3_notify_flag_del(const char *iommu) "DEL SMMUNotifier node for iommu mr=%s"
|
||||||
smmuv3_inv_notifiers_iova(const char *name, uint16_t asid, uint64_t iova) "iommu mr=%s asid=%d iova=0x%"PRIx64
|
smmuv3_inv_notifiers_iova(const char *name, uint16_t asid, uint64_t iova, uint8_t tg, uint64_t num_pages) "iommu mr=%s asid=%d iova=0x%"PRIx64" tg=%d num_pages=0x%"PRIx64
|
||||||
|
|
||||||
|
|
|
@ -168,7 +168,8 @@ SMMUIOTLBKey smmu_get_iotlb_key(uint16_t asid, uint64_t iova,
|
||||||
uint8_t tg, uint8_t level);
|
uint8_t tg, uint8_t level);
|
||||||
void smmu_iotlb_inv_all(SMMUState *s);
|
void smmu_iotlb_inv_all(SMMUState *s);
|
||||||
void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid);
|
void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid);
|
||||||
void smmu_iotlb_inv_iova(SMMUState *s, int asid, dma_addr_t iova);
|
void smmu_iotlb_inv_iova(SMMUState *s, int asid, dma_addr_t iova,
|
||||||
|
uint8_t tg, uint64_t num_pages, uint8_t ttl);
|
||||||
|
|
||||||
/* Unmap the range of all the notifiers registered to any IOMMU mr */
|
/* Unmap the range of all the notifiers registered to any IOMMU mr */
|
||||||
void smmu_inv_notifiers_all(SMMUState *s);
|
void smmu_inv_notifiers_all(SMMUState *s);
|
||||||
|
|
Loading…
Reference in a new issue