Skip to content

Commit a9d4028

Browse files
nicolincwilldeacon
authored andcommitted
iommu/tegra241-cmdqv: Limit CMDs for VCMDQs of a guest owned VINTF
When VCMDQs are assigned to a VINTF owned by a guest (HYP_OWN bit unset), only TLB and ATC invalidation commands are supported by the VCMDQ HW. So, implement the new cmdq->supports_cmd op to scan the input cmd in order to make sure that it is supported by the selected queue. Note that the guest VM shouldn't have HYP_OWN bit being set regardless of guest kernel driver writing it or not, i.e. the hypervisor running in the host OS should wire this bit to zero when trapping a write access to this VINTF_CONFIG register from a guest kernel. Reviewed-by: Jason Gunthorpe <[email protected]> Signed-off-by: Nicolin Chen <[email protected]> Link: https://lore.kernel.org/r/8160292337059b91271045800e5c62f7295e2c24.1724970714.git.nicolinc@nvidia.com Signed-off-by: Will Deacon <[email protected]>
1 parent f59e854 commit a9d4028

File tree

3 files changed

+51
-14
lines changed

3 files changed

+51
-14
lines changed

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -346,12 +346,13 @@ static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent)
346346
return 0;
347347
}
348348

349-
static struct arm_smmu_cmdq *arm_smmu_get_cmdq(struct arm_smmu_device *smmu)
349+
static struct arm_smmu_cmdq *arm_smmu_get_cmdq(struct arm_smmu_device *smmu,
350+
struct arm_smmu_cmdq_ent *ent)
350351
{
351352
struct arm_smmu_cmdq *cmdq = NULL;
352353

353354
if (smmu->impl_ops && smmu->impl_ops->get_secondary_cmdq)
354-
cmdq = smmu->impl_ops->get_secondary_cmdq(smmu);
355+
cmdq = smmu->impl_ops->get_secondary_cmdq(smmu, ent);
355356

356357
return cmdq ?: &smmu->cmdq;
357358
}
@@ -897,7 +898,7 @@ static int __arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu,
897898
}
898899

899900
return arm_smmu_cmdq_issue_cmdlist(
900-
smmu, arm_smmu_get_cmdq(smmu), cmd, 1, sync);
901+
smmu, arm_smmu_get_cmdq(smmu, ent), cmd, 1, sync);
901902
}
902903

903904
static int arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu,
@@ -913,10 +914,11 @@ static int arm_smmu_cmdq_issue_cmd_with_sync(struct arm_smmu_device *smmu,
913914
}
914915

915916
static void arm_smmu_cmdq_batch_init(struct arm_smmu_device *smmu,
916-
struct arm_smmu_cmdq_batch *cmds)
917+
struct arm_smmu_cmdq_batch *cmds,
918+
struct arm_smmu_cmdq_ent *ent)
917919
{
918920
cmds->num = 0;
919-
cmds->cmdq = arm_smmu_get_cmdq(smmu);
921+
cmds->cmdq = arm_smmu_get_cmdq(smmu, ent);
920922
}
921923

922924
static void arm_smmu_cmdq_batch_add(struct arm_smmu_device *smmu,
@@ -931,13 +933,13 @@ static void arm_smmu_cmdq_batch_add(struct arm_smmu_device *smmu,
931933
if (force_sync || unsupported_cmd) {
932934
arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmdq, cmds->cmds,
933935
cmds->num, true);
934-
arm_smmu_cmdq_batch_init(smmu, cmds);
936+
arm_smmu_cmdq_batch_init(smmu, cmds, cmd);
935937
}
936938

937939
if (cmds->num == CMDQ_BATCH_ENTRIES) {
938940
arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmdq, cmds->cmds,
939941
cmds->num, false);
940-
arm_smmu_cmdq_batch_init(smmu, cmds);
942+
arm_smmu_cmdq_batch_init(smmu, cmds, cmd);
941943
}
942944

