Skip to content

Commit 81f81db

Browse files
nicolincjgunthorpe
authored andcommitted
iommu/tegra241-cmdqv: Do not statically map LVCMDQs
To simplify the mappings from global VCMDQs to VINTFs' LVCMDQs, the design chose to do static allocations and mappings in the global reset function. However, with the user-owned VINTF support, it exposes a security concern: if user space VM only wants one LVCMDQ for a VINTF, statically mapping two or more LVCMDQs creates a hidden VCMDQ that user space could DoS attack by writing random stuff to overwhelm the kernel with unhandleable IRQs. Thus, to support the user-owned VINTF feature, a LVCMDQ mapping has to be done dynamically. HW allows pre-assigning global VCMDQs in the CMDQ_ALLOC registers, without finalizing the mappings by keeping CMDQV_CMDQ_ALLOCATED=0. So, add a pair of map/unmap helper that simply sets/clears that bit. For kernel-owned VINTF0, move LVCMDQ mappings to tegra241_vintf_hw_init(), and the unmappings to tegra241_vintf_hw_deinit(). For user-owned VINTFs that will be added, the mappings/unmappings will be on demand upon an LVCMDQ allocation from the user space. However, the dynamic LVCMDQ mapping/unmapping can complicate the timing of calling tegra241_vcmdq_hw_init/deinit(), which write LVCMDQ address space, i.e. requiring LVCMDQ to be mapped. Highlight that with a note to the top of either of them. Link: https://patch.msgid.link/r/be115a8f75537632daf5995b3e583d8a76553fba.1752126748.git.nicolinc@nvidia.com Acked-by: Pranjal Shrivastava <[email protected]> Reviewed-by: Jason Gunthorpe <[email protected]> Signed-off-by: Nicolin Chen <[email protected]> Signed-off-by: Jason Gunthorpe <[email protected]>
1 parent 589899e commit 81f81db

File tree

1 file changed

+33
-4
lines changed

1 file changed

+33
-4
lines changed

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

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ tegra241_cmdqv_get_cmdq(struct arm_smmu_device *smmu,
351351

352352
/* HW Reset Functions */
353353

354+
/* This function is for LVCMDQ, so @vcmdq must not be unmapped yet */
354355
static void tegra241_vcmdq_hw_deinit(struct tegra241_vcmdq *vcmdq)
355356
{
356357
char header[64], *h = lvcmdq_error_header(vcmdq, header, 64);
@@ -379,6 +380,7 @@ static void tegra241_vcmdq_hw_deinit(struct tegra241_vcmdq *vcmdq)
379380
dev_dbg(vcmdq->cmdqv->dev, "%sdeinited\n", h);
380381
}
381382

383+
/* This function is for LVCMDQ, so @vcmdq must be mapped prior */
382384
static int tegra241_vcmdq_hw_init(struct tegra241_vcmdq *vcmdq)
383385
{
384386
char header[64], *h = lvcmdq_error_header(vcmdq, header, 64);
@@ -404,16 +406,42 @@ static int tegra241_vcmdq_hw_init(struct tegra241_vcmdq *vcmdq)
404406
return 0;
405407
}
406408

409+
/* Unmap a global VCMDQ from the pre-assigned LVCMDQ */
410+
static void tegra241_vcmdq_unmap_lvcmdq(struct tegra241_vcmdq *vcmdq)
411+
{
412+
u32 regval = readl(REG_CMDQV(vcmdq->cmdqv, CMDQ_ALLOC(vcmdq->idx)));
413+
char header[64], *h = lvcmdq_error_header(vcmdq, header, 64);
414+
415+
writel(regval & ~CMDQV_CMDQ_ALLOCATED,
416+
REG_CMDQV(vcmdq->cmdqv, CMDQ_ALLOC(vcmdq->idx)));
417+
dev_dbg(vcmdq->cmdqv->dev, "%sunmapped\n", h);
418+
}
419+
407420
static void tegra241_vintf_hw_deinit(struct tegra241_vintf *vintf)
408421
{
409-
u16 lidx;
422+
u16 lidx = vintf->cmdqv->num_lvcmdqs_per_vintf;
410423

411-
for (lidx = 0; lidx < vintf->cmdqv->num_lvcmdqs_per_vintf; lidx++)
412-
if (vintf->lvcmdqs && vintf->lvcmdqs[lidx])
424+
/* HW requires to unmap LVCMDQs in descending order */
425+
while (lidx--) {
426+
if (vintf->lvcmdqs && vintf->lvcmdqs[lidx]) {
413427
tegra241_vcmdq_hw_deinit(vintf->lvcmdqs[lidx]);
428+
tegra241_vcmdq_unmap_lvcmdq(vintf->lvcmdqs[lidx]);
429+
}
430+
}
414431
vintf_write_config(vintf, 0);
415432
}
416433

434+
/* Map a global VCMDQ to the pre-assigned LVCMDQ */
435+
static void tegra241_vcmdq_map_lvcmdq(struct tegra241_vcmdq *vcmdq)
436+
{
437+
u32 regval = readl(REG_CMDQV(vcmdq->cmdqv, CMDQ_ALLOC(vcmdq->idx)));
438+
char header[64], *h = lvcmdq_error_header(vcmdq, header, 64);
439+
440+
writel(regval | CMDQV_CMDQ_ALLOCATED,
441+
REG_CMDQV(vcmdq->cmdqv, CMDQ_ALLOC(vcmdq->idx)));
442+
dev_dbg(vcmdq->cmdqv->dev, "%smapped\n", h);
443+
}
444+
417445
static int tegra241_vintf_hw_init(struct tegra241_vintf *vintf, bool hyp_own)
418446
{
419447
u32 regval;
@@ -441,8 +469,10 @@ static int tegra241_vintf_hw_init(struct tegra241_vintf *vintf, bool hyp_own)
441469
*/
442470
vintf->hyp_own = !!(VINTF_HYP_OWN & readl(REG_VINTF(vintf, CONFIG)));
443471

472+
/* HW requires to map LVCMDQs in ascending order */
444473
for (lidx = 0; lidx < vintf->cmdqv->num_lvcmdqs_per_vintf; lidx++) {
445474
if (vintf->lvcmdqs && vintf->lvcmdqs[lidx]) {
475+
tegra241_vcmdq_map_lvcmdq(vintf->lvcmdqs[lidx]);
446476
ret = tegra241_vcmdq_hw_init(vintf->lvcmdqs[lidx]);
447477
if (ret) {
448478
tegra241_vintf_hw_deinit(vintf);
@@ -476,7 +506,6 @@ static int tegra241_cmdqv_hw_reset(struct arm_smmu_device *smmu)
476506
for (lidx = 0; lidx < cmdqv->num_lvcmdqs_per_vintf; lidx++) {
477507
regval = FIELD_PREP(CMDQV_CMDQ_ALLOC_VINTF, idx);
478508
regval |= FIELD_PREP(CMDQV_CMDQ_ALLOC_LVCMDQ, lidx);
479-
regval |= CMDQV_CMDQ_ALLOCATED;
480509
writel_relaxed(regval,
481510
REG_CMDQV(cmdqv, CMDQ_ALLOC(qidx++)));
482511
}

0 commit comments

Comments
 (0)