/* SPDX-License-Identifier: GPL-2.0-only */

#ifndef __GENERIC_PT_IOMMU_H
#define __GENERIC_PT_IOMMU_H

#include <linux/generic_pt/common.h>
#include <linux/iommu.h>
#include <linux/mm_types.h>

struct iommu_iotlb_gather;
struct pt_iommu_ops;
struct pt_iommu_driver_ops;
struct iommu_dirty_bitmap;




struct pt_iommu {
	
	struct iommu_domain domain;

	
	const struct pt_iommu_ops *ops;

	
	const struct pt_iommu_driver_ops *driver_ops;

	
	int nid;

	
	struct device *iommu_device;
};

static inline struct pt_iommu *iommupt_from_domain(struct iommu_domain *domain)
{
	if (!IS_ENABLED(CONFIG_IOMMU_PT) || !domain->is_iommupt)
		return NULL;
	return container_of(domain, struct pt_iommu, domain);
}


struct pt_iommu_info {
	
	u64 pgsize_bitmap;
};

struct pt_iommu_ops {
	
	int (*map_range)(struct pt_iommu *iommu_table, dma_addr_t iova,
			 phys_addr_t paddr, dma_addr_t len, unsigned int prot,
			 gfp_t gfp, size_t *mapped);

	
	size_t (*unmap_range)(struct pt_iommu *iommu_table, dma_addr_t iova,
			      dma_addr_t len,
			      struct iommu_iotlb_gather *iotlb_gather);

	
	int (*set_dirty)(struct pt_iommu *iommu_table, dma_addr_t iova);

	
	void (*get_info)(struct pt_iommu *iommu_table,
			 struct pt_iommu_info *info);

	
	void (*deinit)(struct pt_iommu *iommu_table);
};


struct pt_iommu_driver_ops {
	
	void (*change_top)(struct pt_iommu *iommu_table, phys_addr_t top_paddr,
			   unsigned int top_level);

	
	spinlock_t *(*get_top_lock)(struct pt_iommu *iommu_table);
};

static inline void pt_iommu_deinit(struct pt_iommu *iommu_table)
{
	
	if (iommu_table->ops)
		iommu_table->ops->deinit(iommu_table);
}


struct pt_iommu_cfg {
	
	unsigned int features;
	
	u8 hw_max_vasz_lg2;
	
	u8 hw_max_oasz_lg2;
};


#define IOMMU_PROTOTYPES(fmt)                                                  \
	phys_addr_t pt_iommu_##fmt##_iova_to_phys(struct iommu_domain *domain, \
						  dma_addr_t iova);            \
	int pt_iommu_##fmt##_read_and_clear_dirty(                             \
		struct iommu_domain *domain, unsigned long iova, size_t size,  \
		unsigned long flags, struct iommu_dirty_bitmap *dirty);        \
	int pt_iommu_##fmt##_init(struct pt_iommu_##fmt *table,                \
				  const struct pt_iommu_##fmt##_cfg *cfg,      \
				  gfp_t gfp);                                  \
	void pt_iommu_##fmt##_hw_info(struct pt_iommu_##fmt *table,            \
				      struct pt_iommu_##fmt##_hw_info *info)
#define IOMMU_FORMAT(fmt, member)       \
	struct pt_iommu_##fmt {         \
		struct pt_iommu iommu;  \
		struct pt_##fmt member; \
	};                              \
	IOMMU_PROTOTYPES(fmt)


#define IOMMU_PT_DOMAIN_OPS(fmt)                        \
	.iova_to_phys = &pt_iommu_##fmt##_iova_to_phys
#define IOMMU_PT_DIRTY_OPS(fmt) \
	.read_and_clear_dirty = &pt_iommu_##fmt##_read_and_clear_dirty


#define PT_IOMMU_CHECK_DOMAIN(s, pt_iommu_memb, domain_memb) \
	static_assert(offsetof(s, pt_iommu_memb.domain) ==   \
		      offsetof(s, domain_memb))

struct pt_iommu_amdv1_cfg {
	struct pt_iommu_cfg common;
	unsigned int starting_level;
};

struct pt_iommu_amdv1_hw_info {
	u64 host_pt_root;
	u8 mode;
};

IOMMU_FORMAT(amdv1, amdpt);


#define pt_iommu_amdv1_mock pt_iommu_amdv1
#define pt_iommu_amdv1_mock_cfg pt_iommu_amdv1_cfg
struct pt_iommu_amdv1_mock_hw_info;
IOMMU_PROTOTYPES(amdv1_mock);

struct pt_iommu_vtdss_cfg {
	struct pt_iommu_cfg common;
	
	unsigned int top_level;
};

struct pt_iommu_vtdss_hw_info {
	u64 ssptptr;
	u8 aw;
};

IOMMU_FORMAT(vtdss, vtdss_pt);

struct pt_iommu_x86_64_cfg {
	struct pt_iommu_cfg common;
	
	unsigned int top_level;
};

struct pt_iommu_x86_64_hw_info {
	u64 gcr3_pt;
	u8 levels;
};

IOMMU_FORMAT(x86_64, x86_64_pt);

#undef IOMMU_PROTOTYPES
#undef IOMMU_FORMAT
#endif
