/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright ( C ) 2021 Intel Corporation
* Copyright ( c ) 2021 - 2022 , NVIDIA CORPORATION & AFFILIATES
*/
#ifndef __LINUX_IOMMUFD_H
#define __LINUX_IOMMUFD_H
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/iommu.h>
#include <linux/refcount.h>
#include <linux/types.h>
#include <linux/xarray.h>
#include <uapi/linux/iommufd.h>
struct device;
struct file;
struct iommu_group;
struct iommu_user_data;
struct iommu_user_data_array;
struct iommufd_access;
struct iommufd_ctx;
struct iommufd_device;
struct iommufd_viommu_ops;
struct page;
enum iommufd_object_type {
IOMMUFD_OBJ_NONE,
IOMMUFD_OBJ_ANY = IOMMUFD_OBJ_NONE,
IOMMUFD_OBJ_DEVICE,
IOMMUFD_OBJ_HWPT_PAGING,
IOMMUFD_OBJ_HWPT_NESTED,
IOMMUFD_OBJ_IOAS,
IOMMUFD_OBJ_ACCESS,
IOMMUFD_OBJ_FAULT,
IOMMUFD_OBJ_VIOMMU,
IOMMUFD_OBJ_VDEVICE,
IOMMUFD_OBJ_VEVENTQ,
IOMMUFD_OBJ_HW_QUEUE,
#ifdef CONFIG_IOMMUFD_TEST
IOMMUFD_OBJ_SELFTEST,
#endif
IOMMUFD_OBJ_MAX,
};
/* Base struct for all objects with a userspace ID handle. */
struct iommufd_object {
/*
* Destroy will sleep and wait for wait_cnt to go to zero . This allows
* concurrent users of the ID to reliably avoid causing a spurious
* destroy failure . Incrementing this count should either be short
* lived or be revoked and blocked during pre_destroy ( ) .
*/
refcount_t wait_cnt;
refcount_t users;
enum iommufd_object_type type;
unsigned int id;
};
struct iommufd_device *iommufd_device_bind(struct iommufd_ctx *ictx,
struct device *dev, u32 *id);
void iommufd_device_unbind(struct iommufd_device *idev);
int iommufd_device_attach(struct iommufd_device *idev, ioasid_t pasid,
u32 *pt_id);
int iommufd_device_replace(struct iommufd_device *idev, ioasid_t pasid,
u32 *pt_id);
void iommufd_device_detach(struct iommufd_device *idev, ioasid_t pasid);
struct iommufd_ctx *iommufd_device_to_ictx(struct iommufd_device *idev);
u32 iommufd_device_to_id(struct iommufd_device *idev);
struct iommufd_access_ops {
u8 needs_pin_pages : 1 ;
void (*unmap)(void *data, unsigned long iova, unsigned long length);
};
enum {
IOMMUFD_ACCESS_RW_READ = 0 ,
IOMMUFD_ACCESS_RW_WRITE = 1 << 0 ,
/* Set if the caller is in a kthread then rw will use kthread_use_mm() */
IOMMUFD_ACCESS_RW_KTHREAD = 1 << 1 ,
/* Only for use by selftest */
__IOMMUFD_ACCESS_RW_SLOW_PATH = 1 << 2 ,
};
struct iommufd_access *
iommufd_access_create(struct iommufd_ctx *ictx,
const struct iommufd_access_ops *ops, void *data, u32 *id);
void iommufd_access_destroy(struct iommufd_access *access);
int iommufd_access_attach(struct iommufd_access *access, u32 ioas_id);
int iommufd_access_replace(struct iommufd_access *access, u32 ioas_id);
void iommufd_access_detach(struct iommufd_access *access);
void iommufd_ctx_get(struct iommufd_ctx *ictx);
struct iommufd_viommu {
struct iommufd_object obj;
struct iommufd_ctx *ictx;
struct iommu_device *iommu_dev;
struct iommufd_hwpt_paging *hwpt;
const struct iommufd_viommu_ops *ops;
struct xarray vdevs;
struct list_head veventqs;
struct rw_semaphore veventqs_rwsem;
enum iommu_viommu_type type;
};
struct iommufd_vdevice {
struct iommufd_object obj;
struct iommufd_viommu *viommu;
struct iommufd_device *idev;
/*
* Virtual device ID per vIOMMU , e . g . vSID of ARM SMMUv3 , vDeviceID of
* AMD IOMMU , and vRID of Intel VT - d
*/
u64 virt_id;
/* Clean up all driver-specific parts of an iommufd_vdevice */
void (*destroy)(struct iommufd_vdevice *vdev);
};
struct iommufd_hw_queue {
struct iommufd_object obj;
struct iommufd_viommu *viommu;
struct iommufd_access *access;
u64 base_addr; /* in guest physical address space */
size_t length;
enum iommu_hw_queue_type type;
/* Clean up all driver-specific parts of an iommufd_hw_queue */
void (*destroy)(struct iommufd_hw_queue *hw_queue);
};
/**
* struct iommufd_viommu_ops - vIOMMU specific operations
* @ destroy : Clean up all driver - specific parts of an iommufd_viommu . The memory
* of the vIOMMU will be free - ed by iommufd core after calling this op
* @ alloc_domain_nested : Allocate a IOMMU_DOMAIN_NESTED on a vIOMMU that holds a
* nesting parent domain ( IOMMU_DOMAIN_PAGING ) . @ user_data
* must be defined in include / uapi / linux / iommufd . h .
* It must fully initialize the new iommu_domain before
* returning . Upon failure , ERR_PTR must be returned .
* @ cache_invalidate : Flush hardware cache used by a vIOMMU . It can be used for
* any IOMMU hardware specific cache : TLB and device cache .
* The @ array passes in the cache invalidation requests , in
* form of a driver data structure . A driver must update the
* array - > entry_num to report the number of handled requests .
* The data structure of the array entry must be defined in
* include / uapi / linux / iommufd . h
* @ vdevice_size : Size of the driver - defined vDEVICE structure per this vIOMMU
* @ vdevice_init : Initialize the driver - level structure of a vDEVICE object , or
* related HW procedure . @ vdev is already initialized by iommufd
* core : vdev - > dev and vdev - > viommu pointers ; vdev - > id carries a
* per - vIOMMU virtual ID ( refer to struct iommu_vdevice_alloc in
* include / uapi / linux / iommufd . h )
* If driver has a deinit function to revert what vdevice_init op
* does , it should set it to the @ vdev - > destroy function pointer
* @ get_hw_queue_size : Get the size of a driver - defined HW queue structure for a
* given @ viommu corresponding to @ queue_type . Driver should
* return 0 if HW queue aren ' t supported accordingly . It is
* required for driver to use the HW_QUEUE_STRUCT_SIZE macro
* to sanitize the driver - level HW queue structure related
* to the core one
* @ hw_queue_init_phys : Initialize the driver - level structure of a HW queue that
* is initialized with its core - level structure that holds
* all the info about a guest queue memory .
* Driver providing this op indicates that HW accesses the
* guest queue memory via physical addresses .
* @ index carries the logical HW QUEUE ID per vIOMMU in a
* guest VM , for a multi - queue model . @ base_addr_pa carries
* the physical location of the guest queue
* If driver has a deinit function to revert what this op
* does , it should set it to the @ hw_queue - > destroy pointer
*/
struct iommufd_viommu_ops {
void (*destroy)(struct iommufd_viommu *viommu);
struct iommu_domain *(*alloc_domain_nested)(
struct iommufd_viommu *viommu, u32 flags,
const struct iommu_user_data *user_data);
int (*cache_invalidate)(struct iommufd_viommu *viommu,
struct iommu_user_data_array *array);
const size_t vdevice_size;
int (*vdevice_init)(struct iommufd_vdevice *vdev);
size_t (*get_hw_queue_size)(struct iommufd_viommu *viommu,
enum iommu_hw_queue_type queue_type);
/* AMD's HW will add hw_queue_init simply using @hw_queue->base_addr */
int (*hw_queue_init_phys)(struct iommufd_hw_queue *hw_queue, u32 index,
phys_addr_t base_addr_pa);
};
#if IS_ENABLED(CONFIG_IOMMUFD)
struct iommufd_ctx *iommufd_ctx_from_file(struct file *file);
struct iommufd_ctx *iommufd_ctx_from_fd(int fd);
void iommufd_ctx_put(struct iommufd_ctx *ictx);
bool iommufd_ctx_has_group(struct iommufd_ctx *ictx, struct iommu_group *group);
int iommufd_access_pin_pages(struct iommufd_access *access, unsigned long iova,
unsigned long length, struct page **out_pages,
unsigned int flags);
void iommufd_access_unpin_pages(struct iommufd_access *access,
unsigned long iova, unsigned long length);
int iommufd_access_rw(struct iommufd_access *access, unsigned long iova,
void *data, size_t len, unsigned int flags);
int iommufd_vfio_compat_ioas_get_id(struct iommufd_ctx *ictx, u32 *out_ioas_id);
int iommufd_vfio_compat_ioas_create(struct iommufd_ctx *ictx);
int iommufd_vfio_compat_set_no_iommu(struct iommufd_ctx *ictx);
#else /* !CONFIG_IOMMUFD */
static inline struct iommufd_ctx *iommufd_ctx_from_file(struct file *file)
{
return ERR_PTR(-EOPNOTSUPP);
}
static inline void iommufd_ctx_put(struct iommufd_ctx *ictx)
{
}
static inline int iommufd_access_pin_pages(struct iommufd_access *access,
unsigned long iova,
unsigned long length,
struct page **out_pages,
unsigned int flags)
{
return -EOPNOTSUPP;
}
static inline void iommufd_access_unpin_pages(struct iommufd_access *access,
unsigned long iova,
unsigned long length)
{
}
static inline int iommufd_access_rw(struct iommufd_access *access,
unsigned long iova, void *data, size_t len,
unsigned int flags)
{
return -EOPNOTSUPP;
}
static inline int iommufd_vfio_compat_ioas_create(struct iommufd_ctx *ictx)
{
return -EOPNOTSUPP;
}
static inline int iommufd_vfio_compat_set_no_iommu(struct iommufd_ctx *ictx)
{
return -EOPNOTSUPP;
}
#endif /* CONFIG_IOMMUFD */
#if IS_ENABLED(CONFIG_IOMMUFD_DRIVER_CORE)
int _iommufd_object_depend(struct iommufd_object *obj_dependent,
struct iommufd_object *obj_depended);
void _iommufd_object_undepend(struct iommufd_object *obj_dependent,
struct iommufd_object *obj_depended);
int _iommufd_alloc_mmap(struct iommufd_ctx *ictx, struct iommufd_object *owner,
phys_addr_t mmio_addr, size_t length,
unsigned long *offset);
void _iommufd_destroy_mmap(struct iommufd_ctx *ictx,
struct iommufd_object *owner, unsigned long offset);
struct device *iommufd_vdevice_to_device(struct iommufd_vdevice *vdev);
struct device *iommufd_viommu_find_dev(struct iommufd_viommu *viommu,
unsigned long vdev_id);
int iommufd_viommu_get_vdev_id(struct iommufd_viommu *viommu,
struct device *dev, unsigned long *vdev_id);
int iommufd_viommu_report_event(struct iommufd_viommu *viommu,
enum iommu_veventq_type type, void *event_data,
size_t data_len);
#else /* !CONFIG_IOMMUFD_DRIVER_CORE */
static inline int _iommufd_object_depend(struct iommufd_object *obj_dependent,
struct iommufd_object *obj_depended)
{
return -EOPNOTSUPP;
}
static inline void
_iommufd_object_undepend(struct iommufd_object *obj_dependent,
struct iommufd_object *obj_depended)
{
}
static inline int _iommufd_alloc_mmap(struct iommufd_ctx *ictx,
struct iommufd_object *owner,
phys_addr_t mmio_addr, size_t length,
unsigned long *offset)
{
return -EOPNOTSUPP;
}
static inline void _iommufd_destroy_mmap(struct iommufd_ctx *ictx,
struct iommufd_object *owner,
unsigned long offset)
{
}
static inline struct device *
iommufd_vdevice_to_device(struct iommufd_vdevice *vdev)
{
return NULL;
}
static inline struct device *
iommufd_viommu_find_dev(struct iommufd_viommu *viommu, unsigned long vdev_id)
{
return NULL;
}
static inline int iommufd_viommu_get_vdev_id(struct iommufd_viommu *viommu,
struct device *dev,
unsigned long *vdev_id)
{
return -ENOENT;
}
static inline int iommufd_viommu_report_event(struct iommufd_viommu *viommu,
enum iommu_veventq_type type,
void *event_data, size_t data_len)
{
return -EOPNOTSUPP;
}
#endif /* CONFIG_IOMMUFD_DRIVER_CORE */
#define VIOMMU_STRUCT_SIZE(drv_struct, member) \
(sizeof (drv_struct) + \
BUILD_BUG_ON_ZERO(offsetof(drv_struct, member)) + \
BUILD_BUG_ON_ZERO(!__same_type(struct iommufd_viommu, \
((drv_struct *)NULL)->member)))
#define VDEVICE_STRUCT_SIZE(drv_struct, member) \
(sizeof (drv_struct) + \
BUILD_BUG_ON_ZERO(offsetof(drv_struct, member)) + \
BUILD_BUG_ON_ZERO(!__same_type(struct iommufd_vdevice, \
((drv_struct *)NULL)->member)))
#define HW_QUEUE_STRUCT_SIZE(drv_struct, member) \
(sizeof (drv_struct) + \
BUILD_BUG_ON_ZERO(offsetof(drv_struct, member)) + \
BUILD_BUG_ON_ZERO(!__same_type(struct iommufd_hw_queue, \
((drv_struct *)NULL)->member)))
/*
* Helpers for IOMMU driver to build / destroy a dependency between two sibling
* structures created by one of the allocators above
*/
#define iommufd_hw_queue_depend(dependent, depended, member) \
({ \
int ret = -EINVAL; \
\
static_assert(__same_type(struct iommufd_hw_queue, \
dependent->member)); \
static_assert(__same_type(typeof(*dependent), *depended)); \
if (!WARN_ON_ONCE(dependent->member.viommu != \
depended->member.viommu)) \
ret = _iommufd_object_depend(&dependent->member.obj, \
&depended->member.obj); \
ret; \
})
#define iommufd_hw_queue_undepend(dependent, depended, member) \
({ \
static_assert(__same_type(struct iommufd_hw_queue, \
dependent->member)); \
static_assert(__same_type(typeof(*dependent), *depended)); \
WARN_ON_ONCE(dependent->member.viommu != \
depended->member.viommu); \
_iommufd_object_undepend(&dependent->member.obj, \
&depended->member.obj); \
})
/*
* Helpers for IOMMU driver to alloc / destroy an mmapable area for a structure .
*
* To support an mmappable MMIO region , kernel driver must first register it to
* iommufd core to allocate an @ offset , during a driver - structure initialization
* ( e . g . viommu_init op ) . Then , it should report to user space this @ offset and
* the @ length of the MMIO region for mmap syscall .
*/
static inline int iommufd_viommu_alloc_mmap(struct iommufd_viommu *viommu,
phys_addr_t mmio_addr,
size_t length,
unsigned long *offset)
{
return _iommufd_alloc_mmap(viommu->ictx, &viommu->obj, mmio_addr,
length, offset);
}
static inline void iommufd_viommu_destroy_mmap(struct iommufd_viommu *viommu,
unsigned long offset)
{
_iommufd_destroy_mmap(viommu->ictx, &viommu->obj, offset);
}
#endif
Messung V0.5 in Prozent C=96 H=100 G=97
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland