iommufd.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. /*
  2. * iommufd container backend
  3. *
  4. * Copyright (C) 2023 Intel Corporation.
  5. * Copyright Red Hat, Inc. 2023
  6. *
  7. * Authors: Yi Liu <yi.l.liu@intel.com>
  8. * Eric Auger <eric.auger@redhat.com>
  9. *
  10. * SPDX-License-Identifier: GPL-2.0-or-later
  11. */
  12. #include "qemu/osdep.h"
  13. #include "system/iommufd.h"
  14. #include "qapi/error.h"
  15. #include "qemu/module.h"
  16. #include "qom/object_interfaces.h"
  17. #include "qemu/error-report.h"
  18. #include "monitor/monitor.h"
  19. #include "trace.h"
  20. #include "hw/vfio/vfio-common.h"
  21. #include <sys/ioctl.h>
  22. #include <linux/iommufd.h>
  23. static void iommufd_backend_init(Object *obj)
  24. {
  25. IOMMUFDBackend *be = IOMMUFD_BACKEND(obj);
  26. be->fd = -1;
  27. be->users = 0;
  28. be->owned = true;
  29. }
  30. static void iommufd_backend_finalize(Object *obj)
  31. {
  32. IOMMUFDBackend *be = IOMMUFD_BACKEND(obj);
  33. if (be->owned) {
  34. close(be->fd);
  35. be->fd = -1;
  36. }
  37. }
  38. static void iommufd_backend_set_fd(Object *obj, const char *str, Error **errp)
  39. {
  40. ERRP_GUARD();
  41. IOMMUFDBackend *be = IOMMUFD_BACKEND(obj);
  42. int fd = -1;
  43. fd = monitor_fd_param(monitor_cur(), str, errp);
  44. if (fd == -1) {
  45. error_prepend(errp, "Could not parse remote object fd %s:", str);
  46. return;
  47. }
  48. be->fd = fd;
  49. be->owned = false;
  50. trace_iommu_backend_set_fd(be->fd);
  51. }
  52. static bool iommufd_backend_can_be_deleted(UserCreatable *uc)
  53. {
  54. IOMMUFDBackend *be = IOMMUFD_BACKEND(uc);
  55. return !be->users;
  56. }
  57. static void iommufd_backend_class_init(ObjectClass *oc, void *data)
  58. {
  59. UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
  60. ucc->can_be_deleted = iommufd_backend_can_be_deleted;
  61. object_class_property_add_str(oc, "fd", NULL, iommufd_backend_set_fd);
  62. }
  63. bool iommufd_backend_connect(IOMMUFDBackend *be, Error **errp)
  64. {
  65. int fd;
  66. if (be->owned && !be->users) {
  67. fd = qemu_open("/dev/iommu", O_RDWR, errp);
  68. if (fd < 0) {
  69. return false;
  70. }
  71. be->fd = fd;
  72. }
  73. be->users++;
  74. trace_iommufd_backend_connect(be->fd, be->owned, be->users);
  75. return true;
  76. }
  77. void iommufd_backend_disconnect(IOMMUFDBackend *be)
  78. {
  79. if (!be->users) {
  80. goto out;
  81. }
  82. be->users--;
  83. if (!be->users && be->owned) {
  84. close(be->fd);
  85. be->fd = -1;
  86. }
  87. out:
  88. trace_iommufd_backend_disconnect(be->fd, be->users);
  89. }
  90. bool iommufd_backend_alloc_ioas(IOMMUFDBackend *be, uint32_t *ioas_id,
  91. Error **errp)
  92. {
  93. int fd = be->fd;
  94. struct iommu_ioas_alloc alloc_data = {
  95. .size = sizeof(alloc_data),
  96. .flags = 0,
  97. };
  98. if (ioctl(fd, IOMMU_IOAS_ALLOC, &alloc_data)) {
  99. error_setg_errno(errp, errno, "Failed to allocate ioas");
  100. return false;
  101. }
  102. *ioas_id = alloc_data.out_ioas_id;
  103. trace_iommufd_backend_alloc_ioas(fd, *ioas_id);
  104. return true;
  105. }
  106. void iommufd_backend_free_id(IOMMUFDBackend *be, uint32_t id)
  107. {
  108. int ret, fd = be->fd;
  109. struct iommu_destroy des = {
  110. .size = sizeof(des),
  111. .id = id,
  112. };
  113. ret = ioctl(fd, IOMMU_DESTROY, &des);
  114. trace_iommufd_backend_free_id(fd, id, ret);
  115. if (ret) {
  116. error_report("Failed to free id: %u %m", id);
  117. }
  118. }
  119. int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova,
  120. ram_addr_t size, void *vaddr, bool readonly)
  121. {
  122. int ret, fd = be->fd;
  123. struct iommu_ioas_map map = {
  124. .size = sizeof(map),
  125. .flags = IOMMU_IOAS_MAP_READABLE |
  126. IOMMU_IOAS_MAP_FIXED_IOVA,
  127. .ioas_id = ioas_id,
  128. .__reserved = 0,
  129. .user_va = (uintptr_t)vaddr,
  130. .iova = iova,
  131. .length = size,
  132. };
  133. if (!readonly) {
  134. map.flags |= IOMMU_IOAS_MAP_WRITEABLE;
  135. }
  136. ret = ioctl(fd, IOMMU_IOAS_MAP, &map);
  137. trace_iommufd_backend_map_dma(fd, ioas_id, iova, size,
  138. vaddr, readonly, ret);
  139. if (ret) {
  140. ret = -errno;
  141. /* TODO: Not support mapping hardware PCI BAR region for now. */
  142. if (errno == EFAULT) {
  143. warn_report("IOMMU_IOAS_MAP failed: %m, PCI BAR?");
  144. } else {
  145. error_report("IOMMU_IOAS_MAP failed: %m");
  146. }
  147. }
  148. return ret;
  149. }
  150. int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id,
  151. hwaddr iova, ram_addr_t size)
  152. {
  153. int ret, fd = be->fd;
  154. struct iommu_ioas_unmap unmap = {
  155. .size = sizeof(unmap),
  156. .ioas_id = ioas_id,
  157. .iova = iova,
  158. .length = size,
  159. };
  160. ret = ioctl(fd, IOMMU_IOAS_UNMAP, &unmap);
  161. /*
  162. * IOMMUFD takes mapping as some kind of object, unmapping
  163. * nonexistent mapping is treated as deleting a nonexistent
  164. * object and return ENOENT. This is different from legacy
  165. * backend which allows it. vIOMMU may trigger a lot of
  166. * redundant unmapping, to avoid flush the log, treat them
  167. * as succeess for IOMMUFD just like legacy backend.
  168. */
  169. if (ret && errno == ENOENT) {
  170. trace_iommufd_backend_unmap_dma_non_exist(fd, ioas_id, iova, size, ret);
  171. ret = 0;
  172. } else {
  173. trace_iommufd_backend_unmap_dma(fd, ioas_id, iova, size, ret);
  174. }
  175. if (ret) {
  176. ret = -errno;
  177. error_report("IOMMU_IOAS_UNMAP failed: %m");
  178. }
  179. return ret;
  180. }
  181. bool iommufd_backend_alloc_hwpt(IOMMUFDBackend *be, uint32_t dev_id,
  182. uint32_t pt_id, uint32_t flags,
  183. uint32_t data_type, uint32_t data_len,
  184. void *data_ptr, uint32_t *out_hwpt,
  185. Error **errp)
  186. {
  187. int ret, fd = be->fd;
  188. struct iommu_hwpt_alloc alloc_hwpt = {
  189. .size = sizeof(struct iommu_hwpt_alloc),
  190. .flags = flags,
  191. .dev_id = dev_id,
  192. .pt_id = pt_id,
  193. .data_type = data_type,
  194. .data_len = data_len,
  195. .data_uptr = (uintptr_t)data_ptr,
  196. };
  197. ret = ioctl(fd, IOMMU_HWPT_ALLOC, &alloc_hwpt);
  198. trace_iommufd_backend_alloc_hwpt(fd, dev_id, pt_id, flags, data_type,
  199. data_len, (uintptr_t)data_ptr,
  200. alloc_hwpt.out_hwpt_id, ret);
  201. if (ret) {
  202. error_setg_errno(errp, errno, "Failed to allocate hwpt");
  203. return false;
  204. }
  205. *out_hwpt = alloc_hwpt.out_hwpt_id;
  206. return true;
  207. }
  208. bool iommufd_backend_set_dirty_tracking(IOMMUFDBackend *be,
  209. uint32_t hwpt_id, bool start,
  210. Error **errp)
  211. {
  212. int ret;
  213. struct iommu_hwpt_set_dirty_tracking set_dirty = {
  214. .size = sizeof(set_dirty),
  215. .hwpt_id = hwpt_id,
  216. .flags = start ? IOMMU_HWPT_DIRTY_TRACKING_ENABLE : 0,
  217. };
  218. ret = ioctl(be->fd, IOMMU_HWPT_SET_DIRTY_TRACKING, &set_dirty);
  219. trace_iommufd_backend_set_dirty(be->fd, hwpt_id, start, ret ? errno : 0);
  220. if (ret) {
  221. error_setg_errno(errp, errno,
  222. "IOMMU_HWPT_SET_DIRTY_TRACKING(hwpt_id %u) failed",
  223. hwpt_id);
  224. return false;
  225. }
  226. return true;
  227. }
  228. bool iommufd_backend_get_dirty_bitmap(IOMMUFDBackend *be,
  229. uint32_t hwpt_id,
  230. uint64_t iova, ram_addr_t size,
  231. uint64_t page_size, uint64_t *data,
  232. Error **errp)
  233. {
  234. int ret;
  235. struct iommu_hwpt_get_dirty_bitmap get_dirty_bitmap = {
  236. .size = sizeof(get_dirty_bitmap),
  237. .hwpt_id = hwpt_id,
  238. .iova = iova,
  239. .length = size,
  240. .page_size = page_size,
  241. .data = (uintptr_t)data,
  242. };
  243. ret = ioctl(be->fd, IOMMU_HWPT_GET_DIRTY_BITMAP, &get_dirty_bitmap);
  244. trace_iommufd_backend_get_dirty_bitmap(be->fd, hwpt_id, iova, size,
  245. page_size, ret ? errno : 0);
  246. if (ret) {
  247. error_setg_errno(errp, errno,
  248. "IOMMU_HWPT_GET_DIRTY_BITMAP (iova: 0x%"HWADDR_PRIx
  249. " size: 0x"RAM_ADDR_FMT") failed", iova, size);
  250. return false;
  251. }
  252. return true;
  253. }
  254. bool iommufd_backend_get_device_info(IOMMUFDBackend *be, uint32_t devid,
  255. uint32_t *type, void *data, uint32_t len,
  256. uint64_t *caps, Error **errp)
  257. {
  258. struct iommu_hw_info info = {
  259. .size = sizeof(info),
  260. .dev_id = devid,
  261. .data_len = len,
  262. .data_uptr = (uintptr_t)data,
  263. };
  264. if (ioctl(be->fd, IOMMU_GET_HW_INFO, &info)) {
  265. error_setg_errno(errp, errno, "Failed to get hardware info");
  266. return false;
  267. }
  268. g_assert(type);
  269. *type = info.out_data_type;
  270. g_assert(caps);
  271. *caps = info.out_capabilities;
  272. return true;
  273. }
  274. static int hiod_iommufd_get_cap(HostIOMMUDevice *hiod, int cap, Error **errp)
  275. {
  276. HostIOMMUDeviceCaps *caps = &hiod->caps;
  277. switch (cap) {
  278. case HOST_IOMMU_DEVICE_CAP_IOMMU_TYPE:
  279. return caps->type;
  280. case HOST_IOMMU_DEVICE_CAP_AW_BITS:
  281. return vfio_device_get_aw_bits(hiod->agent);
  282. default:
  283. error_setg(errp, "%s: unsupported capability %x", hiod->name, cap);
  284. return -EINVAL;
  285. }
  286. }
  287. static void hiod_iommufd_class_init(ObjectClass *oc, void *data)
  288. {
  289. HostIOMMUDeviceClass *hioc = HOST_IOMMU_DEVICE_CLASS(oc);
  290. hioc->get_cap = hiod_iommufd_get_cap;
  291. };
  292. static const TypeInfo types[] = {
  293. {
  294. .name = TYPE_IOMMUFD_BACKEND,
  295. .parent = TYPE_OBJECT,
  296. .instance_size = sizeof(IOMMUFDBackend),
  297. .instance_init = iommufd_backend_init,
  298. .instance_finalize = iommufd_backend_finalize,
  299. .class_size = sizeof(IOMMUFDBackendClass),
  300. .class_init = iommufd_backend_class_init,
  301. .interfaces = (InterfaceInfo[]) {
  302. { TYPE_USER_CREATABLE },
  303. { }
  304. }
  305. }, {
  306. .name = TYPE_HOST_IOMMU_DEVICE_IOMMUFD,
  307. .parent = TYPE_HOST_IOMMU_DEVICE,
  308. .class_init = hiod_iommufd_class_init,
  309. .abstract = true,
  310. }
  311. };
  312. DEFINE_TYPES(types)