mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
iommufd: Add an ioctl to query PA from IOVA for noiommu mode
To support no-IOMMU mode where userspace drivers perform unsafe DMA using physical addresses, introduce a new API to retrieve the physical address of a user-allocated DMA buffer that has been mapped to an IOVA via IOMMU_IOAS_MAP. The mapping is backed by SW-only I/O page tables maintained by the GENERIC_PT framework. Link: https://patch.msgid.link/r/a60a601509688e8552c75e668deb548c93974a3b.1783360051.git.jacob.pan@linux.microsoft.com Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com> Reviewed-by: Kevin Tian <kevin.tian@intel.com> Reviewed-by: Pranjal Shrivastava <praan@google.com> Reviewed-by: Yi Liu <yi.l.liu@intel.com> Suggested-by: Jason Gunthorpe <jgg@nvidia.com> Co-developed-by: Jason Gunthorpe <jgg@nvidia.com> Signed-off-by: Jacob Pan <jacob.pan@linux.microsoft.com> Fixes: 2c6cf6ab1564 ("iommufd: Allow binding to a noiommu device") Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
This commit is contained in:
parent
072f454c38
commit
60decab325
|
|
@ -859,6 +859,84 @@ int iopt_unmap_iova(struct io_pagetable *iopt, unsigned long iova,
|
|||
return iopt_unmap_iova_range(iopt, iova, iova_last, unmapped);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IOMMUFD_NOIOMMU
|
||||
int iopt_get_phys(struct io_pagetable *iopt, unsigned long iova, u64 *paddr,
|
||||
u64 *length)
|
||||
{
|
||||
struct iopt_area *area;
|
||||
struct iopt_pages *pages;
|
||||
u64 max_length = *length;
|
||||
u64 tmp_length = 0;
|
||||
u64 tmp_paddr = 0;
|
||||
int rc = 0;
|
||||
|
||||
if (!max_length)
|
||||
return -EINVAL;
|
||||
|
||||
down_read(&iopt->iova_rwsem);
|
||||
area = iopt_area_iter_first(iopt, iova, iova);
|
||||
if (!area || !area->pages) {
|
||||
rc = -ENOENT;
|
||||
goto unlock_exit;
|
||||
}
|
||||
|
||||
pages = area->pages;
|
||||
mutex_lock(&pages->mutex);
|
||||
if (iopt_dmabuf_revoked(pages)) {
|
||||
rc = -EINVAL;
|
||||
goto unlock_pages;
|
||||
}
|
||||
|
||||
if (!area->storage_domain ||
|
||||
area->storage_domain->owner != &iommufd_noiommu_ops) {
|
||||
rc = -EOPNOTSUPP;
|
||||
goto unlock_pages;
|
||||
}
|
||||
|
||||
*paddr = iommu_iova_to_phys(area->storage_domain, iova);
|
||||
tmp_length = min_t(u64, PAGE_SIZE - offset_in_page(iova),
|
||||
iopt_area_last_iova(area) - iova + 1);
|
||||
tmp_paddr = *paddr;
|
||||
/*
|
||||
* Return the physically contiguous length, capped by the caller
|
||||
* supplied range length.
|
||||
*/
|
||||
while (iova < iopt_area_last_iova(area)) {
|
||||
unsigned long next_iova;
|
||||
u64 next_paddr;
|
||||
|
||||
if (tmp_length >= max_length)
|
||||
break;
|
||||
|
||||
if (check_add_overflow(iova, PAGE_SIZE, &next_iova))
|
||||
break;
|
||||
|
||||
if (next_iova > iopt_area_last_iova(area))
|
||||
break;
|
||||
|
||||
next_paddr = iommu_iova_to_phys(area->storage_domain, next_iova);
|
||||
|
||||
if (!next_paddr || next_paddr != tmp_paddr + PAGE_SIZE)
|
||||
break;
|
||||
|
||||
iova = next_iova;
|
||||
tmp_paddr += PAGE_SIZE;
|
||||
tmp_length += PAGE_SIZE;
|
||||
}
|
||||
|
||||
if (tmp_length > max_length)
|
||||
tmp_length = max_length;
|
||||
*length = tmp_length;
|
||||
|
||||
unlock_pages:
|
||||
mutex_unlock(&pages->mutex);
|
||||
unlock_exit:
|
||||
up_read(&iopt->iova_rwsem);
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
int iopt_unmap_all(struct io_pagetable *iopt, unsigned long *unmapped)
|
||||
{
|
||||
/* If the IOVAs are empty then unmap all succeeds */
|
||||
|
|
|
|||
|
|
@ -375,6 +375,42 @@ int iommufd_ioas_unmap(struct iommufd_ucmd *ucmd)
|
|||
return rc;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IOMMUFD_NOIOMMU
|
||||
int iommufd_ioas_noiommu_get_pa(struct iommufd_ucmd *ucmd)
|
||||
{
|
||||
struct iommu_ioas_noiommu_get_pa *cmd = ucmd->cmd;
|
||||
struct iommufd_ioas *ioas;
|
||||
int rc;
|
||||
|
||||
if (!capable(CAP_SYS_RAWIO))
|
||||
return -EPERM;
|
||||
|
||||
if (cmd->flags || cmd->__reserved)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!cmd->length)
|
||||
return -EINVAL;
|
||||
|
||||
if (cmd->iova >= ULONG_MAX)
|
||||
return -EOVERFLOW;
|
||||
|
||||
ioas = iommufd_get_ioas(ucmd->ictx, cmd->ioas_id);
|
||||
if (IS_ERR(ioas))
|
||||
return PTR_ERR(ioas);
|
||||
|
||||
rc = iopt_get_phys(&ioas->iopt, cmd->iova, &cmd->out_phys,
|
||||
&cmd->length);
|
||||
if (rc)
|
||||
goto out_put;
|
||||
|
||||
rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
|
||||
out_put:
|
||||
iommufd_put_object(ucmd->ictx, &ioas->obj);
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void iommufd_release_all_iova_rwsem(struct iommufd_ctx *ictx,
|
||||
struct xarray *ioas_list)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -118,6 +118,16 @@ int iopt_map_pages(struct io_pagetable *iopt, struct list_head *pages_list,
|
|||
int iopt_unmap_iova(struct io_pagetable *iopt, unsigned long iova,
|
||||
unsigned long length, unsigned long *unmapped);
|
||||
int iopt_unmap_all(struct io_pagetable *iopt, unsigned long *unmapped);
|
||||
#ifdef CONFIG_IOMMUFD_NOIOMMU
|
||||
int iopt_get_phys(struct io_pagetable *iopt, unsigned long iova, u64 *paddr,
|
||||
u64 *length);
|
||||
#else
|
||||
static inline int iopt_get_phys(struct io_pagetable *iopt, unsigned long iova,
|
||||
u64 *paddr, u64 *length)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif
|
||||
|
||||
int iopt_read_and_clear_dirty_data(struct io_pagetable *iopt,
|
||||
struct iommu_domain *domain,
|
||||
|
|
@ -347,6 +357,14 @@ int iommufd_ioas_map_file(struct iommufd_ucmd *ucmd);
|
|||
int iommufd_ioas_change_process(struct iommufd_ucmd *ucmd);
|
||||
int iommufd_ioas_copy(struct iommufd_ucmd *ucmd);
|
||||
int iommufd_ioas_unmap(struct iommufd_ucmd *ucmd);
|
||||
#ifdef CONFIG_IOMMUFD_NOIOMMU
|
||||
int iommufd_ioas_noiommu_get_pa(struct iommufd_ucmd *ucmd);
|
||||
#else
|
||||
static inline int iommufd_ioas_noiommu_get_pa(struct iommufd_ucmd *ucmd)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif
|
||||
int iommufd_ioas_option(struct iommufd_ucmd *ucmd);
|
||||
int iommufd_option_rlimit_mode(struct iommu_option *cmd,
|
||||
struct iommufd_ctx *ictx);
|
||||
|
|
|
|||
|
|
@ -444,6 +444,7 @@ union ucmd_buffer {
|
|||
struct iommu_ioas_alloc alloc;
|
||||
struct iommu_ioas_allow_iovas allow_iovas;
|
||||
struct iommu_ioas_copy ioas_copy;
|
||||
struct iommu_ioas_noiommu_get_pa noiommu_get_pa;
|
||||
struct iommu_ioas_iova_ranges iova_ranges;
|
||||
struct iommu_ioas_map map;
|
||||
struct iommu_ioas_unmap unmap;
|
||||
|
|
@ -502,6 +503,8 @@ static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = {
|
|||
IOCTL_OP(IOMMU_IOAS_MAP, iommufd_ioas_map, struct iommu_ioas_map, iova),
|
||||
IOCTL_OP(IOMMU_IOAS_MAP_FILE, iommufd_ioas_map_file,
|
||||
struct iommu_ioas_map_file, iova),
|
||||
IOCTL_OP(IOMMU_IOAS_NOIOMMU_GET_PA, iommufd_ioas_noiommu_get_pa, struct iommu_ioas_noiommu_get_pa,
|
||||
out_phys),
|
||||
IOCTL_OP(IOMMU_IOAS_UNMAP, iommufd_ioas_unmap, struct iommu_ioas_unmap,
|
||||
length),
|
||||
IOCTL_OP(IOMMU_OPTION, iommufd_option, struct iommu_option, val64),
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ enum {
|
|||
IOMMUFD_CMD_IOAS_CHANGE_PROCESS = 0x92,
|
||||
IOMMUFD_CMD_VEVENTQ_ALLOC = 0x93,
|
||||
IOMMUFD_CMD_HW_QUEUE_ALLOC = 0x94,
|
||||
IOMMUFD_CMD_IOAS_NOIOMMU_GET_PA = 0x95,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -219,6 +220,33 @@ struct iommu_ioas_map {
|
|||
};
|
||||
#define IOMMU_IOAS_MAP _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_MAP)
|
||||
|
||||
/**
|
||||
* struct iommu_ioas_noiommu_get_pa - ioctl(IOMMU_IOAS_NOIOMMU_GET_PA)
|
||||
* @size: sizeof(struct iommu_ioas_noiommu_get_pa)
|
||||
* @flags: Reserved, must be 0 for now
|
||||
* @ioas_id: IOAS ID to query IOVA to PA mapping from
|
||||
* @__reserved: Must be 0
|
||||
* @iova: IOVA to query
|
||||
* @length: On input, non-zero maximum number of bytes to query starting from
|
||||
* @iova. On output, number of physically contiguous bytes starting
|
||||
* from @out_phys, capped by the input length.
|
||||
* @out_phys: Output physical address the IOVA maps to
|
||||
*
|
||||
* Query the physical address backing an IOVA range. The beginning of the
|
||||
* range must be mapped already and length must be non-zero. For noiommu
|
||||
* devices doing unsafe DMA only.
|
||||
*/
|
||||
struct iommu_ioas_noiommu_get_pa {
|
||||
__u32 size;
|
||||
__u32 flags;
|
||||
__u32 ioas_id;
|
||||
__u32 __reserved;
|
||||
__aligned_u64 iova;
|
||||
__aligned_u64 length;
|
||||
__aligned_u64 out_phys;
|
||||
};
|
||||
#define IOMMU_IOAS_NOIOMMU_GET_PA _IO(IOMMUFD_TYPE, IOMMUFD_CMD_IOAS_NOIOMMU_GET_PA)
|
||||
|
||||
/**
|
||||
* struct iommu_ioas_map_file - ioctl(IOMMU_IOAS_MAP_FILE)
|
||||
* @size: sizeof(struct iommu_ioas_map_file)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user