Skip to content

Commit d6c55c0

Browse files
committed
iommufd: Change the order of MSI setup
Eric points out this is wrong for the rare case of someone using allow_unsafe_interrupts on ARM. We always have to setup the MSI window in the domain if the iommu driver asks for it. Move the iommu_get_msi_cookie() setup to the top of the function and always do it, regardless of the security mode. Add checks to iommufd_device_setup_msi() to ensure the driver is not doing something incomprehensible. No current driver will set both a HW and SW MSI window, or have more than one SW MSI window. Fixes: e8d5721 ("iommufd: Add kAPI toward external drivers for physical devices") Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Kevin Tian <[email protected]> Reported-by: Eric Auger <[email protected]> Signed-off-by: Jason Gunthorpe <[email protected]>
1 parent a26fa39 commit d6c55c0

File tree

2 files changed

+39
-41
lines changed

2 files changed

+39
-41
lines changed

drivers/iommu/iommufd/device.c

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -139,53 +139,47 @@ static int iommufd_device_setup_msi(struct iommufd_device *idev,
139139
int rc;
140140

141141
/*
142-
* IOMMU_CAP_INTR_REMAP means that the platform is isolating MSI, and it
143-
* creates the MSI window by default in the iommu domain. Nothing
144-
* further to do.
145-
*/
146-
if (device_iommu_capable(idev->dev, IOMMU_CAP_INTR_REMAP))
147-
return 0;
148-
149-
/*
150-
* On ARM systems that set the global IRQ_DOMAIN_FLAG_MSI_REMAP every
151-
* allocated iommu_domain will block interrupts by default and this
152-
* special flow is needed to turn them back on. iommu_dma_prepare_msi()
153-
* will install pages into our domain after request_irq() to make this
154-
* work.
142+
* If the IOMMU driver gives a IOMMU_RESV_SW_MSI then it is asking us to
143+
* call iommu_get_msi_cookie() on its behalf. This is necessary to setup
144+
* the MSI window so iommu_dma_prepare_msi() can install pages into our
145+
* domain after request_irq(). If it is not done interrupts will not
146+
* work on this domain.
155147
*
156148
* FIXME: This is conceptually broken for iommufd since we want to allow
157149
* userspace to change the domains, eg switch from an identity IOAS to a
158150
* DMA IOAS. There is currently no way to create a MSI window that
159151
* matches what the IRQ layer actually expects in a newly created
160152
* domain.
161153
*/
162-
if (irq_domain_check_msi_remap()) {
163-
if (WARN_ON(!sw_msi_start))
164-
return -EPERM;
154+
if (sw_msi_start != PHYS_ADDR_MAX && !hwpt->msi_cookie) {
155+
rc = iommu_get_msi_cookie(hwpt->domain, sw_msi_start);
156+
if (rc)
157+
return rc;
158+
165159
/*
166160
* iommu_get_msi_cookie() can only be called once per domain,
167161
* it returns -EBUSY on later calls.
168162
*/
169-
if (hwpt->msi_cookie)
170-
return 0;
171-
rc = iommu_get_msi_cookie(hwpt->domain, sw_msi_start);
172-
if (rc)
173-
return rc;
174163
hwpt->msi_cookie = true;
175-
return 0;
176164
}
177165

178166
/*
179-
* Otherwise the platform has a MSI window that is not isolated. For
180-
* historical compat with VFIO allow a module parameter to ignore the
181-
* insecurity.
167+
* For historical compat with VFIO the insecure interrupt path is
168+
* allowed if the module parameter is set. Insecure means that a MemWr
169+
* operation from the device (eg a simple DMA) cannot trigger an
170+
* interrupt outside this iommufd context.
182171
*/
183-
if (!allow_unsafe_interrupts)
184-
return -EPERM;
172+
if (!device_iommu_capable(idev->dev, IOMMU_CAP_INTR_REMAP) &&
173+
!irq_domain_check_msi_remap()) {
174+
if (!allow_unsafe_interrupts)
175+
return -EPERM;
185176

186-
dev_warn(
187-
idev->dev,
188-
"MSI interrupt window cannot be isolated by the IOMMU, this platform is insecure. Use the \"allow_unsafe_interrupts\" module parameter to override\n");
177+
dev_warn(
178+
idev->dev,
179+
"MSI interrupts are not secure, they cannot be isolated by the platform. "
180+
"Check that platform features like interrupt remapping are enabled. "
181+
"Use the \"allow_unsafe_interrupts\" module parameter to override\n");
182+
}
189183
return 0;
190184
}
191185

@@ -203,7 +197,7 @@ static bool iommufd_hw_pagetable_has_group(struct iommufd_hw_pagetable *hwpt,
203197
static int iommufd_device_do_attach(struct iommufd_device *idev,
204198
struct iommufd_hw_pagetable *hwpt)
205199
{
206-
phys_addr_t sw_msi_start = 0;
200+
phys_addr_t sw_msi_start = PHYS_ADDR_MAX;
207201
int rc;
208202

209203
mutex_lock(&hwpt->devices_lock);

drivers/iommu/iommufd/io_pagetable.c

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,6 +1170,8 @@ int iopt_table_enforce_group_resv_regions(struct io_pagetable *iopt,
11701170
struct iommu_resv_region *resv;
11711171
struct iommu_resv_region *tmp;
11721172
LIST_HEAD(group_resv_regions);
1173+
unsigned int num_hw_msi = 0;
1174+
unsigned int num_sw_msi = 0;
11731175
int rc;
11741176

11751177
down_write(&iopt->iova_rwsem);
@@ -1181,23 +1183,25 @@ int iopt_table_enforce_group_resv_regions(struct io_pagetable *iopt,
11811183
if (resv->type == IOMMU_RESV_DIRECT_RELAXABLE)
11821184
continue;
11831185

1184-
/*
1185-
* The presence of any 'real' MSI regions should take precedence
1186-
* over the software-managed one if the IOMMU driver happens to
1187-
* advertise both types.
1188-
*/
1189-
if (sw_msi_start && resv->type == IOMMU_RESV_MSI) {
1190-
*sw_msi_start = 0;
1191-
sw_msi_start = NULL;
1192-
}
1193-
if (sw_msi_start && resv->type == IOMMU_RESV_SW_MSI)
1186+
if (sw_msi_start && resv->type == IOMMU_RESV_MSI)
1187+
num_hw_msi++;
1188+
if (sw_msi_start && resv->type == IOMMU_RESV_SW_MSI) {
11941189
*sw_msi_start = resv->start;
1190+
num_sw_msi++;
1191+
}
11951192

11961193
rc = iopt_reserve_iova(iopt, resv->start,
11971194
resv->length - 1 + resv->start, device);
11981195
if (rc)
11991196
goto out_reserved;
12001197
}
1198+
1199+
/* Drivers must offer sane combinations of regions */
1200+
if (WARN_ON(num_sw_msi && num_hw_msi) || WARN_ON(num_sw_msi > 1)) {
1201+
rc = -EINVAL;
1202+
goto out_reserved;
1203+
}
1204+
12011205
rc = 0;
12021206
goto out_free_resv;
12031207

0 commit comments

Comments
 (0)