| 1 | /* |
| 2 | * ARM SMMU Support |
| 3 | * |
| 4 | * Copyright (C) 2015-2016 Broadcom Corporation |
| 5 | * Copyright (c) 2017 Red Hat, Inc. |
| 6 | * Written by Prem Mallappa, Eric Auger |
| 7 | * |
| 8 | * This program is free software; you can redistribute it and/or modify |
| 9 | * it under the terms of the GNU General Public License version 2 as |
| 10 | * published by the Free Software Foundation. |
| 11 | * |
| 12 | * This program is distributed in the hope that it will be useful, |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | * GNU General Public License for more details. |
| 16 | * |
| 17 | */ |
| 18 | |
| 19 | #ifndef HW_ARM_SMMU_COMMON_H |
| 20 | #define HW_ARM_SMMU_COMMON_H |
| 21 | |
| 22 | #include "hw/core/sysbus.h" |
| 23 | #include "hw/pci/pci.h" |
| 24 | #include "qom/object.h" |
| 25 | |
| 26 | #define SMMU_PCI_BUS_MAX 256 |
| 27 | #define SMMU_PCI_DEVFN_MAX 256 |
| 28 | #define SMMU_PCI_DEVFN(sid) (sid & 0xFF) |
| 29 | |
| 30 | /* VMSAv8-64 Translation constants and functions */ |
| 31 | #define VMSA_LEVELS 4 |
| 32 | #define VMSA_MAX_S2_CONCAT 16 |
| 33 | |
| 34 | #define VMSA_STRIDE(gran) ((gran) - VMSA_LEVELS + 1) |
| 35 | #define VMSA_BIT_LVL(isz, strd, lvl) ((isz) - (strd) * \ |
| 36 | (VMSA_LEVELS - (lvl))) |
| 37 | #define VMSA_IDXMSK(isz, strd, lvl) ((1ULL << \ |
| 38 | VMSA_BIT_LVL(isz, strd, lvl)) - 1) |
| 39 | |
| 40 | #define CACHED_ENTRY_TO_ADDR(ent, addr) ((ent)->entry.translated_addr + \ |
| 41 | ((addr) & (ent)->entry.addr_mask)) |
| 42 | |
| 43 | /* |
| 44 | * Page table walk error types |
| 45 | */ |
| 46 | typedef enum { |
| 47 | SMMU_PTW_ERR_NONE, |
| 48 | SMMU_PTW_ERR_WALK_EABT, /* Translation walk external abort */ |
| 49 | SMMU_PTW_ERR_TRANSLATION, /* Translation fault */ |
| 50 | SMMU_PTW_ERR_ADDR_SIZE, /* Address Size fault */ |
| 51 | SMMU_PTW_ERR_ACCESS, /* Access fault */ |
| 52 | SMMU_PTW_ERR_PERMISSION, /* Permission fault */ |
| 53 | } SMMUPTWEventType; |
| 54 | |
| 55 | /* SMMU Stage */ |
| 56 | typedef enum { |
| 57 | SMMU_STAGE_1 = 1, |
| 58 | SMMU_STAGE_2, |
| 59 | SMMU_NESTED, |
| 60 | } SMMUStage; |
| 61 | |
| 62 | typedef struct SMMUPTWEventInfo { |
| 63 | SMMUStage stage; |
| 64 | SMMUPTWEventType type; |
| 65 | dma_addr_t addr; /* fetched address that induced an abort, if any */ |
| 66 | bool is_ipa_descriptor; /* src for fault in nested translation. */ |
| 67 | } SMMUPTWEventInfo; |
| 68 | |
| 69 | typedef struct SMMUTransTableInfo { |
| 70 | bool disabled; /* is the translation table disabled? */ |
| 71 | uint64_t ttb; /* TT base address */ |
| 72 | uint8_t tsz; /* input range, ie. 2^(64 -tsz)*/ |
| 73 | uint8_t granule_sz; /* granule page shift */ |
| 74 | bool had; /* hierarchical attribute disable */ |
| 75 | } SMMUTransTableInfo; |
| 76 | |
| 77 | typedef struct SMMUTLBEntry { |
| 78 | IOMMUTLBEntry entry; |
| 79 | uint8_t level; |
| 80 | uint8_t granule; |
| 81 | IOMMUAccessFlags parent_perm; |
| 82 | } SMMUTLBEntry; |
| 83 | |
| 84 | /* Stage-2 configuration. */ |
| 85 | typedef struct SMMUS2Cfg { |
| 86 | uint8_t tsz; /* Size of IPA input region (S2T0SZ) */ |
| 87 | uint8_t sl0; /* Start level of translation (S2SL0) */ |
| 88 | bool affd; /* AF Fault Disable (S2AFFD) */ |
| 89 | bool record_faults; /* Record fault events (S2R) */ |
| 90 | uint8_t granule_sz; /* Granule page shift (based on S2TG) */ |
| 91 | uint8_t eff_ps; /* Effective PA output range (based on S2PS) */ |
| 92 | int vmid; /* Virtual Machine ID (S2VMID) */ |
| 93 | uint64_t vttb; /* Address of translation table base (S2TTB) */ |
| 94 | } SMMUS2Cfg; |
| 95 | |
| 96 | /* |
| 97 | * Generic structure populated by derived SMMU devices |
| 98 | * after decoding the configuration information and used as |
| 99 | * input to the page table walk |
| 100 | */ |
| 101 | typedef struct SMMUTransCfg { |
| 102 | /* Shared fields between stage-1 and stage-2. */ |
| 103 | SMMUStage stage; /* translation stage */ |
| 104 | bool disabled; /* smmu is disabled */ |
| 105 | bool bypassed; /* translation is bypassed */ |
| 106 | bool aborted; /* translation is aborted */ |
| 107 | bool affd; /* AF fault disable */ |
| 108 | uint32_t iotlb_hits; /* counts IOTLB hits */ |
| 109 | uint32_t iotlb_misses; /* counts IOTLB misses*/ |
| 110 | /* Used by stage-1 only. */ |
| 111 | bool aa64; /* arch64 or aarch32 translation table */ |
| 112 | bool record_faults; /* record fault events */ |
| 113 | uint8_t oas; /* output address width */ |
| 114 | uint8_t tbi; /* Top Byte Ignore */ |
| 115 | int asid; |
| 116 | SMMUTransTableInfo tt[2]; |
| 117 | /* Used by stage-2 only. */ |
| 118 | struct SMMUS2Cfg s2cfg; |
| 119 | } SMMUTransCfg; |
| 120 | |
| 121 | typedef struct SMMUDevice { |
| 122 | void *smmu; |
| 123 | PCIBus *bus; |
| 124 | int devfn; |
| 125 | IOMMUMemoryRegion iommu; |
| 126 | AddressSpace as; |
| 127 | uint32_t cfg_cache_hits; |
| 128 | uint32_t cfg_cache_misses; |
| 129 | QLIST_ENTRY(SMMUDevice) next; |
| 130 | } SMMUDevice; |
| 131 | |
| 132 | typedef struct SMMUPciBus { |
| 133 | PCIBus *bus; |
| 134 | SMMUDevice *pbdev[]; /* Parent array is sparse, so dynamically alloc */ |
| 135 | } SMMUPciBus; |
| 136 | |
| 137 | typedef struct SMMUIOTLBKey { |
| 138 | uint64_t iova; |
| 139 | int asid; |
| 140 | int vmid; |
| 141 | uint8_t tg; |
| 142 | uint8_t level; |
| 143 | } SMMUIOTLBKey; |
| 144 | |
| 145 | typedef struct SMMUSIDRange { |
| 146 | uint32_t start; |
| 147 | uint32_t end; |
| 148 | } SMMUSIDRange; |
| 149 | |
| 150 | struct SMMUState { |
| 151 | /* <private> */ |
| 152 | SysBusDevice dev; |
| 153 | const char *mrtypename; |
| 154 | MemoryRegion iomem; |
| 155 | |
| 156 | GHashTable *smmu_pcibus_by_busptr; |
| 157 | GHashTable *configs; /* cache for configuration data */ |
| 158 | GHashTable *iotlb; |
| 159 | SMMUPciBus *smmu_pcibus_by_bus_num[SMMU_PCI_BUS_MAX]; |
| 160 | PCIBus *pci_bus; |
| 161 | QLIST_HEAD(, SMMUDevice) devices_with_notifiers; |
| 162 | uint8_t bus_num; |
| 163 | PCIBus *primary_bus; |
| 164 | bool smmu_per_bus; /* SMMU is specific to the primary_bus */ |
| 165 | MemoryRegion *memory; |
| 166 | AddressSpace memory_as; |
| 167 | MemoryRegion *secure_memory; |
| 168 | AddressSpace secure_memory_as; |
| 169 | const PCIIOMMUOps *iommu_ops; |
| 170 | }; |
| 171 | |
| 172 | struct SMMUBaseClass { |
| 173 | /* <private> */ |
| 174 | SysBusDeviceClass parent_class; |
| 175 | |
| 176 | /*< public >*/ |
| 177 | |
| 178 | DeviceRealize parent_realize; |
| 179 | |
| 180 | }; |
| 181 | |
| 182 | #define TYPE_ARM_SMMU "arm-smmu" |
| 183 | OBJECT_DECLARE_TYPE(SMMUState, SMMUBaseClass, ARM_SMMU) |
| 184 | |
| 185 | /* Return the SMMUPciBus handle associated to a PCI bus number */ |
| 186 | SMMUPciBus *smmu_find_smmu_pcibus(SMMUState *s, uint8_t bus_num); |
| 187 | |
| 188 | /* Return the SMMUPciBus handle associated to a PCI bus */ |
| 189 | SMMUPciBus *smmu_get_sbus(SMMUState *s, PCIBus *bus); |
| 190 | |
| 191 | /* Initialize SMMUDevice handle associated to a SMMUPciBus */ |
| 192 | void smmu_init_sdev(SMMUState *s, SMMUDevice *sdev, PCIBus *bus, int devfn); |
| 193 | |
| 194 | /* Return the stream ID of an SMMU device */ |
| 195 | static inline uint16_t smmu_get_sid(SMMUDevice *sdev) |
| 196 | { |
| 197 | return PCI_BUILD_BDF(pci_bus_num(sdev->bus), sdev->devfn); |
| 198 | } |
| 199 | |
| 200 | /** |
| 201 | * smmu_ptw - Perform the page table walk for a given iova / access flags |
| 202 | * pair, according to @cfg translation config |
| 203 | */ |
| 204 | int smmu_ptw(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t iova, |
| 205 | IOMMUAccessFlags perm, SMMUTLBEntry *tlbe, |
| 206 | SMMUPTWEventInfo *info); |
| 207 | |
| 208 | /* |
| 209 | * smmu_translate - Look for a translation in TLB, if not, do a PTW. |
| 210 | * Returns NULL on PTW error or incase of TLB permission errors. |
| 211 | */ |
| 212 | SMMUTLBEntry *smmu_translate(SMMUState *bs, SMMUTransCfg *cfg, dma_addr_t addr, |
| 213 | IOMMUAccessFlags flag, SMMUPTWEventInfo *info); |
| 214 | |
| 215 | /** |
| 216 | * select_tt - compute which translation table shall be used according to |
| 217 | * the input iova and translation config and return the TT specific info |
| 218 | */ |
| 219 | SMMUTransTableInfo *select_tt(SMMUTransCfg *cfg, dma_addr_t iova); |
| 220 | |
| 221 | /* Return the SMMUDevice associated to @sid, or NULL if none */ |
| 222 | SMMUDevice *smmu_find_sdev(SMMUState *s, uint32_t sid); |
| 223 | |
| 224 | #define SMMU_IOTLB_MAX_SIZE 256 |
| 225 | |
| 226 | SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg, |
| 227 | SMMUTransTableInfo *tt, hwaddr iova); |
| 228 | void smmu_iotlb_insert(SMMUState *bs, SMMUTransCfg *cfg, SMMUTLBEntry *entry); |
| 229 | SMMUIOTLBKey smmu_get_iotlb_key(int asid, int vmid, uint64_t iova, |
| 230 | uint8_t tg, uint8_t level); |
| 231 | void smmu_iotlb_inv_all(SMMUState *s); |
| 232 | void smmu_iotlb_inv_asid_vmid(SMMUState *s, int asid, int vmid); |
| 233 | void smmu_iotlb_inv_vmid(SMMUState *s, int vmid); |
| 234 | void smmu_iotlb_inv_vmid_s1(SMMUState *s, int vmid); |
| 235 | void smmu_iotlb_inv_iova(SMMUState *s, int asid, int vmid, dma_addr_t iova, |
| 236 | uint8_t tg, uint64_t num_pages, uint8_t ttl); |
| 237 | void smmu_iotlb_inv_ipa(SMMUState *s, int vmid, dma_addr_t ipa, uint8_t tg, |
| 238 | uint64_t num_pages, uint8_t ttl); |
| 239 | void smmu_configs_inv_sid_range(SMMUState *s, SMMUSIDRange sid_range); |
| 240 | /* Unmap the range of all the notifiers registered to any IOMMU mr */ |
| 241 | void smmu_inv_notifiers_all(SMMUState *s); |
| 242 | |
| 243 | #endif /* HW_ARM_SMMU_COMMON_H */ |