/* * Copyright (c) 2015 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 _DEV_ACPI_DMARREG_H_ #define _DEV_ACPI_DMARREG_H_ /*#define IOMMU_DEBUG*/ #define VTD_STRIDE_MASK 0x1FF #define VTD_STRIDE_SIZE 9 #define VTD_PAGE_SIZE 4096 #define VTD_PAGE_MASK 0xFFF #define VTD_PTE_MASK 0x0000FFFFFFFFF000LL #define VTD_LEVEL0 12 #define VTD_LEVEL1 21 #define VTD_LEVEL2 30 /* Minimum level supported */ #define VTD_LEVEL3 39 /* Also supported */ #define VTD_LEVEL4 48 #define VTD_LEVEL5 57 #define _xbit(x,y) (((x)>> (y)) & 1) #define _xfld(x,y) (uint32_t)(((x)>> y##_SHIFT) & y##_MASK) #define VTD_AWTOLEVEL(x) (((x) - 30) / VTD_STRIDE_SIZE) #define VTD_LEVELTOAW(x) (((x) * VTD_STRIDE_SIZE) + 30) #define DMAR_VER_REG 0x00 /* 32:Arch version supported by this IOMMU */ #define DMAR_RTADDR_REG 0x20 /* 64:Root entry table */ #define DMAR_FEDATA_REG 0x3c /* 32:Fault event interrupt data register */ #define DMAR_FEADDR_REG 0x40 /* 32:Fault event interrupt addr register */ #define DMAR_FEUADDR_REG 0x44 /* 32:Upper address register */ #define DMAR_AFLOG_REG 0x58 /* 64:Advanced Fault control */ #define DMAR_PMEN_REG 0x64 /* 32:Enable Protected Memory Region */ #define DMAR_PLMBASE_REG 0x68 /* 32:PMRR Low addr */ #define DMAR_PLMLIMIT_REG 0x6c /* 32:PMRR low limit */ #define DMAR_PHMBASE_REG 0x70 /* 64:pmrr high base addr */ #define DMAR_PHMLIMIT_REG 0x78 /* 64:pmrr high limit */ #define DMAR_ICS_REG 0x9C /* 32:Invalidation complete status register */ #define DMAR_IECTL_REG 0xa0 /* 32:Invalidation event control register */ #define DMAR_IEDATA_REG 0xa4 /* 32:Invalidation event data register */ #define DMAR_IEADDR_REG 0xa8 /* 32:Invalidation event address register */ #define DMAR_IEUADDR_REG 0xac /* 32:Invalidation event upper address register */ #define DMAR_IRTA_REG 0xb8 /* 64:Interrupt remapping table addr register */ #define DMAR_CAP_REG 0x08 /* 64:Hardware supported capabilities */ #define CAP_PI (1LL << 59) #define CAP_FL1GP (1LL << 56) #define CAP_DRD (1LL << 55) #define CAP_DWD (1LL << 54) #define CAP_MAMV_MASK 0x3F #define CAP_MAMV_SHIFT 48LL #define cap_mamv(x) _xfld(x,CAP_MAMV) #define CAP_NFR_MASK 0xFF #define CAP_NFR_SHIFT 40LL #define cap_nfr(x) (_xfld(x,CAP_NFR) + 1) #define CAP_PSI (1LL << 39) #define CAP_SLLPS_MASK 0xF #define CAP_SLLPS_SHIFT 34LL #define cap_sllps(x) _xfld(x,CAP_SLLPS) #define CAP_FRO_MASK 0x3FF #define CAP_FRO_SHIFT 24LL #define cap_fro(x) (_xfld(x,CAP_FRO) * 16) #define CAP_ZLR (1LL << 22) #define CAP_MGAW_MASK 0x3F #define CAP_MGAW_SHIFT 16LL #define cap_mgaw(x) (_xfld(x,CAP_MGAW) + 1) #define CAP_SAGAW_MASK 0x1F #define CAP_SAGAW_SHIFT 8LL #define cap_sagaw(x) _xfld(x,CAP_SAGAW) #define CAP_CM (1LL << 7) #define CAP_PHMR (1LL << 6) #define CAP_PLMR (1LL << 5) #define CAP_RWBF (1LL << 4) #define CAP_AFL (1LL << 3) #define CAP_ND_MASK 0x7 #define CAP_ND_SHIFT 0x00 #define cap_nd(x) (16 << (((x) & CAP_ND_MASK) << 1)) #define DMAR_ECAP_REG 0x10 /* 64:Extended capabilities supported */ #define ECAP_PSS_MASK 0x1F #define ECAP_PSS_SHIFT 35 #define ECAP_EAFS (1LL << 34) #define ECAP_NWFS (1LL << 33) #define ECAP_SRS (1LL << 31) #define ECAP_ERS (1LL << 30) #define ECAP_PRS (1LL << 29) #define ECAP_PASID (1LL << 28) #define ECAP_DIS (1LL << 27) #define ECAP_NEST (1LL << 26) #define ECAP_MTS (1LL << 25) #define ECAP_ECS (1LL << 24) #define ECAP_MHMV_MASK 0xF #define ECAP_MHMV_SHIFT 0x20 #define ecap_mhmv(x) _xfld(x,ECAP_MHMV) #define ECAP_IRO_MASK 0x3FF /* IOTLB Register */ #define ECAP_IRO_SHIFT 0x8 #define ecap_iro(x) (_xfld(x,ECAP_IRO) * 16) #define ECAP_SC (1LL << 7) /* Snoop Control */ #define ECAP_PT (1LL << 6) /* HW Passthru */ #define ECAP_EIM (1LL << 4) #define ECAP_IR (1LL << 3) /* Interrupt remap */ #define ECAP_DT (1LL << 2) /* Device IOTLB */ #define ECAP_QI (1LL << 1) /* Queued Invalidation */ #define ECAP_C (1LL << 0) /* Coherent cache */ #define DMAR_GCMD_REG 0x18 /* 32:Global command register */ #define GCMD_TE (1LL << 31) #define GCMD_SRTP (1LL << 30) #define GCMD_SFL (1LL << 29) #define GCMD_EAFL (1LL << 28) #define GCMD_WBF (1LL << 27) #define GCMD_QIE (1LL << 26) #define GCMD_IRE (1LL << 25) #define GCMD_SIRTP (1LL << 24) #define GCMD_CFI (1LL << 23) #define DMAR_GSTS_REG 0x1c /* 32:Global status register */ #define GSTS_TES (1LL << 31) #define GSTS_RTPS (1LL << 30) #define GSTS_FLS (1LL << 29) #define GSTS_AFLS (1LL << 28) #define GSTS_WBFS (1LL << 27) #define GSTS_QIES (1LL << 26) #define GSTS_IRES (1LL << 25) #define GSTS_IRTPS (1LL << 24) #define GSTS_CFIS (1LL << 23) #define DMAR_CCMD_REG 0x28 /* 64:Context command reg */ #define CCMD_ICC (1LL << 63) #define CCMD_CIRG_MASK 0x3 #define CCMD_CIRG_SHIFT 61 #define CCMD_CIRG(x) ((uint64_t)(x) << CCMD_CIRG_SHIFT) #define CCMD_CAIG_MASK 0x3 #define CCMD_CAIG_SHIFT 59 #define CCMD_FM_MASK 0x3 #define CCMD_FM_SHIFT 32 #define CCMD_FM(x) (((uint64_t)(x) << CCMD_FM_SHIFT)) #define CCMD_SID_MASK 0xFFFF #define CCMD_SID_SHIFT 8 #define CCMD_SID(x) (((x) << CCMD_SID_SHIFT)) #define CCMD_DID_MASK 0xFFFF #define CCMD_DID_SHIFT 0 #define CCMD_DID(x) (((x) << CCMD_DID_SHIFT)) #define CIG_GLOBAL CCMD_CIRG(CTX_GLOBAL) #define CIG_DOMAIN CCMD_CIRG(CTX_DOMAIN) #define CIG_DEVICE CCMD_CIRG(CTX_DEVICE) #define DMAR_FSTS_REG 0x34 /* 32:Fault Status register */ #define FSTS_FRI_MASK 0xFF #define FSTS_FRI_SHIFT 8 #define FSTS_PRO (1LL << 7) #define FSTS_ITE (1LL << 6) #define FSTS_ICE (1LL << 5) #define FSTS_IQE (1LL << 4) #define FSTS_APF (1LL << 3) #define FSTS_APO (1LL << 2) #define FSTS_PPF (1LL << 1) #define FSTS_PFO (1LL << 0) #define DMAR_FECTL_REG 0x38 /* 32:Fault control register */ #define FECTL_IM (1LL << 31) #define FECTL_IP (1LL << 30) #define FRCD_HI_F (1LL << (127-64)) #define FRCD_HI_T (1LL << (126-64)) #define FRCD_HI_AT_MASK 0x3 #define FRCD_HI_AT_SHIFT (124-64) #define FRCD_HI_PV_MASK 0xFFFFF #define FRCD_HI_PV_SHIFT (104-64) #define FRCD_HI_FR_MASK 0xFF #define FRCD_HI_FR_SHIFT (96-64) #define FRCD_HI_PP (1LL << (95-64)) #define FRCD_HI_SID_MASK 0xFF #define FRCD_HI_SID_SHIFT 0 #define FRCD_HI_BUS_SHIFT 8 #define FRCD_HI_BUS_MASK 0xFF #define FRCD_HI_DEV_SHIFT 3 #define FRCD_HI_DEV_MASK 0x1F #define FRCD_HI_FUN_SHIFT 0 #define FRCD_HI_FUN_MASK 0x7 #define DMAR_IOTLB_REG(x) (ecap_iro((x)->ecap) + 8) #define DMAR_IVA_REG(x) (ecap_iro((x)->ecap) + 0) #define DMAR_FRIH_REG(x,i) (cap_fro((x)->cap) + 16*(i) + 8) #define DMAR_FRIL_REG(x,i) (cap_fro((x)->cap) + 16*(i) + 0) #define IOTLB_IVT (1LL << 63) #define IOTLB_IIRG_MASK 0x3 #define IOTLB_IIRG_SHIFT 60 #define IOTLB_IIRG(x) ((uint64_t)(x) << IOTLB_IIRG_SHIFT) #define IOTLB_IAIG_MASK 0x3 #define IOTLB_IAIG_SHIFT 57 #define IOTLB_DR (1LL << 49) #define IOTLB_DW (1LL << 48) #define IOTLB_DID_MASK 0xFFFF #define IOTLB_DID_SHIFT 32 #define IOTLB_DID(x) ((uint64_t)(x) << IOTLB_DID_SHIFT) #define IIG_GLOBAL IOTLB_IIRG(IOTLB_GLOBAL) #define IIG_DOMAIN IOTLB_IIRG(IOTLB_DOMAIN) #define IIG_PAGE IOTLB_IIRG(IOTLB_PAGE) #define DMAR_IQH_REG 0x80 /* 64:Invalidation queue head register */ #define DMAR_IQT_REG 0x88 /* 64:Invalidation queue tail register */ #define DMAR_IQA_REG 0x90 /* 64:Invalidation queue addr register */ #define IQA_QS_256 0 /* 256 entries */ #define IQA_QS_512 1 /* 512 */ #define IQA_QS_1K 2 /* 1024 */ #define IQA_QS_2K 3 /* 2048 */ #define IQA_QS_4K 4 /* 4096 */ #define IQA_QS_8K 5 /* 8192 */ #define IQA_QS_16K 6 /* 16384 */ #define IQA_QS_32K 7 /* 32768 */ /* Read-Modify-Write helpers */ static inline void iommu_rmw32(void *ov, uint32_t mask, uint32_t shift, uint32_t nv) { *(uint32_t *)ov &= ~(mask << shift); *(uint32_t *)ov |= (nv & mask) << shift; } static inline void iommu_rmw64(void *ov, uint32_t mask, uint32_t shift, uint64_t nv) { *(uint64_t *)ov &= ~(mask << shift); *(uint64_t *)ov |= (nv & mask) << shift; } /* * Root Entry: one per bus (256 x 128 bit = 4k) * 0 = Present * 1:11 = Reserved * 12:HAW-1 = Context Table Pointer * HAW:63 = Reserved * 64:127 = Reserved */ #define ROOT_P (1L << 0) struct root_entry { uint64_t lo; uint64_t hi; }; /* Check if root entry is valid */ static inline bool root_entry_is_valid(struct root_entry *re) { return (re->lo & ROOT_P); } /* * Context Entry: one per devfn (256 x 128 bit = 4k) * 0 = Present * 1 = Fault Processing Disable * 2:3 = Translation Type * 4:11 = Reserved * 12:63 = Second Level Page Translation * 64:66 = Address Width (# PTE levels) * 67:70 = Ignore * 71 = Reserved * 72:87 = Domain ID * 88:127 = Reserved */ #define CTX_P (1L << 0) #define CTX_FPD (1L << 1) #define CTX_T_MASK 0x3 #define CTX_T_SHIFT 2 enum { CTX_T_MULTI, CTX_T_IOTLB, CTX_T_PASSTHRU }; #define CTX_H_AW_MASK 0x7 #define CTX_H_AW_SHIFT 0 #define CTX_H_USER_MASK 0xF #define CTX_H_USER_SHIFT 3 #define CTX_H_DID_MASK 0xFFFF #define CTX_H_DID_SHIFT 8 struct context_entry { uint64_t lo; uint64_t hi; }; /* Set fault processing enable/disable */ static inline void context_set_fpd(struct context_entry *ce, int enable) { ce->lo &= ~CTX_FPD; if (enable) ce->lo |= CTX_FPD; } /* Set context entry present */ static inline void context_set_present(struct context_entry *ce) { ce->lo |= CTX_P; } /* Set Second Level Page Table Entry PA */ static inline void context_set_slpte(struct context_entry *ce, paddr_t slpte) { ce->lo &= VTD_PAGE_MASK; ce->lo |= (slpte & ~VTD_PAGE_MASK); } /* Set translation type */ static inline void context_set_translation_type(struct context_entry *ce, int tt) { ce->lo &= ~(CTX_T_MASK << CTX_T_SHIFT); ce->lo |= ((tt & CTX_T_MASK) << CTX_T_SHIFT); } /* Set Address Width (# of Page Table levels) */ static inline void context_set_address_width(struct context_entry *ce, int lvl) { ce->hi &= ~(CTX_H_AW_MASK << CTX_H_AW_SHIFT); ce->hi |= ((lvl & CTX_H_AW_MASK) << CTX_H_AW_SHIFT); } /* Set domain ID */ static inline void context_set_domain_id(struct context_entry *ce, int did) { ce->hi &= ~(CTX_H_DID_MASK << CTX_H_DID_SHIFT); ce->hi |= ((did & CTX_H_DID_MASK) << CTX_H_DID_SHIFT); } /* Get Second Level Page Table PA */ static inline uint64_t context_pte(struct context_entry *ce) { return (ce->lo & ~VTD_PAGE_MASK); } /* Get translation type */ static inline int context_translation_type(struct context_entry *ce) { return (ce->lo >> CTX_T_SHIFT) & CTX_T_MASK; } /* Get domain ID */ static inline int context_domain_id(struct context_entry *ce) { return (ce->hi >> CTX_H_DID_SHIFT) & CTX_H_DID_MASK; } /* Get Address Width */ static inline int context_address_width(struct context_entry *ce) { return VTD_LEVELTOAW((ce->hi >> CTX_H_AW_SHIFT) & CTX_H_AW_MASK); } /* Check if context entry is valid */ static inline bool context_entry_is_valid(struct context_entry *ce) { return (ce->lo & CTX_P); } /* User-available bits in context entry */ static inline int context_user(struct context_entry *ce) { return (ce->hi >> CTX_H_USER_SHIFT) & CTX_H_USER_MASK; } static inline void context_set_user(struct context_entry *ce, int v) { ce->hi &= ~(CTX_H_USER_MASK << CTX_H_USER_SHIFT); ce->hi |= ((v & CTX_H_USER_MASK) << CTX_H_USER_SHIFT); } /* * Fault entry * 0..HAW-1 = Fault address * HAW:63 = Reserved * 64:71 = Source ID * 96:103 = Fault Reason * 104:123 = PV * 124:125 = Address Translation type * 126 = Type (0 = Read, 1 = Write) * 127 = Fault bit */ struct fault_entry { uint64_t lo; uint64_t hi; }; /* PTE Entry: 512 x 64-bit = 4k */ #define PTE_P (1L << 0) #define PTE_R 0x00 #define PTE_W (1L << 1) #define PTE_US (1L << 2) #define PTE_PWT (1L << 3) #define PTE_PCD (1L << 4) #define PTE_A (1L << 5) #define PTE_D (1L << 6) #define PTE_PAT (1L << 7) #define PTE_G (1L << 8) #define PTE_EA (1L << 10) #define PTE_XD (1LL << 63) /* PDE Level entry */ #define PTE_PS (1L << 7) /* PDPE Level entry */ /* ---------------------------------------------------------------- * 5555555444444444333333333222222222111111111000000000------------ * [PML4 ->] PDPE.1GB * [PML4 ->] PDPE.PDE -> PDE.2MB * [PML4 ->] PDPE.PDE -> PDE -> PTE * GAW0 = (12.20) (PTE) * GAW1 = (21.29) (PDE) * GAW2 = (30.38) (PDPE) * GAW3 = (39.47) (PML4) * GAW4 = (48.57) (n/a) * GAW5 = (58.63) (n/a) */ struct pte_entry { uint64_t val; }; /* * Queued Invalidation entry * 0:3 = 01h * 4:5 = Granularity * 6:15 = Reserved * 16:31 = Domain ID * 32:47 = Source ID * 48:49 = FM */ /* Invalidate Context Entry */ #define QI_CTX_DID_MASK 0xFFFF #define QI_CTX_DID_SHIFT 16 #define QI_CTX_SID_MASK 0xFFFF #define QI_CTX_SID_SHIFT 32 #define QI_CTX_FM_MASK 0x3 #define QI_CTX_FM_SHIFT 48 #define QI_CTX_IG_MASK 0x3 #define QI_CTX_IG_SHIFT 4 #define QI_CTX_DID(x) (((uint64_t)(x) << QI_CTX_DID_SHIFT)) #define QI_CTX_SID(x) (((uint64_t)(x) << QI_CTX_SID_SHIFT)) #define QI_CTX_FM(x) (((uint64_t)(x) << QI_CTX_FM_SHIFT)) #define QI_CTX_IG_GLOBAL (CTX_GLOBAL << QI_CTX_IG_SHIFT) #define QI_CTX_IG_DOMAIN (CTX_DOMAIN << QI_CTX_IG_SHIFT) #define QI_CTX_IG_DEVICE (CTX_DEVICE << QI_CTX_IG_SHIFT) /* Invalidate IOTLB Entry */ #define QI_IOTLB_DID_MASK 0xFFFF #define QI_IOTLB_DID_SHIFT 16 #define QI_IOTLB_IG_MASK 0x3 #define QI_IOTLB_IG_SHIFT 4 #define QI_IOTLB_DR (1LL << 6) #define QI_IOTLB_DW (1LL << 5) #define QI_IOTLB_DID(x) (((uint64_t)(x) << QI_IOTLB_DID_SHIFT)) #define QI_IOTLB_IG_GLOBAL (1 << QI_IOTLB_IG_SHIFT) #define QI_IOTLB_IG_DOMAIN (2 << QI_IOTLB_IG_SHIFT) #define QI_IOTLB_IG_PAGE (3 << QI_IOTLB_IG_SHIFT) /* QI Commands */ #define QI_CTX 0x1 #define QI_IOTLB 0x2 #define QI_DEVTLB 0x3 #define QI_INTR 0x4 #define QI_WAIT 0x5 #define QI_EXTTLB 0x6 #define QI_PAS 0x7 #define QI_EXTDEV 0x8 struct qi_entry { uint64_t lo; uint64_t hi; }; enum { CTX_GLOBAL = 1, CTX_DOMAIN, CTX_DEVICE, IOTLB_GLOBAL = 1, IOTLB_DOMAIN, IOTLB_PAGE, }; enum { VTD_FAULT_ROOT_P = 0x1, /* P field in root entry is 0 */ VTD_FAULT_CTX_P = 0x2, /* P field in context entry is 0 */ VTD_FAULT_CTX_INVAL = 0x3, /* context AW/TT/SLPPTR invalid */ VTD_FAULT_LIMIT = 0x4, /* Address is outside of MGAW */ VTD_FAULT_WRITE = 0x5, /* Address-translation fault, non-writable */ VTD_FAULT_READ = 0x6, /* Address-translation fault, non-readable */ VTD_FAULT_PTE_INVAL = 0x7, /* page table hw access error */ VTD_FAULT_ROOT_INVAL = 0x8, /* root table hw access error */ VTD_FAULT_CTX_TBL_INVAL = 0x9, /* context entry hw access error */ VTD_FAULT_ROOT_RESERVED = 0xa, /* non-zero reserved field in root entry */ VTD_FAULT_CTX_RESERVED = 0xb, /* non-zero reserved field in context entry */ VTD_FAULT_PTE_RESERVED = 0xc, /* non-zero reserved field in paging entry */ VTD_FAULT_CTX_TT = 0xd, /* invalid translation type */ }; #endif void acpidmar_pci_hook(pci_chipset_tag_t, struct pci_attach_args *); void dmar_ptmap(bus_dma_tag_t, bus_addr_t); #define __EXTRACT(v,m) (((v) >> m##_SHIFT) & m##_MASK)