/* * Copyright (c) 2019 Jordan Hargrave * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #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 /* Extended Feature Register */ #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 /* IOMMU Control */ #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) /*======================== * DEVICE TABLE ENTRY * Contains mapping of bus-device-function * * 0 Valid (V) * 1 Translation Valid (TV) * 7:8 Host Address Dirty (HAD) * 9:11 Page Table Depth (usually 4) * 12:51 Page Table Physical Address * 52 PPR Enable * 53 GPRP * 54 Guest I/O Protection Valid (GIoV) * 55 Guest Translation Valid (GV) * 56:57 Guest Levels translated (GLX) * 58:60 Guest CR3 bits 12:14 (GCR3TRP) * 61 I/O Read Permission (IR) * 62 I/O Write Permission (IW) * 64:79 Domain ID * 80:95 Guest CR3 bits 15:30 (GCR3TRP) * 96 IOTLB Enable (I) * 97 Suppress multiple I/O page faults (I) * 98 Supress all I/O page faults (SA) * 99:100 Port I/O Control (IoCTL) * 101 Cache IOTLB Hint * 102 Snoop Disable (SD) * 103 Allow Exclusion (EX) * 104:105 System Management Message (SysMgt) * 107:127 Guest CR3 bits 31:51 (GCR3TRP) * 128 Interrupt Map Valid (IV) * 129:132 Interrupt Table Length (IntTabLen) *========================*/ 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) /* dw0 */ #define DTE_TV (1L << 1) /* dw0 */ #define DTE_LEVEL_SHIFT 9 /* dw0 */ #define DTE_LEVEL_MASK 0x7 /* dw0 */ #define DTE_HPTRP_MASK 0x000FFFFFFFFFF000LL /* dw0,1 */ #define DTE_PPR (1L << 20) /* dw1 */ #define DTE_GPRP (1L << 21) /* dw1 */ #define DTE_GIOV (1L << 22) /* dw1 */ #define DTE_GV (1L << 23) /* dw1 */ #define DTE_IR (1L << 29) /* dw1 */ #define DTE_IW (1L << 30) /* dw1 */ #define DTE_DID_MASK 0xFFFF /* dw2 */ #define DTE_IV (1L << 0) /* dw3 */ #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) /* Set Guest CR3 address */ 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); } /* Set Interrupt Remapping Root Pointer */ 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)); } /* Set Interrupt Remapping Table length */ 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); } /* Set Interrupt Remapping Valid */ static inline void dte_set_interrupt_valid(struct ivhd_dte *dte) { dte->dw4 |= DTE_IV; } /* Set Domain ID in Device Table Entry */ static inline void dte_set_domain(struct ivhd_dte *dte, uint16_t did) { dte->dw2 = (dte->dw2 & ~DTE_DID_MASK) | (did & DTE_DID_MASK); } /* Set Page Table Pointer for device */ 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); } /* Set Page Table Levels Mask */ 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; } /* Set Device Table Entry valid. * Domain/Level/Mode/PageTable should already be set */ static inline void dte_set_valid(struct ivhd_dte *dte) { dte->dw0 |= DTE_V; } /* Check if Device Table Entry is valid */ static inline int dte_is_valid(struct ivhd_dte *dte) { return (dte->dw0 & DTE_V); } /*========================================= * COMMAND *=========================================*/ 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, }; /*========================================= * EVENT *=========================================*/ 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 /* IOMMU Fault reasons */ 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