196940271Sjordan /*
296940271Sjordan * Copyright (c) 2019 Jordan Hargrave <jordan_hargrave@hotmail.com>
396940271Sjordan *
496940271Sjordan * Permission to use, copy, modify, and distribute this software for any
596940271Sjordan * purpose with or without fee is hereby granted, provided that the above
696940271Sjordan * copyright notice and this permission notice appear in all copies.
796940271Sjordan *
896940271Sjordan * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
996940271Sjordan * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1096940271Sjordan * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1196940271Sjordan * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1296940271Sjordan * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1396940271Sjordan * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1496940271Sjordan * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1596940271Sjordan */
1696940271Sjordan #ifndef __amd_iommu_h__
1796940271Sjordan #define __amd_iommu_h__
1896940271Sjordan
1996940271Sjordan #define DEV_TAB_BASE_REG 0x0000
2096940271Sjordan #define CMD_BASE_REG 0x0008
2196940271Sjordan #define EVT_BASE_REG 0x0010
2296940271Sjordan
2396940271Sjordan #define EXCL_BASE_REG 0x0020
2496940271Sjordan #define EXCL_LIMIT_REG 0x0028
2596940271Sjordan
2696940271Sjordan /* Extended Feature Register */
2796940271Sjordan #define EXTFEAT_REG 0x0030
2896940271Sjordan #define EFR_PREFSUP (1L << 0)
2996940271Sjordan #define EFR_PPRSUP (1L << 1)
3096940271Sjordan #define EFR_NXSUP (1L << 3)
3196940271Sjordan #define EFR_GTSUP (1L << 4)
3296940271Sjordan #define EFR_IASUP (1L << 6)
3396940271Sjordan #define EFR_GASUP (1L << 7)
3496940271Sjordan #define EFR_HESUP (1L << 8)
3596940271Sjordan #define EFR_PCSUP (1L << 9)
3696940271Sjordan #define EFR_HATS_SHIFT 10
3796940271Sjordan #define EFR_HATS_MASK 0x3
3896940271Sjordan #define EFR_GATS_SHIFT 12
3996940271Sjordan #define EFR_GATS_MASK 0x3
4096940271Sjordan #define EFR_GLXSUP_SHIFT 14
4196940271Sjordan #define EFR_GLXSUP_MASK 0x3
4296940271Sjordan #define EFR_SMIFSUP_SHIFT 16
4396940271Sjordan #define EFR_SMIFSUP_MASK 0x3
4496940271Sjordan #define EFR_SMIFRC_SHIFT 18
4596940271Sjordan #define EFR_SMIFRC_MASK 0x7
4696940271Sjordan #define EFR_GAMSUP_SHIFT 21
4796940271Sjordan #define EFR_GAMSUP_MASK 0x7
4896940271Sjordan
4996940271Sjordan #define CMD_HEAD_REG 0x2000
5096940271Sjordan #define CMD_TAIL_REG 0x2008
5196940271Sjordan #define EVT_HEAD_REG 0x2010
5296940271Sjordan #define EVT_TAIL_REG 0x2018
5396940271Sjordan
5496940271Sjordan #define IOMMUSTS_REG 0x2020
5596940271Sjordan
5696940271Sjordan #define DEV_TAB_MASK 0x000FFFFFFFFFF000LL
5796940271Sjordan #define DEV_TAB_LEN 0x1FF
5896940271Sjordan
5996940271Sjordan /* IOMMU Control */
6096940271Sjordan #define IOMMUCTL_REG 0x0018
6196940271Sjordan #define CTL_IOMMUEN (1L << 0)
6296940271Sjordan #define CTL_HTTUNEN (1L << 1)
6396940271Sjordan #define CTL_EVENTLOGEN (1L << 2)
6496940271Sjordan #define CTL_EVENTINTEN (1L << 3)
6596940271Sjordan #define CTL_COMWAITINTEN (1L << 4)
6696940271Sjordan #define CTL_INVTIMEOUT_SHIFT 5
6796940271Sjordan #define CTL_INVTIMEOUT_MASK 0x7
6896940271Sjordan #define CTL_INVTIMEOUT_NONE 0
6996940271Sjordan #define CTL_INVTIMEOUT_1MS 1
7096940271Sjordan #define CTL_INVTIMEOUT_10MS 2
7196940271Sjordan #define CTL_INVTIMEOUT_100MS 3
7296940271Sjordan #define CTL_INVTIMEOUT_1S 4
7396940271Sjordan #define CTL_INVTIMEOUT_10S 5
7496940271Sjordan #define CTL_INVTIMEOUT_100S 6
7596940271Sjordan #define CTL_PASSPW (1L << 8)
7696940271Sjordan #define CTL_RESPASSPW (1L << 9)
7796940271Sjordan #define CTL_COHERENT (1L << 10)
7896940271Sjordan #define CTL_ISOC (1L << 11)
7996940271Sjordan #define CTL_CMDBUFEN (1L << 12)
8096940271Sjordan #define CTL_PPRLOGEN (1L << 13)
8196940271Sjordan #define CTL_PPRINTEN (1L << 14)
8296940271Sjordan #define CTL_PPREN (1L << 15)
8396940271Sjordan #define CTL_GTEN (1L << 16)
8496940271Sjordan #define CTL_GAEN (1L << 17)
8596940271Sjordan #define CTL_CRW_SHIFT 18
8696940271Sjordan #define CTL_CRW_MASK 0xF
8796940271Sjordan #define CTL_SMIFEN (1L << 22)
8896940271Sjordan #define CTL_SLFWBDIS (1L << 23)
8996940271Sjordan #define CTL_SMIFLOGEN (1L << 24)
9096940271Sjordan #define CTL_GAMEN_SHIFT 25
9196940271Sjordan #define CTL_GAMEN_MASK 0x7
9296940271Sjordan #define CTL_GALOGEN (1L << 28)
9396940271Sjordan #define CTL_GAINTEN (1L << 29)
9496940271Sjordan #define CTL_DUALPPRLOGEN_SHIFT 30
9596940271Sjordan #define CTL_DUALPPRLOGEN_MASK 0x3
9696940271Sjordan #define CTL_DUALEVTLOGEN_SHIFT 32
9796940271Sjordan #define CTL_DUALEVTLOGEN_MASK 0x3
9896940271Sjordan #define CTL_DEVTBLSEGEN_SHIFT 34
9996940271Sjordan #define CTL_DEVTBLSEGEN_MASK 0x7
10096940271Sjordan #define CTL_PRIVABRTEN_SHIFT 37
10196940271Sjordan #define CTL_PRIVABRTEN_MASK 0x3
10296940271Sjordan #define CTL_PPRAUTORSPEN (1LL << 39)
10396940271Sjordan #define CTL_MARCEN (1LL << 40)
10496940271Sjordan #define CTL_BLKSTOPMRKEN (1LL << 41)
10596940271Sjordan #define CTL_PPRAUTOSPAON (1LL << 42)
10696940271Sjordan #define CTL_DOMAINIDPNE (1LL << 43)
10796940271Sjordan
10896940271Sjordan #define CMD_BASE_MASK 0x000FFFFFFFFFF000LL
10996940271Sjordan #define CMD_TBL_SIZE 4096
11096940271Sjordan #define CMD_TBL_LEN_4K (8LL << 56)
11196940271Sjordan #define CMD_TBL_LEN_8K (9lL << 56)
11296940271Sjordan
11396940271Sjordan #define EVT_BASE_MASK 0x000FFFFFFFFFF000LL
11496940271Sjordan #define EVT_TBL_SIZE 4096
11596940271Sjordan #define EVT_TBL_LEN_4K (8LL << 56)
11696940271Sjordan #define EVT_TBL_LEN_8K (9LL << 56)
11796940271Sjordan
11896940271Sjordan /*========================
11996940271Sjordan * DEVICE TABLE ENTRY
12096940271Sjordan * Contains mapping of bus-device-function
12196940271Sjordan *
12296940271Sjordan * 0 Valid (V)
12396940271Sjordan * 1 Translation Valid (TV)
12496940271Sjordan * 7:8 Host Address Dirty (HAD)
12596940271Sjordan * 9:11 Page Table Depth (usually 4)
12696940271Sjordan * 12:51 Page Table Physical Address
12796940271Sjordan * 52 PPR Enable
12896940271Sjordan * 53 GPRP
12996940271Sjordan * 54 Guest I/O Protection Valid (GIoV)
13096940271Sjordan * 55 Guest Translation Valid (GV)
13196940271Sjordan * 56:57 Guest Levels translated (GLX)
13296940271Sjordan * 58:60 Guest CR3 bits 12:14 (GCR3TRP)
13396940271Sjordan * 61 I/O Read Permission (IR)
13496940271Sjordan * 62 I/O Write Permission (IW)
13596940271Sjordan * 64:79 Domain ID
13696940271Sjordan * 80:95 Guest CR3 bits 15:30 (GCR3TRP)
13796940271Sjordan * 96 IOTLB Enable (I)
13896940271Sjordan * 97 Suppress multiple I/O page faults (I)
139*4b1a56afSjsg * 98 Suppress all I/O page faults (SA)
14096940271Sjordan * 99:100 Port I/O Control (IoCTL)
14196940271Sjordan * 101 Cache IOTLB Hint
14296940271Sjordan * 102 Snoop Disable (SD)
14396940271Sjordan * 103 Allow Exclusion (EX)
14496940271Sjordan * 104:105 System Management Message (SysMgt)
14596940271Sjordan * 107:127 Guest CR3 bits 31:51 (GCR3TRP)
14696940271Sjordan * 128 Interrupt Map Valid (IV)
14796940271Sjordan * 129:132 Interrupt Table Length (IntTabLen)
14896940271Sjordan *========================*/
14996940271Sjordan struct ivhd_dte {
15096940271Sjordan uint32_t dw0;
15196940271Sjordan uint32_t dw1;
15296940271Sjordan uint32_t dw2;
15396940271Sjordan uint32_t dw3;
15496940271Sjordan uint32_t dw4;
15596940271Sjordan uint32_t dw5;
15696940271Sjordan uint32_t dw6;
15796940271Sjordan uint32_t dw7;
15896940271Sjordan } __packed;
15996940271Sjordan
16096940271Sjordan #define HWDTE_SIZE (65536 * sizeof(struct ivhd_dte))
16196940271Sjordan
16296940271Sjordan #define DTE_V (1L << 0) /* dw0 */
16396940271Sjordan #define DTE_TV (1L << 1) /* dw0 */
16496940271Sjordan #define DTE_LEVEL_SHIFT 9 /* dw0 */
16596940271Sjordan #define DTE_LEVEL_MASK 0x7 /* dw0 */
16696940271Sjordan #define DTE_HPTRP_MASK 0x000FFFFFFFFFF000LL /* dw0,1 */
16796940271Sjordan
16896940271Sjordan #define DTE_PPR (1L << 20) /* dw1 */
16996940271Sjordan #define DTE_GPRP (1L << 21) /* dw1 */
17096940271Sjordan #define DTE_GIOV (1L << 22) /* dw1 */
17196940271Sjordan #define DTE_GV (1L << 23) /* dw1 */
17296940271Sjordan #define DTE_IR (1L << 29) /* dw1 */
17396940271Sjordan #define DTE_IW (1L << 30) /* dw1 */
17496940271Sjordan
17596940271Sjordan #define DTE_DID_MASK 0xFFFF /* dw2 */
17696940271Sjordan
17796940271Sjordan #define DTE_IV (1L << 0) /* dw3 */
17896940271Sjordan #define DTE_SE (1L << 1)
17996940271Sjordan #define DTE_SA (1L << 2)
18096940271Sjordan #define DTE_INTTABLEN_SHIFT 1
18196940271Sjordan #define DTE_INTTABLEN_MASK 0xF
18296940271Sjordan #define DTE_IRTP_MASK 0x000FFFFFFFFFFFC0LL
18396940271Sjordan
18496940271Sjordan #define PTE_LVL5 48
18596940271Sjordan #define PTE_LVL4 39
18696940271Sjordan #define PTE_LVL3 30
18796940271Sjordan #define PTE_LVL2 21
18896940271Sjordan #define PTE_LVL1 12
18996940271Sjordan
19096940271Sjordan #define PTE_NXTLVL(x) (((x) & 0x7) << 9)
19196940271Sjordan #define PTE_PADDR_MASK 0x000FFFFFFFFFF000LL
19296940271Sjordan #define PTE_IR (1LL << 61)
19396940271Sjordan #define PTE_IW (1LL << 62)
19496940271Sjordan
19596940271Sjordan #define DTE_GCR312_MASK 0x3
19696940271Sjordan #define DTE_GCR312_SHIFT 24
19796940271Sjordan
19896940271Sjordan #define DTE_GCR315_MASK 0xFFFF
19996940271Sjordan #define DTE_GCR315_SHIFT 16
20096940271Sjordan
20196940271Sjordan #define DTE_GCR331_MASK 0xFFFFF
20296940271Sjordan #define DTE_GCR331_SHIFT 12
20396940271Sjordan
20496940271Sjordan #define _get64(x) *(uint64_t *)(x)
20596940271Sjordan #define _put64(x,v) *(uint64_t *)(x) = (v)
20696940271Sjordan
20796940271Sjordan /* Set Guest CR3 address */
20896940271Sjordan static inline void
dte_set_guest_cr3(struct ivhd_dte * dte,paddr_t paddr)20996940271Sjordan dte_set_guest_cr3(struct ivhd_dte *dte, paddr_t paddr)
21096940271Sjordan {
21196940271Sjordan iommu_rmw32(&dte->dw1, DTE_GCR312_MASK, DTE_GCR312_SHIFT, paddr >> 12);
21296940271Sjordan iommu_rmw32(&dte->dw2, DTE_GCR315_MASK, DTE_GCR315_SHIFT, paddr >> 15);
21396940271Sjordan iommu_rmw32(&dte->dw3, DTE_GCR331_MASK, DTE_GCR331_SHIFT, paddr >> 31);
21496940271Sjordan }
21596940271Sjordan
21696940271Sjordan /* Set Interrupt Remapping Root Pointer */
21796940271Sjordan static inline void
dte_set_interrupt_table_root_ptr(struct ivhd_dte * dte,paddr_t paddr)21896940271Sjordan dte_set_interrupt_table_root_ptr(struct ivhd_dte *dte, paddr_t paddr)
21996940271Sjordan {
22096940271Sjordan uint64_t ov = _get64(&dte->dw4);
22196940271Sjordan _put64(&dte->dw4, (ov & ~DTE_IRTP_MASK) | (paddr & DTE_IRTP_MASK));
22296940271Sjordan }
22396940271Sjordan
22496940271Sjordan /* Set Interrupt Remapping Table length */
22596940271Sjordan static inline void
dte_set_interrupt_table_length(struct ivhd_dte * dte,int nEnt)22696940271Sjordan dte_set_interrupt_table_length(struct ivhd_dte *dte, int nEnt)
22796940271Sjordan {
22896940271Sjordan iommu_rmw32(&dte->dw4, DTE_INTTABLEN_MASK, DTE_INTTABLEN_SHIFT, nEnt);
22996940271Sjordan }
23096940271Sjordan
23196940271Sjordan /* Set Interrupt Remapping Valid */
23296940271Sjordan static inline void
dte_set_interrupt_valid(struct ivhd_dte * dte)23396940271Sjordan dte_set_interrupt_valid(struct ivhd_dte *dte)
23496940271Sjordan {
23596940271Sjordan dte->dw4 |= DTE_IV;
23696940271Sjordan }
23796940271Sjordan
23896940271Sjordan /* Set Domain ID in Device Table Entry */
23996940271Sjordan static inline void
dte_set_domain(struct ivhd_dte * dte,uint16_t did)24096940271Sjordan dte_set_domain(struct ivhd_dte *dte, uint16_t did)
24196940271Sjordan {
24296940271Sjordan dte->dw2 = (dte->dw2 & ~DTE_DID_MASK) | (did & DTE_DID_MASK);
24396940271Sjordan }
24496940271Sjordan
24596940271Sjordan /* Set Page Table Pointer for device */
24696940271Sjordan static inline void
dte_set_host_page_table_root_ptr(struct ivhd_dte * dte,paddr_t paddr)24796940271Sjordan dte_set_host_page_table_root_ptr(struct ivhd_dte *dte, paddr_t paddr)
24896940271Sjordan {
24996940271Sjordan uint64_t ov;
25096940271Sjordan
25196940271Sjordan ov = _get64(&dte->dw0) & ~DTE_HPTRP_MASK;
25296940271Sjordan ov |= (paddr & DTE_HPTRP_MASK) | PTE_IW | PTE_IR;
25396940271Sjordan
25496940271Sjordan _put64(&dte->dw0, ov);
25596940271Sjordan }
25696940271Sjordan
25796940271Sjordan /* Set Page Table Levels Mask */
25896940271Sjordan static inline void
dte_set_mode(struct ivhd_dte * dte,int mode)25996940271Sjordan dte_set_mode(struct ivhd_dte *dte, int mode)
26096940271Sjordan {
26196940271Sjordan iommu_rmw32(&dte->dw0, DTE_LEVEL_MASK, DTE_LEVEL_SHIFT, mode);
26296940271Sjordan }
26396940271Sjordan
26496940271Sjordan static inline void
dte_set_tv(struct ivhd_dte * dte)26596940271Sjordan dte_set_tv(struct ivhd_dte *dte)
26696940271Sjordan {
26796940271Sjordan dte->dw0 |= DTE_TV;
26896940271Sjordan }
26996940271Sjordan
27096940271Sjordan /* Set Device Table Entry valid.
27196940271Sjordan * Domain/Level/Mode/PageTable should already be set
27296940271Sjordan */
27396940271Sjordan static inline void
dte_set_valid(struct ivhd_dte * dte)27496940271Sjordan dte_set_valid(struct ivhd_dte *dte)
27596940271Sjordan {
27696940271Sjordan dte->dw0 |= DTE_V;
27796940271Sjordan }
27896940271Sjordan
27996940271Sjordan /* Check if Device Table Entry is valid */
28096940271Sjordan static inline int
dte_is_valid(struct ivhd_dte * dte)28196940271Sjordan dte_is_valid(struct ivhd_dte *dte)
28296940271Sjordan {
28396940271Sjordan return (dte->dw0 & DTE_V);
28496940271Sjordan }
28596940271Sjordan
28696940271Sjordan /*=========================================
28796940271Sjordan * COMMAND
28896940271Sjordan *=========================================*/
28996940271Sjordan struct ivhd_command {
29096940271Sjordan uint32_t dw0;
29196940271Sjordan uint32_t dw1;
29296940271Sjordan uint32_t dw2;
29396940271Sjordan uint32_t dw3;
29496940271Sjordan } __packed;
29596940271Sjordan
29696940271Sjordan #define CMD_SHIFT 28
29796940271Sjordan
29896940271Sjordan enum {
29996940271Sjordan COMPLETION_WAIT = 0x01,
30096940271Sjordan INVALIDATE_DEVTAB_ENTRY = 0x02,
30196940271Sjordan INVALIDATE_IOMMU_PAGES = 0x03,
30296940271Sjordan INVALIDATE_IOTLB_PAGES = 0x04,
30396940271Sjordan INVALIDATE_INTERRUPT_TABLE = 0x05,
30496940271Sjordan PREFETCH_IOMMU_PAGES = 0x06,
30596940271Sjordan COMPLETE_PPR_REQUEST = 0x07,
30696940271Sjordan INVALIDATE_IOMMU_ALL = 0x08,
30796940271Sjordan };
30896940271Sjordan
30996940271Sjordan /*=========================================
31096940271Sjordan * EVENT
31196940271Sjordan *=========================================*/
31296940271Sjordan struct ivhd_event {
31396940271Sjordan uint32_t dw0;
31496940271Sjordan uint32_t dw1;
31596940271Sjordan uint32_t dw2;
31696940271Sjordan uint32_t dw3;
31796940271Sjordan } __packed;
31896940271Sjordan
31996940271Sjordan #define EVT_TYPE_SHIFT 28
32096940271Sjordan #define EVT_TYPE_MASK 0xF
32196940271Sjordan #define EVT_SID_SHIFT 0
32296940271Sjordan #define EVT_SID_MASK 0xFFFF
32396940271Sjordan #define EVT_DID_SHIFT 0
32496940271Sjordan #define EVT_DID_MASK 0xFFFF
32596940271Sjordan #define EVT_FLAG_SHIFT 16
32696940271Sjordan #define EVT_FLAG_MASK 0xFFF
32796940271Sjordan
32896940271Sjordan /* IOMMU Fault reasons */
32996940271Sjordan enum {
33096940271Sjordan ILLEGAL_DEV_TABLE_ENTRY = 0x1,
33196940271Sjordan IO_PAGE_FAULT = 0x2,
33296940271Sjordan DEV_TAB_HARDWARE_ERROR = 0x3,
33396940271Sjordan PAGE_TAB_HARDWARE_ERROR = 0x4,
33496940271Sjordan ILLEGAL_COMMAND_ERROR = 0x5,
33596940271Sjordan COMMAND_HARDWARE_ERROR = 0x6,
33696940271Sjordan IOTLB_INV_TIMEOUT = 0x7,
33796940271Sjordan INVALID_DEVICE_REQUEST = 0x8,
33896940271Sjordan };
33996940271Sjordan
34096940271Sjordan #define EVT_GN (1L << 16)
34196940271Sjordan #define EVT_NX (1L << 17)
34296940271Sjordan #define EVT_US (1L << 18)
34396940271Sjordan #define EVT_I (1L << 19)
34496940271Sjordan #define EVT_PR (1L << 20)
34596940271Sjordan #define EVT_RW (1L << 21)
34696940271Sjordan #define EVT_PE (1L << 22)
34796940271Sjordan #define EVT_RZ (1L << 23)
34896940271Sjordan #define EVT_TR (1L << 24)
34996940271Sjordan
35096940271Sjordan struct iommu_softc;
35196940271Sjordan
35296940271Sjordan int ivhd_flush_devtab(struct iommu_softc *, int);
35396940271Sjordan int ivhd_invalidate_iommu_all(struct iommu_softc *);
35496940271Sjordan int ivhd_invalidate_interrupt_table(struct iommu_softc *, int);
35596940271Sjordan int ivhd_issue_command(struct iommu_softc *, const struct ivhd_command *, int);
35696940271Sjordan int ivhd_invalidate_domain(struct iommu_softc *, int);
35796940271Sjordan
35896940271Sjordan void _dumppte(struct pte_entry *, int, vaddr_t);
35996940271Sjordan
36096940271Sjordan #endif
361