mirror of
https://github.com/torvalds/linux
synced 2024-10-07 11:53:31 +00:00
iommufd: Add IOMMU_GET_HW_INFO
Under nested IOMMU translation, userspace owns the stage-1 translation table (e.g. the stage-1 page table of Intel VT-d or the context table of ARM SMMUv3, and etc.). Stage-1 translation tables are vendor specific, and need to be compatible with the underlying IOMMU hardware. Hence, userspace should know the IOMMU hardware capability before creating and configuring the stage-1 translation table to kernel. This adds IOMMU_GET_HW_INFO ioctl to query the IOMMU hardware information (a.k.a capability) for a given device. The returned data is vendor specific, userspace needs to decode it with the structure by the output @out_data_type field. As only physical devices have IOMMU hardware, so this will return error if the given device is not a physical device. Link: https://lore.kernel.org/r/20230818101033.4100-4-yi.l.liu@intel.com Reviewed-by: Lu Baolu <baolu.lu@linux.intel.com> Reviewed-by: Kevin Tian <kevin.tian@intel.com> Co-developed-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Yi Liu <yi.l.liu@intel.com> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
This commit is contained in:
parent
60fedb262b
commit
55dd4023ce
|
@ -4,6 +4,7 @@
|
|||
#include <linux/iommufd.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/iommu.h>
|
||||
#include <uapi/linux/iommufd.h>
|
||||
#include "../iommu-priv.h"
|
||||
|
||||
#include "io_pagetable.h"
|
||||
|
@ -1119,3 +1120,75 @@ int iommufd_access_rw(struct iommufd_access *access, unsigned long iova,
|
|||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(iommufd_access_rw, IOMMUFD);
|
||||
|
||||
int iommufd_get_hw_info(struct iommufd_ucmd *ucmd)
|
||||
{
|
||||
struct iommu_hw_info *cmd = ucmd->cmd;
|
||||
void __user *user_ptr = u64_to_user_ptr(cmd->data_uptr);
|
||||
const struct iommu_ops *ops;
|
||||
struct iommufd_device *idev;
|
||||
unsigned int data_len;
|
||||
unsigned int copy_len;
|
||||
void *data;
|
||||
int rc;
|
||||
|
||||
if (cmd->flags || cmd->__reserved)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
idev = iommufd_get_device(ucmd, cmd->dev_id);
|
||||
if (IS_ERR(idev))
|
||||
return PTR_ERR(idev);
|
||||
|
||||
ops = dev_iommu_ops(idev->dev);
|
||||
if (ops->hw_info) {
|
||||
data = ops->hw_info(idev->dev, &data_len, &cmd->out_data_type);
|
||||
if (IS_ERR(data)) {
|
||||
rc = PTR_ERR(data);
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
/*
|
||||
* drivers that have hw_info callback should have a unique
|
||||
* iommu_hw_info_type.
|
||||
*/
|
||||
if (WARN_ON_ONCE(cmd->out_data_type ==
|
||||
IOMMU_HW_INFO_TYPE_NONE)) {
|
||||
rc = -ENODEV;
|
||||
goto out_free;
|
||||
}
|
||||
} else {
|
||||
cmd->out_data_type = IOMMU_HW_INFO_TYPE_NONE;
|
||||
data_len = 0;
|
||||
data = NULL;
|
||||
}
|
||||
|
||||
copy_len = min(cmd->data_len, data_len);
|
||||
if (copy_to_user(user_ptr, data, copy_len)) {
|
||||
rc = -EFAULT;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/*
|
||||
* Zero the trailing bytes if the user buffer is bigger than the
|
||||
* data size kernel actually has.
|
||||
*/
|
||||
if (copy_len < cmd->data_len) {
|
||||
if (clear_user(user_ptr + copy_len, cmd->data_len - copy_len)) {
|
||||
rc = -EFAULT;
|
||||
goto out_free;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We return the length the kernel supports so userspace may know what
|
||||
* the kernel capability is. It could be larger than the input buffer.
|
||||
*/
|
||||
cmd->data_len = data_len;
|
||||
|
||||
rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
|
||||
out_free:
|
||||
kfree(data);
|
||||
out_put:
|
||||
iommufd_put_object(&idev->obj);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -296,6 +296,7 @@ iommufd_get_device(struct iommufd_ucmd *ucmd, u32 id)
|
|||
}
|
||||
|
||||
void iommufd_device_destroy(struct iommufd_object *obj);
|
||||
int iommufd_get_hw_info(struct iommufd_ucmd *ucmd);
|
||||
|
||||
struct iommufd_access {
|
||||
struct iommufd_object obj;
|
||||
|
|
|
@ -305,6 +305,7 @@ static int iommufd_option(struct iommufd_ucmd *ucmd)
|
|||
|
||||
union ucmd_buffer {
|
||||
struct iommu_destroy destroy;
|
||||
struct iommu_hw_info info;
|
||||
struct iommu_hwpt_alloc hwpt;
|
||||
struct iommu_ioas_alloc alloc;
|
||||
struct iommu_ioas_allow_iovas allow_iovas;
|
||||
|
@ -337,6 +338,8 @@ struct iommufd_ioctl_op {
|
|||
}
|
||||
static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = {
|
||||
IOCTL_OP(IOMMU_DESTROY, iommufd_destroy, struct iommu_destroy, id),
|
||||
IOCTL_OP(IOMMU_GET_HW_INFO, iommufd_get_hw_info, struct iommu_hw_info,
|
||||
__reserved),
|
||||
IOCTL_OP(IOMMU_HWPT_ALLOC, iommufd_hwpt_alloc, struct iommu_hwpt_alloc,
|
||||
__reserved),
|
||||
IOCTL_OP(IOMMU_IOAS_ALLOC, iommufd_ioas_alloc_ioctl,
|
||||
|
|
|
@ -46,6 +46,7 @@ enum {
|
|||
IOMMUFD_CMD_OPTION,
|
||||
IOMMUFD_CMD_VFIO_IOAS,
|
||||
IOMMUFD_CMD_HWPT_ALLOC,
|
||||
IOMMUFD_CMD_GET_HW_INFO,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -379,4 +380,42 @@ struct iommu_hwpt_alloc {
|
|||
enum iommu_hw_info_type {
|
||||
IOMMU_HW_INFO_TYPE_NONE,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iommu_hw_info - ioctl(IOMMU_GET_HW_INFO)
|
||||
* @size: sizeof(struct iommu_hw_info)
|
||||
* @flags: Must be 0
|
||||
* @dev_id: The device bound to the iommufd
|
||||
* @data_len: Input the length of a user buffer in bytes. Output the length of
|
||||
* data that kernel supports
|
||||
* @data_uptr: User pointer to a user-space buffer used by the kernel to fill
|
||||
* the iommu type specific hardware information data
|
||||
* @out_data_type: Output the iommu hardware info type as defined in the enum
|
||||
* iommu_hw_info_type.
|
||||
* @__reserved: Must be 0
|
||||
*
|
||||
* Query an iommu type specific hardware information data from an iommu behind
|
||||
* a given device that has been bound to iommufd. This hardware info data will
|
||||
* be used to sync capabilities between the virtual iommu and the physical
|
||||
* iommu, e.g. a nested translation setup needs to check the hardware info, so
|
||||
* a guest stage-1 page table can be compatible with the physical iommu.
|
||||
*
|
||||
* To capture an iommu type specific hardware information data, @data_uptr and
|
||||
* its length @data_len must be provided. Trailing bytes will be zeroed if the
|
||||
* user buffer is larger than the data that kernel has. Otherwise, kernel only
|
||||
* fills the buffer using the given length in @data_len. If the ioctl succeeds,
|
||||
* @data_len will be updated to the length that kernel actually supports,
|
||||
* @out_data_type will be filled to decode the data filled in the buffer
|
||||
* pointed by @data_uptr. Input @data_len == zero is allowed.
|
||||
*/
|
||||
struct iommu_hw_info {
|
||||
__u32 size;
|
||||
__u32 flags;
|
||||
__u32 dev_id;
|
||||
__u32 data_len;
|
||||
__aligned_u64 data_uptr;
|
||||
__u32 out_data_type;
|
||||
__u32 __reserved;
|
||||
};
|
||||
#define IOMMU_GET_HW_INFO _IO(IOMMUFD_TYPE, IOMMUFD_CMD_GET_HW_INFO)
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue