#ifndef __amd_iommu_h__
#define __amd_iommu_h__
#define DEV_TAB_BASE_REG 0x0000
#define CMD_BASE_REG 0x0008
#define EVT_BASE_REG 0x0010
#define EXCL_BASE_REG 0x0020
#define EXCL_LIMIT_REG 0x0028
#define EXTFEAT_REG 0x0030
#define EFR_PREFSUP (1L << 0)
#define EFR_PPRSUP (1L << 1)
#define EFR_NXSUP (1L << 3)
#define EFR_GTSUP (1L << 4)
#define EFR_IASUP (1L << 6)
#define EFR_GASUP (1L << 7)
#define EFR_HESUP (1L << 8)
#define EFR_PCSUP (1L << 9)
#define EFR_HATS_SHIFT 10
#define EFR_HATS_MASK 0x3
#define EFR_GATS_SHIFT 12
#define EFR_GATS_MASK 0x3
#define EFR_GLXSUP_SHIFT 14
#define EFR_GLXSUP_MASK 0x3
#define EFR_SMIFSUP_SHIFT 16
#define EFR_SMIFSUP_MASK 0x3
#define EFR_SMIFRC_SHIFT 18
#define EFR_SMIFRC_MASK 0x7
#define EFR_GAMSUP_SHIFT 21
#define EFR_GAMSUP_MASK 0x7
#define CMD_HEAD_REG 0x2000
#define CMD_TAIL_REG 0x2008
#define EVT_HEAD_REG 0x2010
#define EVT_TAIL_REG 0x2018
#define IOMMUSTS_REG 0x2020
#define DEV_TAB_MASK 0x000FFFFFFFFFF000LL
#define DEV_TAB_LEN 0x1FF
#define IOMMUCTL_REG 0x0018
#define CTL_IOMMUEN (1L << 0)
#define CTL_HTTUNEN (1L << 1)
#define CTL_EVENTLOGEN (1L << 2)
#define CTL_EVENTINTEN (1L << 3)
#define CTL_COMWAITINTEN (1L << 4)
#define CTL_INVTIMEOUT_SHIFT 5
#define CTL_INVTIMEOUT_MASK 0x7
#define CTL_INVTIMEOUT_NONE 0
#define CTL_INVTIMEOUT_1MS 1
#define CTL_INVTIMEOUT_10MS 2
#define CTL_INVTIMEOUT_100MS 3
#define CTL_INVTIMEOUT_1S 4
#define CTL_INVTIMEOUT_10S 5
#define CTL_INVTIMEOUT_100S 6
#define CTL_PASSPW (1L << 8)
#define CTL_RESPASSPW (1L << 9)
#define CTL_COHERENT (1L << 10)
#define CTL_ISOC (1L << 11)
#define CTL_CMDBUFEN (1L << 12)
#define CTL_PPRLOGEN (1L << 13)
#define CTL_PPRINTEN (1L << 14)
#define CTL_PPREN (1L << 15)
#define CTL_GTEN (1L << 16)
#define CTL_GAEN (1L << 17)
#define CTL_CRW_SHIFT 18
#define CTL_CRW_MASK 0xF
#define CTL_SMIFEN (1L << 22)
#define CTL_SLFWBDIS (1L << 23)
#define CTL_SMIFLOGEN (1L << 24)
#define CTL_GAMEN_SHIFT 25
#define CTL_GAMEN_MASK 0x7
#define CTL_GALOGEN (1L << 28)
#define CTL_GAINTEN (1L << 29)
#define CTL_DUALPPRLOGEN_SHIFT 30
#define CTL_DUALPPRLOGEN_MASK 0x3
#define CTL_DUALEVTLOGEN_SHIFT 32
#define CTL_DUALEVTLOGEN_MASK 0x3
#define CTL_DEVTBLSEGEN_SHIFT 34
#define CTL_DEVTBLSEGEN_MASK 0x7
#define CTL_PRIVABRTEN_SHIFT 37
#define CTL_PRIVABRTEN_MASK 0x3
#define CTL_PPRAUTORSPEN (1LL << 39)
#define CTL_MARCEN (1LL << 40)
#define CTL_BLKSTOPMRKEN (1LL << 41)
#define CTL_PPRAUTOSPAON (1LL << 42)
#define CTL_DOMAINIDPNE (1LL << 43)
#define CMD_BASE_MASK 0x000FFFFFFFFFF000LL
#define CMD_TBL_SIZE 4096
#define CMD_TBL_LEN_4K (8LL << 56)
#define CMD_TBL_LEN_8K (9lL << 56)
#define EVT_BASE_MASK 0x000FFFFFFFFFF000LL
#define EVT_TBL_SIZE 4096
#define EVT_TBL_LEN_4K (8LL << 56)
#define EVT_TBL_LEN_8K (9LL << 56)
struct ivhd_dte {
uint32_t dw0;
uint32_t dw1;
uint32_t dw2;
uint32_t dw3;
uint32_t dw4;
uint32_t dw5;
uint32_t dw6;
uint32_t dw7;
} __packed;
#define HWDTE_SIZE (65536 * sizeof(struct ivhd_dte))
#define DTE_V (1L << 0)
#define DTE_TV (1L << 1)
#define DTE_LEVEL_SHIFT 9
#define DTE_LEVEL_MASK 0x7
#define DTE_HPTRP_MASK 0x000FFFFFFFFFF000LL
#define DTE_PPR (1L << 20)
#define DTE_GPRP (1L << 21)
#define DTE_GIOV (1L << 22)
#define DTE_GV (1L << 23)
#define DTE_IR (1L << 29)
#define DTE_IW (1L << 30)
#define DTE_DID_MASK 0xFFFF
#define DTE_IV (1L << 0)
#define DTE_SE (1L << 1)
#define DTE_SA (1L << 2)
#define DTE_INTTABLEN_SHIFT 1
#define DTE_INTTABLEN_MASK 0xF
#define DTE_IRTP_MASK 0x000FFFFFFFFFFFC0LL
#define PTE_LVL5 48
#define PTE_LVL4 39
#define PTE_LVL3 30
#define PTE_LVL2 21
#define PTE_LVL1 12
#define PTE_NXTLVL(x) (((x) & 0x7) << 9)
#define PTE_PADDR_MASK 0x000FFFFFFFFFF000LL
#define PTE_IR (1LL << 61)
#define PTE_IW (1LL << 62)
#define DTE_GCR312_MASK 0x3
#define DTE_GCR312_SHIFT 24
#define DTE_GCR315_MASK 0xFFFF
#define DTE_GCR315_SHIFT 16
#define DTE_GCR331_MASK 0xFFFFF
#define DTE_GCR331_SHIFT 12
#define _get64(x) *(uint64_t *)(x)
#define _put64(x,v) *(uint64_t *)(x) = (v)
static inline void
dte_set_guest_cr3(struct ivhd_dte *dte, paddr_t paddr)
{
iommu_rmw32(&dte->dw1, DTE_GCR312_MASK, DTE_GCR312_SHIFT, paddr >> 12);
iommu_rmw32(&dte->dw2, DTE_GCR315_MASK, DTE_GCR315_SHIFT, paddr >> 15);
iommu_rmw32(&dte->dw3, DTE_GCR331_MASK, DTE_GCR331_SHIFT, paddr >> 31);
}
static inline void
dte_set_interrupt_table_root_ptr(struct ivhd_dte *dte, paddr_t paddr)
{
uint64_t ov = _get64(&dte->dw4);
_put64(&dte->dw4, (ov & ~DTE_IRTP_MASK) | (paddr & DTE_IRTP_MASK));
}
static inline void
dte_set_interrupt_table_length(struct ivhd_dte *dte, int nEnt)
{
iommu_rmw32(&dte->dw4, DTE_INTTABLEN_MASK, DTE_INTTABLEN_SHIFT, nEnt);
}
static inline void
dte_set_interrupt_valid(struct ivhd_dte *dte)
{
dte->dw4 |= DTE_IV;
}
static inline void
dte_set_domain(struct ivhd_dte *dte, uint16_t did)
{
dte->dw2 = (dte->dw2 & ~DTE_DID_MASK) | (did & DTE_DID_MASK);
}
static inline void
dte_set_host_page_table_root_ptr(struct ivhd_dte *dte, paddr_t paddr)
{
uint64_t ov;
ov = _get64(&dte->dw0) & ~DTE_HPTRP_MASK;
ov |= (paddr & DTE_HPTRP_MASK) | PTE_IW | PTE_IR;
_put64(&dte->dw0, ov);
}
static inline void
dte_set_mode(struct ivhd_dte *dte, int mode)
{
iommu_rmw32(&dte->dw0, DTE_LEVEL_MASK, DTE_LEVEL_SHIFT, mode);
}
static inline void
dte_set_tv(struct ivhd_dte *dte)
{
dte->dw0 |= DTE_TV;
}
static inline void
dte_set_valid(struct ivhd_dte *dte)
{
dte->dw0 |= DTE_V;
}
static inline int
dte_is_valid(struct ivhd_dte *dte)
{
return (dte->dw0 & DTE_V);
}
struct ivhd_command {
uint32_t dw0;
uint32_t dw1;
uint32_t dw2;
uint32_t dw3;
} __packed;
#define CMD_SHIFT 28
enum {
COMPLETION_WAIT = 0x01,
INVALIDATE_DEVTAB_ENTRY = 0x02,
INVALIDATE_IOMMU_PAGES = 0x03,
INVALIDATE_IOTLB_PAGES = 0x04,
INVALIDATE_INTERRUPT_TABLE = 0x05,
PREFETCH_IOMMU_PAGES = 0x06,
COMPLETE_PPR_REQUEST = 0x07,
INVALIDATE_IOMMU_ALL = 0x08,
};
struct ivhd_event {
uint32_t dw0;
uint32_t dw1;
uint32_t dw2;
uint32_t dw3;
} __packed;
#define EVT_TYPE_SHIFT 28
#define EVT_TYPE_MASK 0xF
#define EVT_SID_SHIFT 0
#define EVT_SID_MASK 0xFFFF
#define EVT_DID_SHIFT 0
#define EVT_DID_MASK 0xFFFF
#define EVT_FLAG_SHIFT 16
#define EVT_FLAG_MASK 0xFFF
enum {
ILLEGAL_DEV_TABLE_ENTRY = 0x1,
IO_PAGE_FAULT = 0x2,
DEV_TAB_HARDWARE_ERROR = 0x3,
PAGE_TAB_HARDWARE_ERROR = 0x4,
ILLEGAL_COMMAND_ERROR = 0x5,
COMMAND_HARDWARE_ERROR = 0x6,
IOTLB_INV_TIMEOUT = 0x7,
INVALID_DEVICE_REQUEST = 0x8,
};
#define EVT_GN (1L << 16)
#define EVT_NX (1L << 17)
#define EVT_US (1L << 18)
#define EVT_I (1L << 19)
#define EVT_PR (1L << 20)
#define EVT_RW (1L << 21)
#define EVT_PE (1L << 22)
#define EVT_RZ (1L << 23)
#define EVT_TR (1L << 24)
struct iommu_softc;
int ivhd_flush_devtab(struct iommu_softc *, int);
int ivhd_invalidate_iommu_all(struct iommu_softc *);
int ivhd_invalidate_interrupt_table(struct iommu_softc *, int);
int ivhd_issue_command(struct iommu_softc *, const struct ivhd_command *, int);
int ivhd_invalidate_domain(struct iommu_softc *, int);
void _dumppte(struct pte_entry *, int, vaddr_t);
#endif