Skip to content

Commit 55dd402

Browse files
yiliu1765jgunthorpe
authored andcommitted
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>
1 parent 60fedb2 commit 55dd402

File tree

4 files changed

+116
-0
lines changed

4 files changed

+116
-0
lines changed

drivers/iommu/iommufd/device.c

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <linux/iommufd.h>
55
#include <linux/slab.h>
66
#include <linux/iommu.h>
7+
#include <uapi/linux/iommufd.h>
78
#include "../iommu-priv.h"
89

910
#include "io_pagetable.h"
@@ -1119,3 +1120,75 @@ int iommufd_access_rw(struct iommufd_access *access, unsigned long iova,
11191120
return rc;
11201121
}
11211122
EXPORT_SYMBOL_NS_GPL(iommufd_access_rw, IOMMUFD);
1123+
1124+
int iommufd_get_hw_info(struct iommufd_ucmd *ucmd)
1125+
{
1126+
struct iommu_hw_info *cmd = ucmd->cmd;
1127+
void __user *user_ptr = u64_to_user_ptr(cmd->data_uptr);
1128+
const struct iommu_ops *ops;
1129+
struct iommufd_device *idev;
1130+
unsigned int data_len;
1131+
unsigned int copy_len;
1132+
void *data;
1133+
int rc;
1134+
1135+
if (cmd->flags || cmd->__reserved)
1136+
return -EOPNOTSUPP;
1137+
1138+
idev = iommufd_get_device(ucmd, cmd->dev_id);
1139+
if (IS_ERR(idev))
1140+
return PTR_ERR(idev);
1141+
1142+
ops = dev_iommu_ops(idev->dev);
1143+
if (ops->hw_info) {
1144+
data = ops->hw_info(idev->dev, &data_len, &cmd->out_data_type);
1145+
if (IS_ERR(data)) {
1146+
rc = PTR_ERR(data);
1147+
goto out_put;
1148+
}
1149+
1150+
/*
1151+
* drivers that have hw_info callback should have a unique
1152+
* iommu_hw_info_type.
1153+
*/
1154+
if (WARN_ON_ONCE(cmd->out_data_type ==
1155+
IOMMU_HW_INFO_TYPE_NONE)) {
1156+
rc = -ENODEV;
1157+
goto out_free;
1158+
}
1159+
} else {
1160+
cmd->out_data_type = IOMMU_HW_INFO_TYPE_NONE;
1161+
data_len = 0;
1162+
data = NULL;
1163+
}
1164+
1165+
copy_len = min(cmd->data_len, data_len);
1166+
if (copy_to_user(user_ptr, data, copy_len)) {
1167+
rc = -EFAULT;
1168+
goto out_free;
1169+
}
1170+
1171+
/*
1172+
* Zero the trailing bytes if the user buffer is bigger than the
1173+
* data size kernel actually has.
1174+
*/
1175+
if (copy_len < cmd->data_len) {
1176+
if (clear_user(user_ptr + copy_len, cmd->data_len - copy_len)) {
1177+
rc = -EFAULT;
1178+
goto out_free;
1179+
}
1180+
}
1181+
1182+
/*
1183+
* We return the length the kernel supports so userspace may know what
1184+
* the kernel capability is. It could be larger than the input buffer.
1185+
*/
1186+
cmd->data_len = data_len;
1187+
1188+
rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd));
1189+
out_free:
1190+
kfree(data);
1191+
out_put:
1192+
iommufd_put_object(&idev->obj);
1193+
return rc;
1194+
}

drivers/iommu/iommufd/iommufd_private.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ iommufd_get_device(struct iommufd_ucmd *ucmd, u32 id)
296296
}
297297

298298
void iommufd_device_destroy(struct iommufd_object *obj);
299+
int iommufd_get_hw_info(struct iommufd_ucmd *ucmd);
299300

300301
struct iommufd_access {
301302
struct iommufd_object obj;

drivers/iommu/iommufd/main.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ static int iommufd_option(struct iommufd_ucmd *ucmd)
305305

306306
union ucmd_buffer {
307307
struct iommu_destroy destroy;
308+
struct iommu_hw_info info;
308309
struct iommu_hwpt_alloc hwpt;
309310
struct iommu_ioas_alloc alloc;
310311
struct iommu_ioas_allow_iovas allow_iovas;
@@ -337,6 +338,8 @@ struct iommufd_ioctl_op {
337338
}
338339
static const struct iommufd_ioctl_op iommufd_ioctl_ops[] = {
339340
IOCTL_OP(IOMMU_DESTROY, iommufd_destroy, struct iommu_destroy, id),
341+
IOCTL_OP(IOMMU_GET_HW_INFO, iommufd_get_hw_info, struct iommu_hw_info,
342+
__reserved),
340343
IOCTL_OP(IOMMU_HWPT_ALLOC, iommufd_hwpt_alloc, struct iommu_hwpt_alloc,
341344
__reserved),
342345
IOCTL_OP(IOMMU_IOAS_ALLOC, iommufd_ioas_alloc_ioctl,

include/uapi/linux/iommufd.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ enum {
4646
IOMMUFD_CMD_OPTION,
4747
IOMMUFD_CMD_VFIO_IOAS,
4848
IOMMUFD_CMD_HWPT_ALLOC,
49+
IOMMUFD_CMD_GET_HW_INFO,
4950
};
5051

5152
/**
@@ -379,4 +380,42 @@ struct iommu_hwpt_alloc {
379380
enum iommu_hw_info_type {
380381
IOMMU_HW_INFO_TYPE_NONE,
381382
};
383+
384+
/**
385+
* struct iommu_hw_info - ioctl(IOMMU_GET_HW_INFO)
386+
* @size: sizeof(struct iommu_hw_info)
387+
* @flags: Must be 0
388+
* @dev_id: The device bound to the iommufd
389+
* @data_len: Input the length of a user buffer in bytes. Output the length of
390+
* data that kernel supports
391+
* @data_uptr: User pointer to a user-space buffer used by the kernel to fill
392+
* the iommu type specific hardware information data
393+
* @out_data_type: Output the iommu hardware info type as defined in the enum
394+
* iommu_hw_info_type.
395+
* @__reserved: Must be 0
396+
*
397+
* Query an iommu type specific hardware information data from an iommu behind
398+
* a given device that has been bound to iommufd. This hardware info data will
399+
* be used to sync capabilities between the virtual iommu and the physical
400+
* iommu, e.g. a nested translation setup needs to check the hardware info, so
401+
* a guest stage-1 page table can be compatible with the physical iommu.
402+
*
403+
* To capture an iommu type specific hardware information data, @data_uptr and
404+
* its length @data_len must be provided. Trailing bytes will be zeroed if the
405+
* user buffer is larger than the data that kernel has. Otherwise, kernel only
406+
* fills the buffer using the given length in @data_len. If the ioctl succeeds,
407+
* @data_len will be updated to the length that kernel actually supports,
408+
* @out_data_type will be filled to decode the data filled in the buffer
409+
* pointed by @data_uptr. Input @data_len == zero is allowed.
410+
*/
411+
struct iommu_hw_info {
412+
__u32 size;
413+
__u32 flags;
414+
__u32 dev_id;
415+
__u32 data_len;
416+
__aligned_u64 data_uptr;
417+
__u32 out_data_type;
418+
__u32 __reserved;
419+
};
420+
#define IOMMU_GET_HW_INFO _IO(IOMMUFD_TYPE, IOMMUFD_CMD_GET_HW_INFO)
382421
#endif

0 commit comments

Comments
 (0)