diff options
| -rw-r--r-- | arch/x86/include/asm/amd_iommu.h | 1 | ||||
| -rw-r--r-- | arch/x86/include/asm/amd_iommu_types.h | 4 | ||||
| -rw-r--r-- | arch/x86/kernel/amd_iommu.c | 141 | ||||
| -rw-r--r-- | arch/x86/kernel/amd_iommu_init.c | 8 | ||||
| -rw-r--r-- | arch/x86/kernel/pci-dma.c | 9 |
5 files changed, 143 insertions, 20 deletions
diff --git a/arch/x86/include/asm/amd_iommu.h b/arch/x86/include/asm/amd_iommu.h index bdf96f119f06..ac95995b7bad 100644 --- a/arch/x86/include/asm/amd_iommu.h +++ b/arch/x86/include/asm/amd_iommu.h | |||
| @@ -25,6 +25,7 @@ | |||
| 25 | #ifdef CONFIG_AMD_IOMMU | 25 | #ifdef CONFIG_AMD_IOMMU |
| 26 | extern int amd_iommu_init(void); | 26 | extern int amd_iommu_init(void); |
| 27 | extern int amd_iommu_init_dma_ops(void); | 27 | extern int amd_iommu_init_dma_ops(void); |
| 28 | extern int amd_iommu_init_passthrough(void); | ||
| 28 | extern void amd_iommu_detect(void); | 29 | extern void amd_iommu_detect(void); |
| 29 | extern irqreturn_t amd_iommu_int_handler(int irq, void *data); | 30 | extern irqreturn_t amd_iommu_int_handler(int irq, void *data); |
| 30 | extern void amd_iommu_flush_all_domains(void); | 31 | extern void amd_iommu_flush_all_domains(void); |
diff --git a/arch/x86/include/asm/amd_iommu_types.h b/arch/x86/include/asm/amd_iommu_types.h index 3a6a3259e1eb..86a56b49f2c6 100644 --- a/arch/x86/include/asm/amd_iommu_types.h +++ b/arch/x86/include/asm/amd_iommu_types.h | |||
| @@ -143,6 +143,7 @@ | |||
| 143 | #define EVT_BUFFER_SIZE 8192 /* 512 entries */ | 143 | #define EVT_BUFFER_SIZE 8192 /* 512 entries */ |
| 144 | #define EVT_LEN_MASK (0x9ULL << 56) | 144 | #define EVT_LEN_MASK (0x9ULL << 56) |
| 145 | 145 | ||
| 146 | #define PAGE_MODE_NONE 0x00 | ||
| 146 | #define PAGE_MODE_1_LEVEL 0x01 | 147 | #define PAGE_MODE_1_LEVEL 0x01 |
| 147 | #define PAGE_MODE_2_LEVEL 0x02 | 148 | #define PAGE_MODE_2_LEVEL 0x02 |
| 148 | #define PAGE_MODE_3_LEVEL 0x03 | 149 | #define PAGE_MODE_3_LEVEL 0x03 |
| @@ -194,6 +195,9 @@ | |||
| 194 | #define PD_DMA_OPS_MASK (1UL << 0) /* domain used for dma_ops */ | 195 | #define PD_DMA_OPS_MASK (1UL << 0) /* domain used for dma_ops */ |
| 195 | #define PD_DEFAULT_MASK (1UL << 1) /* domain is a default dma_ops | 196 | #define PD_DEFAULT_MASK (1UL << 1) /* domain is a default dma_ops |
| 196 | domain for an IOMMU */ | 197 | domain for an IOMMU */ |
| 198 | #define PD_PASSTHROUGH_MASK (1UL << 2) /* domain has no page | ||
| 199 | translation */ | ||
| 200 | |||
| 197 | extern bool amd_iommu_dump; | 201 | extern bool amd_iommu_dump; |
| 198 | #define DUMP_printk(format, arg...) \ | 202 | #define DUMP_printk(format, arg...) \ |
| 199 | do { \ | 203 | do { \ |
diff --git a/arch/x86/kernel/amd_iommu.c b/arch/x86/kernel/amd_iommu.c index 8c93b7c7735e..dc19ed43b54e 100644 --- a/arch/x86/kernel/amd_iommu.c +++ b/arch/x86/kernel/amd_iommu.c | |||
| @@ -41,6 +41,13 @@ static DEFINE_RWLOCK(amd_iommu_devtable_lock); | |||
| 41 | static LIST_HEAD(iommu_pd_list); | 41 | static LIST_HEAD(iommu_pd_list); |
| 42 | static DEFINE_SPINLOCK(iommu_pd_list_lock); | 42 | static DEFINE_SPINLOCK(iommu_pd_list_lock); |
| 43 | 43 | ||
| 44 | /* | ||
| 45 | * Domain for untranslated devices - only allocated | ||
| 46 | * if iommu=pt passed on kernel cmd line. | ||
| 47 | */ | ||
| 48 | static struct protection_domain *pt_domain; | ||
| 49 | |||
| 50 | #ifdef CONFIG_IOMMU_API | ||
| 44 | static struct iommu_ops amd_iommu_ops; | 51 | static struct iommu_ops amd_iommu_ops; |
| 45 | 52 | ||
| 46 | /* | 53 | /* |
| @@ -1130,32 +1137,48 @@ static struct protection_domain *domain_for_device(u16 devid) | |||
| 1130 | * If a device is not yet associated with a domain, this function does | 1137 | * If a device is not yet associated with a domain, this function does |
| 1131 | * assigns it visible for the hardware | 1138 | * assigns it visible for the hardware |
| 1132 | */ | 1139 | */ |
| 1133 | static void attach_device(struct amd_iommu *iommu, | 1140 | static void __attach_device(struct amd_iommu *iommu, |
| 1134 | struct protection_domain *domain, | 1141 | struct protection_domain *domain, |
| 1135 | u16 devid) | 1142 | u16 devid) |
| 1136 | { | 1143 | { |
| 1137 | unsigned long flags; | 1144 | u64 pte_root; |
| 1138 | u64 pte_root = virt_to_phys(domain->pt_root); | ||
| 1139 | 1145 | ||
| 1140 | domain->dev_cnt += 1; | 1146 | /* lock domain */ |
| 1147 | spin_lock(&domain->lock); | ||
| 1148 | |||
| 1149 | pte_root = virt_to_phys(domain->pt_root); | ||
| 1141 | 1150 | ||
| 1142 | pte_root |= (domain->mode & DEV_ENTRY_MODE_MASK) | 1151 | pte_root |= (domain->mode & DEV_ENTRY_MODE_MASK) |
| 1143 | << DEV_ENTRY_MODE_SHIFT; | 1152 | << DEV_ENTRY_MODE_SHIFT; |
| 1144 | pte_root |= IOMMU_PTE_IR | IOMMU_PTE_IW | IOMMU_PTE_P | IOMMU_PTE_TV; | 1153 | pte_root |= IOMMU_PTE_IR | IOMMU_PTE_IW | IOMMU_PTE_P | IOMMU_PTE_TV; |
| 1145 | 1154 | ||
| 1146 | write_lock_irqsave(&amd_iommu_devtable_lock, flags); | ||
| 1147 | amd_iommu_dev_table[devid].data[0] = lower_32_bits(pte_root); | ||
| 1148 | amd_iommu_dev_table[devid].data[1] = upper_32_bits(pte_root); | ||
| 1149 | amd_iommu_dev_table[devid].data[2] = domain->id; | 1155 | amd_iommu_dev_table[devid].data[2] = domain->id; |
| 1156 | amd_iommu_dev_table[devid].data[1] = upper_32_bits(pte_root); | ||
| 1157 | amd_iommu_dev_table[devid].data[0] = lower_32_bits(pte_root); | ||
| 1150 | 1158 | ||
| 1151 | amd_iommu_pd_table[devid] = domain; | 1159 | amd_iommu_pd_table[devid] = domain; |
| 1160 | |||
| 1161 | domain->dev_cnt += 1; | ||
| 1162 | |||
| 1163 | /* ready */ | ||
| 1164 | spin_unlock(&domain->lock); | ||
| 1165 | } | ||
| 1166 | |||
| 1167 | static void attach_device(struct amd_iommu *iommu, | ||
| 1168 | struct protection_domain *domain, | ||
| 1169 | u16 devid) | ||
| 1170 | { | ||
| 1171 | unsigned long flags; | ||
| 1172 | |||
| 1173 | write_lock_irqsave(&amd_iommu_devtable_lock, flags); | ||
| 1174 | __attach_device(iommu, domain, devid); | ||
| 1152 | write_unlock_irqrestore(&amd_iommu_devtable_lock, flags); | 1175 | write_unlock_irqrestore(&amd_iommu_devtable_lock, flags); |
| 1153 | 1176 | ||
| 1154 | /* | 1177 | /* |
| 1155 | * We might boot into a crash-kernel here. The crashed kernel | 1178 | * We might boot into a crash-kernel here. The crashed kernel |
| 1156 | * left the caches in the IOMMU dirty. So we have to flush | 1179 | * left the caches in the IOMMU dirty. So we have to flush |
| 1157 | * here to evict all dirty stuff. | 1180 | * here to evict all dirty stuff. |
| 1158 | */ | 1181 | */ |
| 1159 | iommu_queue_inv_dev_entry(iommu, devid); | 1182 | iommu_queue_inv_dev_entry(iommu, devid); |
| 1160 | iommu_flush_tlb_pde(iommu, domain->id); | 1183 | iommu_flush_tlb_pde(iommu, domain->id); |
| 1161 | } | 1184 | } |
| @@ -1182,6 +1205,15 @@ static void __detach_device(struct protection_domain *domain, u16 devid) | |||
| 1182 | 1205 | ||
| 1183 | /* ready */ | 1206 | /* ready */ |
| 1184 | spin_unlock(&domain->lock); | 1207 | spin_unlock(&domain->lock); |
| 1208 | |||
| 1209 | /* | ||
| 1210 | * If we run in passthrough mode the device must be assigned to the | ||
| 1211 | * passthrough domain if it is detached from any other domain | ||
| 1212 | */ | ||
| 1213 | if (iommu_pass_through) { | ||
| 1214 | struct amd_iommu *iommu = amd_iommu_rlookup_table[devid]; | ||
| 1215 | __attach_device(iommu, pt_domain, devid); | ||
| 1216 | } | ||
| 1185 | } | 1217 | } |
| 1186 | 1218 | ||
| 1187 | /* | 1219 | /* |
| @@ -1227,6 +1259,8 @@ static int device_change_notifier(struct notifier_block *nb, | |||
| 1227 | case BUS_NOTIFY_UNBOUND_DRIVER: | 1259 | case BUS_NOTIFY_UNBOUND_DRIVER: |
| 1228 | if (!domain) | 1260 | if (!domain) |
| 1229 | goto out; | 1261 | goto out; |
| 1262 | if (iommu_pass_through) | ||
| 1263 | break; | ||
| 1230 | detach_device(domain, devid); | 1264 | detach_device(domain, devid); |
| 1231 | break; | 1265 | break; |
| 1232 | case BUS_NOTIFY_ADD_DEVICE: | 1266 | case BUS_NOTIFY_ADD_DEVICE: |
| @@ -2051,19 +2085,47 @@ static void cleanup_domain(struct protection_domain *domain) | |||
| 2051 | write_unlock_irqrestore(&amd_iommu_devtable_lock, flags); | 2085 | write_unlock_irqrestore(&amd_iommu_devtable_lock, flags); |
| 2052 | } | 2086 | } |
| 2053 | 2087 | ||
| 2054 | static int amd_iommu_domain_init(struct iommu_domain *dom) | 2088 | static void protection_domain_free(struct protection_domain *domain) |
| 2089 | { | ||
| 2090 | if (!domain) | ||
| 2091 | return; | ||
| 2092 | |||
| 2093 | if (domain->id) | ||
| 2094 | domain_id_free(domain->id); | ||
| 2095 | |||
| 2096 | kfree(domain); | ||
| 2097 | } | ||
| 2098 | |||
| 2099 | static struct protection_domain *protection_domain_alloc(void) | ||
| 2055 | { | 2100 | { |
| 2056 | struct protection_domain *domain; | 2101 | struct protection_domain *domain; |
| 2057 | 2102 | ||
| 2058 | domain = kzalloc(sizeof(*domain), GFP_KERNEL); | 2103 | domain = kzalloc(sizeof(*domain), GFP_KERNEL); |
| 2059 | if (!domain) | 2104 | if (!domain) |
| 2060 | return -ENOMEM; | 2105 | return NULL; |
| 2061 | 2106 | ||
| 2062 | spin_lock_init(&domain->lock); | 2107 | spin_lock_init(&domain->lock); |
| 2063 | domain->mode = PAGE_MODE_3_LEVEL; | ||
| 2064 | domain->id = domain_id_alloc(); | 2108 | domain->id = domain_id_alloc(); |
| 2065 | if (!domain->id) | 2109 | if (!domain->id) |
| 2110 | goto out_err; | ||
| 2111 | |||
| 2112 | return domain; | ||
| 2113 | |||
| 2114 | out_err: | ||
| 2115 | kfree(domain); | ||
| 2116 | |||
| 2117 | return NULL; | ||
| 2118 | } | ||
| 2119 | |||
| 2120 | static int amd_iommu_domain_init(struct iommu_domain *dom) | ||
| 2121 | { | ||
| 2122 | struct protection_domain *domain; | ||
| 2123 | |||
| 2124 | domain = protection_domain_alloc(); | ||
| 2125 | if (!domain) | ||
| 2066 | goto out_free; | 2126 | goto out_free; |
| 2127 | |||
| 2128 | domain->mode = PAGE_MODE_3_LEVEL; | ||
| 2067 | domain->pt_root = (void *)get_zeroed_page(GFP_KERNEL); | 2129 | domain->pt_root = (void *)get_zeroed_page(GFP_KERNEL); |
| 2068 | if (!domain->pt_root) | 2130 | if (!domain->pt_root) |
| 2069 | goto out_free; | 2131 | goto out_free; |
| @@ -2073,7 +2135,7 @@ static int amd_iommu_domain_init(struct iommu_domain *dom) | |||
| 2073 | return 0; | 2135 | return 0; |
| 2074 | 2136 | ||
| 2075 | out_free: | 2137 | out_free: |
| 2076 | kfree(domain); | 2138 | protection_domain_free(domain); |
| 2077 | 2139 | ||
| 2078 | return -ENOMEM; | 2140 | return -ENOMEM; |
| 2079 | } | 2141 | } |
| @@ -2254,3 +2316,46 @@ static struct iommu_ops amd_iommu_ops = { | |||
| 2254 | .domain_has_cap = amd_iommu_domain_has_cap, | 2316 | .domain_has_cap = amd_iommu_domain_has_cap, |
| 2255 | }; | 2317 | }; |
| 2256 | 2318 | ||
| 2319 | /***************************************************************************** | ||
| 2320 | * | ||
| 2321 | * The next functions do a basic initialization of IOMMU for pass through | ||
| 2322 | * mode | ||
| 2323 | * | ||
| 2324 | * In passthrough mode the IOMMU is initialized and enabled but not used for | ||
| 2325 | * DMA-API translation. | ||
| 2326 | * | ||
| 2327 | *****************************************************************************/ | ||
| 2328 | |||
| 2329 | int __init amd_iommu_init_passthrough(void) | ||
| 2330 | { | ||
| 2331 | struct pci_dev *dev = NULL; | ||
| 2332 | u16 devid, devid2; | ||
| 2333 | |||
| 2334 | /* allocate passthroug domain */ | ||
| 2335 | pt_domain = protection_domain_alloc(); | ||
| 2336 | if (!pt_domain) | ||
| 2337 | return -ENOMEM; | ||
| 2338 | |||
| 2339 | pt_domain->mode |= PAGE_MODE_NONE; | ||
| 2340 | |||
| 2341 | while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { | ||
| 2342 | struct amd_iommu *iommu; | ||
| 2343 | |||
| 2344 | devid = calc_devid(dev->bus->number, dev->devfn); | ||
| 2345 | if (devid > amd_iommu_last_bdf) | ||
| 2346 | continue; | ||
| 2347 | |||
| 2348 | devid2 = amd_iommu_alias_table[devid]; | ||
| 2349 | |||
| 2350 | iommu = amd_iommu_rlookup_table[devid2]; | ||
| 2351 | if (!iommu) | ||
| 2352 | continue; | ||
| 2353 | |||
| 2354 | __attach_device(iommu, pt_domain, devid); | ||
| 2355 | __attach_device(iommu, pt_domain, devid2); | ||
| 2356 | } | ||
| 2357 | |||
| 2358 | pr_info("AMD-Vi: Initialized for Passthrough Mode\n"); | ||
| 2359 | |||
| 2360 | return 0; | ||
| 2361 | } | ||
diff --git a/arch/x86/kernel/amd_iommu_init.c b/arch/x86/kernel/amd_iommu_init.c index 779ace292475..b4b61d462dcc 100644 --- a/arch/x86/kernel/amd_iommu_init.c +++ b/arch/x86/kernel/amd_iommu_init.c | |||
| @@ -1252,12 +1252,18 @@ int __init amd_iommu_init(void) | |||
| 1252 | if (ret) | 1252 | if (ret) |
| 1253 | goto free; | 1253 | goto free; |
| 1254 | 1254 | ||
| 1255 | ret = amd_iommu_init_dma_ops(); | 1255 | if (iommu_pass_through) |
| 1256 | ret = amd_iommu_init_passthrough(); | ||
| 1257 | else | ||
| 1258 | ret = amd_iommu_init_dma_ops(); | ||
| 1256 | if (ret) | 1259 | if (ret) |
| 1257 | goto free; | 1260 | goto free; |
| 1258 | 1261 | ||
| 1259 | enable_iommus(); | 1262 | enable_iommus(); |
| 1260 | 1263 | ||
| 1264 | if (iommu_pass_through) | ||
| 1265 | goto out; | ||
| 1266 | |||
| 1261 | printk(KERN_INFO "AMD-Vi: device isolation "); | 1267 | printk(KERN_INFO "AMD-Vi: device isolation "); |
| 1262 | if (amd_iommu_isolate) | 1268 | if (amd_iommu_isolate) |
| 1263 | printk("enabled\n"); | 1269 | printk("enabled\n"); |
diff --git a/arch/x86/kernel/pci-dma.c b/arch/x86/kernel/pci-dma.c index 1a041bcf506b..873aa079d166 100644 --- a/arch/x86/kernel/pci-dma.c +++ b/arch/x86/kernel/pci-dma.c | |||
| @@ -32,7 +32,14 @@ int no_iommu __read_mostly; | |||
| 32 | /* Set this to 1 if there is a HW IOMMU in the system */ | 32 | /* Set this to 1 if there is a HW IOMMU in the system */ |
| 33 | int iommu_detected __read_mostly = 0; | 33 | int iommu_detected __read_mostly = 0; |
| 34 | 34 | ||
| 35 | int iommu_pass_through; | 35 | /* |
| 36 | * This variable becomes 1 if iommu=pt is passed on the kernel command line. | ||
| 37 | * If this variable is 1, IOMMU implementations do no DMA ranslation for | ||
| 38 | * devices and allow every device to access to whole physical memory. This is | ||
| 39 | * useful if a user want to use an IOMMU only for KVM device assignment to | ||
| 40 | * guests and not for driver dma translation. | ||
| 41 | */ | ||
| 42 | int iommu_pass_through __read_mostly; | ||
| 36 | 43 | ||
| 37 | dma_addr_t bad_dma_address __read_mostly = 0; | 44 | dma_addr_t bad_dma_address __read_mostly = 0; |
| 38 | EXPORT_SYMBOL(bad_dma_address); | 45 | EXPORT_SYMBOL(bad_dma_address); |