943945
index = cmds->num * CMDQ_ENT_DWORDS;
@@ -1205,7 +1207,7 @@ static void arm_smmu_sync_cd(struct arm_smmu_master *master,
12051207
},
12061208
};
12071209

1208-
arm_smmu_cmdq_batch_init(smmu, &cmds);
1210+
arm_smmu_cmdq_batch_init(smmu, &cmds, &cmd);
12091211
for (i = 0; i < master->num_streams; i++) {
12101212
cmd.cfgi.sid = master->streams[i].id;
12111213
arm_smmu_cmdq_batch_add(smmu, &cmds, &cmd);
@@ -2056,7 +2058,7 @@ static int arm_smmu_atc_inv_master(struct arm_smmu_master *master,
20562058

20572059
arm_smmu_atc_inv_to_cmd(ssid, 0, 0, &cmd);
20582060

2059-
arm_smmu_cmdq_batch_init(master->smmu, &cmds);
2061+
arm_smmu_cmdq_batch_init(master->smmu, &cmds, &cmd);
20602062
for (i = 0; i < master->num_streams; i++) {
20612063
cmd.atc.sid = master->streams[i].id;
20622064
arm_smmu_cmdq_batch_add(master->smmu, &cmds, &cmd);
@@ -2071,7 +2073,9 @@ int arm_smmu_atc_inv_domain(struct arm_smmu_domain *smmu_domain,
20712073
struct arm_smmu_master_domain *master_domain;
20722074
int i;
20732075
unsigned long flags;
2074-
struct arm_smmu_cmdq_ent cmd;
2076+
struct arm_smmu_cmdq_ent cmd = {
2077+
.opcode = CMDQ_OP_ATC_INV,
2078+
};
20752079
struct arm_smmu_cmdq_batch cmds;
20762080

20772081
if (!(smmu_domain->smmu->features & ARM_SMMU_FEAT_ATS))
@@ -2094,7 +2098,7 @@ int arm_smmu_atc_inv_domain(struct arm_smmu_domain *smmu_domain,
20942098
if (!atomic_read(&smmu_domain->nr_ats_masters))
20952099
return 0;
20962100

2097-
arm_smmu_cmdq_batch_init(smmu_domain->smmu, &cmds);
2101+
arm_smmu_cmdq_batch_init(smmu_domain->smmu, &cmds, &cmd);
20982102

20992103
spin_lock_irqsave(&smmu_domain->devices_lock, flags);
21002104
list_for_each_entry(master_domain, &smmu_domain->devices,
@@ -2176,7 +2180,7 @@ static void __arm_smmu_tlb_inv_range(struct arm_smmu_cmdq_ent *cmd,
21762180
num_pages++;
21772181
}
21782182

2179-
arm_smmu_cmdq_batch_init(smmu, &cmds);
2183+
arm_smmu_cmdq_batch_init(smmu, &cmds, cmd);
21802184

21812185
while (iova < end) {
21822186
if (smmu->features & ARM_SMMU_FEAT_RANGE_INV) {

drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -642,7 +642,8 @@ struct arm_smmu_strtab_cfg {
642642
struct arm_smmu_impl_ops {
643643
int (*device_reset)(struct arm_smmu_device *smmu);
644644
void (*device_remove)(struct arm_smmu_device *smmu);
645-
struct arm_smmu_cmdq *(*get_secondary_cmdq)(struct arm_smmu_device *smmu);
645+
struct arm_smmu_cmdq *(*get_secondary_cmdq)(
646+
struct arm_smmu_device *smmu, struct arm_smmu_cmdq_ent *ent);
646647
};
647648

648649
/* An SMMUv3 instance */

drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ struct tegra241_vcmdq {
142142
* struct tegra241_vintf - Virtual Interface
143143
* @idx: Global index in the CMDQV
144144
* @enabled: Enable status
145+
* @hyp_own: Owned by hypervisor (in-kernel)
145146
* @cmdqv: Parent CMDQV pointer
146147
* @lvcmdqs: List of logical VCMDQ pointers
147148
* @base: MMIO base address
@@ -150,6 +151,7 @@ struct tegra241_vintf {
150151
u16 idx;
151152

152153
bool enabled;
154+
bool hyp_own;
153155

154156
struct tegra241_cmdqv *cmdqv;
155157
struct tegra241_vcmdq **lvcmdqs;
@@ -301,8 +303,21 @@ static irqreturn_t tegra241_cmdqv_isr(int irq, void *devid)
301303

302304
/* Command Queue Function */
303305

306+
static bool tegra241_guest_vcmdq_supports_cmd(struct arm_smmu_cmdq_ent *ent)
307+
{
308+
switch (ent->opcode) {
309+
case CMDQ_OP_TLBI_NH_ASID:
310+
case CMDQ_OP_TLBI_NH_VA:
311+
case CMDQ_OP_ATC_INV:
312+
return true;
313+
default:
314+
return false;
315+
}
316+
}
317+
304318
static struct arm_smmu_cmdq *
305-
tegra241_cmdqv_get_cmdq(struct arm_smmu_device *smmu)
319+
tegra241_cmdqv_get_cmdq(struct arm_smmu_device *smmu,
320+
struct arm_smmu_cmdq_ent *ent)
306321
{
307322
struct tegra241_cmdqv *cmdqv =
308323
container_of(smmu, struct tegra241_cmdqv, smmu);
@@ -328,6 +343,10 @@ tegra241_cmdqv_get_cmdq(struct arm_smmu_device *smmu)
328343
vcmdq = vintf->lvcmdqs[lidx];
329344
if (!vcmdq || !READ_ONCE(vcmdq->enabled))
330345
return NULL;
346+
347+
/* Unsupported CMD goes for smmu->cmdq pathway */
348+
if (!arm_smmu_cmdq_supports_cmd(&vcmdq->cmdq, ent))
349+
return NULL;
331350
return &vcmdq->cmdq;
332351
}
333352

@@ -406,12 +425,22 @@ static int tegra241_vintf_hw_init(struct tegra241_vintf *vintf, bool hyp_own)
406425
tegra241_vintf_hw_deinit(vintf);
407426

408427
/* Configure and enable VINTF */
428+
/*
429+
* Note that HYP_OWN bit is wired to zero when running in guest kernel,
430+
* whether enabling it here or not, as !HYP_OWN cmdq HWs only support a
431+
* restricted set of supported commands.
432+
*/
409433
regval = FIELD_PREP(VINTF_HYP_OWN, hyp_own);
410434
writel(regval, REG_VINTF(vintf, CONFIG));
411435

412436
ret = vintf_write_config(vintf, regval | VINTF_EN);
413437
if (ret)
414438
return ret;
439+
/*
440+
* As being mentioned above, HYP_OWN bit is wired to zero for a guest
441+
* kernel, so read it back from HW to ensure that reflects in hyp_own
442+
*/
443+
vintf->hyp_own = !!(VINTF_HYP_OWN & readl(REG_VINTF(vintf, CONFIG)));
415444

416445
for (lidx = 0; lidx < vintf->cmdqv->num_lvcmdqs_per_vintf; lidx++) {
417446
if (vintf->lvcmdqs && vintf->lvcmdqs[lidx]) {
@@ -493,6 +522,9 @@ static int tegra241_vcmdq_alloc_smmu_cmdq(struct tegra241_vcmdq *vcmdq)
493522
q->q_base = q->base_dma & VCMDQ_ADDR;
494523
q->q_base |= FIELD_PREP(VCMDQ_LOG2SIZE, q->llq.max_n_shift);
495524

525+
if (!vcmdq->vintf->hyp_own)
526+
cmdq->supports_cmd = tegra241_guest_vcmdq_supports_cmd;
527+
496528
return arm_smmu_cmdq_init(smmu, cmdq);
497529
}
498530

0 commit comments

Comments
 (0)