|
5 | 5 | #include <linux/iommufd.h> |
6 | 6 | #include <linux/slab.h> |
7 | 7 | #include <uapi/linux/iommufd.h> |
| 8 | +#include <linux/msi.h> |
8 | 9 |
|
9 | 10 | #include "../iommu-priv.h" |
10 | 11 | #include "io_pagetable.h" |
@@ -293,36 +294,152 @@ u32 iommufd_device_to_id(struct iommufd_device *idev) |
293 | 294 | } |
294 | 295 | EXPORT_SYMBOL_NS_GPL(iommufd_device_to_id, "IOMMUFD"); |
295 | 296 |
|
| 297 | +/* |
| 298 | + * Get a iommufd_sw_msi_map for the msi physical address requested by the irq |
| 299 | + * layer. The mapping to IOVA is global to the iommufd file descriptor, every |
| 300 | + * domain that is attached to a device using the same MSI parameters will use |
| 301 | + * the same IOVA. |
| 302 | + */ |
| 303 | +static __maybe_unused struct iommufd_sw_msi_map * |
| 304 | +iommufd_sw_msi_get_map(struct iommufd_ctx *ictx, phys_addr_t msi_addr, |
| 305 | + phys_addr_t sw_msi_start) |
| 306 | +{ |
| 307 | + struct iommufd_sw_msi_map *cur; |
| 308 | + unsigned int max_pgoff = 0; |
| 309 | + |
| 310 | + lockdep_assert_held(&ictx->sw_msi_lock); |
| 311 | + |
| 312 | + list_for_each_entry(cur, &ictx->sw_msi_list, sw_msi_item) { |
| 313 | + if (cur->sw_msi_start != sw_msi_start) |
| 314 | + continue; |
| 315 | + max_pgoff = max(max_pgoff, cur->pgoff + 1); |
| 316 | + if (cur->msi_addr == msi_addr) |
| 317 | + return cur; |
| 318 | + } |
| 319 | + |
| 320 | + if (ictx->sw_msi_id >= |
| 321 | + BITS_PER_BYTE * sizeof_field(struct iommufd_sw_msi_maps, bitmap)) |
| 322 | + return ERR_PTR(-EOVERFLOW); |
| 323 | + |
| 324 | + cur = kzalloc(sizeof(*cur), GFP_KERNEL); |
| 325 | + if (!cur) |
| 326 | + return ERR_PTR(-ENOMEM); |
| 327 | + |
| 328 | + cur->sw_msi_start = sw_msi_start; |
| 329 | + cur->msi_addr = msi_addr; |
| 330 | + cur->pgoff = max_pgoff; |
| 331 | + cur->id = ictx->sw_msi_id++; |
| 332 | + list_add_tail(&cur->sw_msi_item, &ictx->sw_msi_list); |
| 333 | + return cur; |
| 334 | +} |
| 335 | + |
| 336 | +static int iommufd_sw_msi_install(struct iommufd_ctx *ictx, |
| 337 | + struct iommufd_hwpt_paging *hwpt_paging, |
| 338 | + struct iommufd_sw_msi_map *msi_map) |
| 339 | +{ |
| 340 | + unsigned long iova; |
| 341 | + |
| 342 | + lockdep_assert_held(&ictx->sw_msi_lock); |
| 343 | + |
| 344 | + iova = msi_map->sw_msi_start + msi_map->pgoff * PAGE_SIZE; |
| 345 | + if (!test_bit(msi_map->id, hwpt_paging->present_sw_msi.bitmap)) { |
| 346 | + int rc; |
| 347 | + |
| 348 | + rc = iommu_map(hwpt_paging->common.domain, iova, |
| 349 | + msi_map->msi_addr, PAGE_SIZE, |
| 350 | + IOMMU_WRITE | IOMMU_READ | IOMMU_MMIO, |
| 351 | + GFP_KERNEL_ACCOUNT); |
| 352 | + if (rc) |
| 353 | + return rc; |
| 354 | + __set_bit(msi_map->id, hwpt_paging->present_sw_msi.bitmap); |
| 355 | + } |
| 356 | + return 0; |
| 357 | +} |
| 358 | + |
| 359 | +/* |
| 360 | + * Called by the irq code if the platform translates the MSI address through the |
| 361 | + * IOMMU. msi_addr is the physical address of the MSI page. iommufd will |
| 362 | + * allocate a fd global iova for the physical page that is the same on all |
| 363 | + * domains and devices. |
| 364 | + */ |
| 365 | +#ifdef CONFIG_IRQ_MSI_IOMMU |
| 366 | +int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc, |
| 367 | + phys_addr_t msi_addr) |
| 368 | +{ |
| 369 | + struct device *dev = msi_desc_to_dev(desc); |
| 370 | + struct iommufd_hwpt_paging *hwpt_paging; |
| 371 | + struct iommu_attach_handle *raw_handle; |
| 372 | + struct iommufd_attach_handle *handle; |
| 373 | + struct iommufd_sw_msi_map *msi_map; |
| 374 | + struct iommufd_ctx *ictx; |
| 375 | + unsigned long iova; |
| 376 | + int rc; |
| 377 | + |
| 378 | + /* |
| 379 | + * It is safe to call iommu_attach_handle_get() here because the iommu |
| 380 | + * core code invokes this under the group mutex which also prevents any |
| 381 | + * change of the attach handle for the duration of this function. |
| 382 | + */ |
| 383 | + iommu_group_mutex_assert(dev); |
| 384 | + |
| 385 | + raw_handle = |
| 386 | + iommu_attach_handle_get(dev->iommu_group, IOMMU_NO_PASID, 0); |
| 387 | + if (IS_ERR(raw_handle)) |
| 388 | + return 0; |
| 389 | + hwpt_paging = find_hwpt_paging(domain->iommufd_hwpt); |
| 390 | + |
| 391 | + handle = to_iommufd_handle(raw_handle); |
| 392 | + /* No IOMMU_RESV_SW_MSI means no change to the msi_msg */ |
| 393 | + if (handle->idev->igroup->sw_msi_start == PHYS_ADDR_MAX) |
| 394 | + return 0; |
| 395 | + |
| 396 | + ictx = handle->idev->ictx; |
| 397 | + guard(mutex)(&ictx->sw_msi_lock); |
| 398 | + /* |
| 399 | + * The input msi_addr is the exact byte offset of the MSI doorbell, we |
| 400 | + * assume the caller has checked that it is contained with a MMIO region |
| 401 | + * that is secure to map at PAGE_SIZE. |
| 402 | + */ |
| 403 | + msi_map = iommufd_sw_msi_get_map(handle->idev->ictx, |
| 404 | + msi_addr & PAGE_MASK, |
| 405 | + handle->idev->igroup->sw_msi_start); |
| 406 | + if (IS_ERR(msi_map)) |
| 407 | + return PTR_ERR(msi_map); |
| 408 | + |
| 409 | + rc = iommufd_sw_msi_install(ictx, hwpt_paging, msi_map); |
| 410 | + if (rc) |
| 411 | + return rc; |
| 412 | + __set_bit(msi_map->id, handle->idev->igroup->required_sw_msi.bitmap); |
| 413 | + |
| 414 | + iova = msi_map->sw_msi_start + msi_map->pgoff * PAGE_SIZE; |
| 415 | + msi_desc_set_iommu_msi_iova(desc, iova, PAGE_SHIFT); |
| 416 | + return 0; |
| 417 | +} |
| 418 | +#endif |
| 419 | + |
296 | 420 | static int iommufd_group_setup_msi(struct iommufd_group *igroup, |
297 | 421 | struct iommufd_hwpt_paging *hwpt_paging) |
298 | 422 | { |
299 | | - phys_addr_t sw_msi_start = igroup->sw_msi_start; |
300 | | - int rc; |
| 423 | + struct iommufd_ctx *ictx = igroup->ictx; |
| 424 | + struct iommufd_sw_msi_map *cur; |
| 425 | + |
| 426 | + if (igroup->sw_msi_start == PHYS_ADDR_MAX) |
| 427 | + return 0; |
301 | 428 |
|
302 | 429 | /* |
303 | | - * If the IOMMU driver gives a IOMMU_RESV_SW_MSI then it is asking us to |
304 | | - * call iommu_get_msi_cookie() on its behalf. This is necessary to setup |
305 | | - * the MSI window so iommu_dma_prepare_msi() can install pages into our |
306 | | - * domain after request_irq(). If it is not done interrupts will not |
307 | | - * work on this domain. |
308 | | - * |
309 | | - * FIXME: This is conceptually broken for iommufd since we want to allow |
310 | | - * userspace to change the domains, eg switch from an identity IOAS to a |
311 | | - * DMA IOAS. There is currently no way to create a MSI window that |
312 | | - * matches what the IRQ layer actually expects in a newly created |
313 | | - * domain. |
| 430 | + * Install all the MSI pages the device has been using into the domain |
314 | 431 | */ |
315 | | - if (sw_msi_start != PHYS_ADDR_MAX && !hwpt_paging->msi_cookie) { |
316 | | - rc = iommu_get_msi_cookie(hwpt_paging->common.domain, |
317 | | - sw_msi_start); |
| 432 | + guard(mutex)(&ictx->sw_msi_lock); |
| 433 | + list_for_each_entry(cur, &ictx->sw_msi_list, sw_msi_item) { |
| 434 | + int rc; |
| 435 | + |
| 436 | + if (cur->sw_msi_start != igroup->sw_msi_start || |
| 437 | + !test_bit(cur->id, igroup->required_sw_msi.bitmap)) |
| 438 | + continue; |
| 439 | + |
| 440 | + rc = iommufd_sw_msi_install(ictx, hwpt_paging, cur); |
318 | 441 | if (rc) |
319 | 442 | return rc; |
320 | | - |
321 | | - /* |
322 | | - * iommu_get_msi_cookie() can only be called once per domain, |
323 | | - * it returns -EBUSY on later calls. |
324 | | - */ |
325 | | - hwpt_paging->msi_cookie = true; |
326 | 443 | } |
327 | 444 | return 0; |
328 | 445 | } |
|
0 commit comments