|
4 | 4 | #include <linux/iommufd.h> |
5 | 5 | #include <linux/slab.h> |
6 | 6 | #include <linux/iommu.h> |
| 7 | +#include <uapi/linux/iommufd.h> |
7 | 8 | #include "../iommu-priv.h" |
8 | 9 |
|
9 | 10 | #include "io_pagetable.h" |
@@ -1119,3 +1120,75 @@ int iommufd_access_rw(struct iommufd_access *access, unsigned long iova, |
1119 | 1120 | return rc; |
1120 | 1121 | } |
1121 | 1122 | 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 | +} |
0 commit comments