diff options
author | Fenghua Yu <fenghua.yu@intel.com> | 2008-10-17 15:14:13 -0400 |
---|---|---|
committer | Tony Luck <tony.luck@intel.com> | 2008-10-17 15:14:13 -0400 |
commit | 62fdd7678a26efadd6ac5c2869543caff77d2df0 (patch) | |
tree | 0dd67208590c4540ff6a4476579a55bcac0d1fce /arch/ia64/kernel | |
parent | 6bb7a935489dab20802dde6c2cb7d8582f4849bf (diff) |
[IA64] Add Variable Page Size and IA64 Support in Intel IOMMU
The patch contains Intel IOMMU IA64 specific code. It defines new
machvec dig_vtd, hooks for IOMMU, DMAR table detection, cache line flush
function, etc.
For a generic kernel with CONFIG_DMAR=y, if Intel IOMMU is detected,
dig_vtd is used for machinve vector. Otherwise, kernel falls back to
dig machine vector. Kernel parameter "machvec=dig" or "intel_iommu=off"
can be used to force kernel to boot dig machine vector.
Signed-off-by: Fenghua Yu <fenghua.yu@intel.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
Diffstat (limited to 'arch/ia64/kernel')
-rw-r--r-- | arch/ia64/kernel/Makefile | 4 | ||||
-rw-r--r-- | arch/ia64/kernel/acpi.c | 17 | ||||
-rw-r--r-- | arch/ia64/kernel/msi_ia64.c | 80 | ||||
-rw-r--r-- | arch/ia64/kernel/pci-dma.c | 129 | ||||
-rw-r--r-- | arch/ia64/kernel/pci-swiotlb.c | 46 | ||||
-rw-r--r-- | arch/ia64/kernel/setup.c | 42 |
6 files changed, 305 insertions, 13 deletions
diff --git a/arch/ia64/kernel/Makefile b/arch/ia64/kernel/Makefile index 87fea11aecb7..af0e750705e3 100644 --- a/arch/ia64/kernel/Makefile +++ b/arch/ia64/kernel/Makefile | |||
@@ -42,6 +42,10 @@ obj-$(CONFIG_IA64_ESI) += esi.o | |||
42 | ifneq ($(CONFIG_IA64_ESI),) | 42 | ifneq ($(CONFIG_IA64_ESI),) |
43 | obj-y += esi_stub.o # must be in kernel proper | 43 | obj-y += esi_stub.o # must be in kernel proper |
44 | endif | 44 | endif |
45 | obj-$(CONFIG_DMAR) += pci-dma.o | ||
46 | ifeq ($(CONFIG_DMAR), y) | ||
47 | obj-$(CONFIG_SWIOTLB) += pci-swiotlb.o | ||
48 | endif | ||
45 | 49 | ||
46 | # The gate DSO image is built using a special linker script. | 50 | # The gate DSO image is built using a special linker script. |
47 | targets += gate.so gate-syms.o | 51 | targets += gate.so gate-syms.o |
diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c index 5d1eb7ee2bf6..8cc2f8a610c4 100644 --- a/arch/ia64/kernel/acpi.c +++ b/arch/ia64/kernel/acpi.c | |||
@@ -91,6 +91,9 @@ acpi_get_sysname(void) | |||
91 | struct acpi_table_rsdp *rsdp; | 91 | struct acpi_table_rsdp *rsdp; |
92 | struct acpi_table_xsdt *xsdt; | 92 | struct acpi_table_xsdt *xsdt; |
93 | struct acpi_table_header *hdr; | 93 | struct acpi_table_header *hdr; |
94 | #ifdef CONFIG_DMAR | ||
95 | u64 i, nentries; | ||
96 | #endif | ||
94 | 97 | ||
95 | rsdp_phys = acpi_find_rsdp(); | 98 | rsdp_phys = acpi_find_rsdp(); |
96 | if (!rsdp_phys) { | 99 | if (!rsdp_phys) { |
@@ -123,6 +126,18 @@ acpi_get_sysname(void) | |||
123 | return "sn2"; | 126 | return "sn2"; |
124 | } | 127 | } |
125 | 128 | ||
129 | #ifdef CONFIG_DMAR | ||
130 | /* Look for Intel IOMMU */ | ||
131 | nentries = (hdr->length - sizeof(*hdr)) / | ||
132 | sizeof(xsdt->table_offset_entry[0]); | ||
133 | for (i = 0; i < nentries; i++) { | ||
134 | hdr = __va(xsdt->table_offset_entry[i]); | ||
135 | if (strncmp(hdr->signature, ACPI_SIG_DMAR, | ||
136 | sizeof(ACPI_SIG_DMAR) - 1) == 0) | ||
137 | return "dig_vtd"; | ||
138 | } | ||
139 | #endif | ||
140 | |||
126 | return "dig"; | 141 | return "dig"; |
127 | #else | 142 | #else |
128 | # if defined (CONFIG_IA64_HP_SIM) | 143 | # if defined (CONFIG_IA64_HP_SIM) |
@@ -137,6 +152,8 @@ acpi_get_sysname(void) | |||
137 | return "uv"; | 152 | return "uv"; |
138 | # elif defined (CONFIG_IA64_DIG) | 153 | # elif defined (CONFIG_IA64_DIG) |
139 | return "dig"; | 154 | return "dig"; |
155 | # elif defined(CONFIG_IA64_DIG_VTD) | ||
156 | return "dig_vtd"; | ||
140 | # else | 157 | # else |
141 | # error Unknown platform. Fix acpi.c. | 158 | # error Unknown platform. Fix acpi.c. |
142 | # endif | 159 | # endif |
diff --git a/arch/ia64/kernel/msi_ia64.c b/arch/ia64/kernel/msi_ia64.c index 60c6ef67ebb2..702a09c13238 100644 --- a/arch/ia64/kernel/msi_ia64.c +++ b/arch/ia64/kernel/msi_ia64.c | |||
@@ -5,6 +5,7 @@ | |||
5 | #include <linux/pci.h> | 5 | #include <linux/pci.h> |
6 | #include <linux/irq.h> | 6 | #include <linux/irq.h> |
7 | #include <linux/msi.h> | 7 | #include <linux/msi.h> |
8 | #include <linux/dmar.h> | ||
8 | #include <asm/smp.h> | 9 | #include <asm/smp.h> |
9 | 10 | ||
10 | /* | 11 | /* |
@@ -162,3 +163,82 @@ void arch_teardown_msi_irq(unsigned int irq) | |||
162 | 163 | ||
163 | return ia64_teardown_msi_irq(irq); | 164 | return ia64_teardown_msi_irq(irq); |
164 | } | 165 | } |
166 | |||
167 | #ifdef CONFIG_DMAR | ||
168 | #ifdef CONFIG_SMP | ||
169 | static void dmar_msi_set_affinity(unsigned int irq, cpumask_t mask) | ||
170 | { | ||
171 | struct irq_cfg *cfg = irq_cfg + irq; | ||
172 | struct msi_msg msg; | ||
173 | int cpu = first_cpu(mask); | ||
174 | |||
175 | |||
176 | if (!cpu_online(cpu)) | ||
177 | return; | ||
178 | |||
179 | if (irq_prepare_move(irq, cpu)) | ||
180 | return; | ||
181 | |||
182 | dmar_msi_read(irq, &msg); | ||
183 | |||
184 | msg.data &= ~MSI_DATA_VECTOR_MASK; | ||
185 | msg.data |= MSI_DATA_VECTOR(cfg->vector); | ||
186 | msg.address_lo &= ~MSI_ADDR_DESTID_MASK; | ||
187 | msg.address_lo |= MSI_ADDR_DESTID_CPU(cpu_physical_id(cpu)); | ||
188 | |||
189 | dmar_msi_write(irq, &msg); | ||
190 | irq_desc[irq].affinity = mask; | ||
191 | } | ||
192 | #endif /* CONFIG_SMP */ | ||
193 | |||
194 | struct irq_chip dmar_msi_type = { | ||
195 | .name = "DMAR_MSI", | ||
196 | .unmask = dmar_msi_unmask, | ||
197 | .mask = dmar_msi_mask, | ||
198 | .ack = ia64_ack_msi_irq, | ||
199 | #ifdef CONFIG_SMP | ||
200 | .set_affinity = dmar_msi_set_affinity, | ||
201 | #endif | ||
202 | .retrigger = ia64_msi_retrigger_irq, | ||
203 | }; | ||
204 | |||
205 | static int | ||
206 | msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_msg *msg) | ||
207 | { | ||
208 | struct irq_cfg *cfg = irq_cfg + irq; | ||
209 | unsigned dest; | ||
210 | cpumask_t mask; | ||
211 | |||
212 | cpus_and(mask, irq_to_domain(irq), cpu_online_map); | ||
213 | dest = cpu_physical_id(first_cpu(mask)); | ||
214 | |||
215 | msg->address_hi = 0; | ||
216 | msg->address_lo = | ||
217 | MSI_ADDR_HEADER | | ||
218 | MSI_ADDR_DESTMODE_PHYS | | ||
219 | MSI_ADDR_REDIRECTION_CPU | | ||
220 | MSI_ADDR_DESTID_CPU(dest); | ||
221 | |||
222 | msg->data = | ||
223 | MSI_DATA_TRIGGER_EDGE | | ||
224 | MSI_DATA_LEVEL_ASSERT | | ||
225 | MSI_DATA_DELIVERY_FIXED | | ||
226 | MSI_DATA_VECTOR(cfg->vector); | ||
227 | return 0; | ||
228 | } | ||
229 | |||
230 | int arch_setup_dmar_msi(unsigned int irq) | ||
231 | { | ||
232 | int ret; | ||
233 | struct msi_msg msg; | ||
234 | |||
235 | ret = msi_compose_msg(NULL, irq, &msg); | ||
236 | if (ret < 0) | ||
237 | return ret; | ||
238 | dmar_msi_write(irq, &msg); | ||
239 | set_irq_chip_and_handler_name(irq, &dmar_msi_type, handle_edge_irq, | ||
240 | "edge"); | ||
241 | return 0; | ||
242 | } | ||
243 | #endif /* CONFIG_DMAR */ | ||
244 | |||
diff --git a/arch/ia64/kernel/pci-dma.c b/arch/ia64/kernel/pci-dma.c new file mode 100644 index 000000000000..10a75b557650 --- /dev/null +++ b/arch/ia64/kernel/pci-dma.c | |||
@@ -0,0 +1,129 @@ | |||
1 | /* | ||
2 | * Dynamic DMA mapping support. | ||
3 | */ | ||
4 | |||
5 | #include <linux/types.h> | ||
6 | #include <linux/mm.h> | ||
7 | #include <linux/string.h> | ||
8 | #include <linux/pci.h> | ||
9 | #include <linux/module.h> | ||
10 | #include <linux/dmar.h> | ||
11 | #include <asm/iommu.h> | ||
12 | #include <asm/machvec.h> | ||
13 | #include <linux/dma-mapping.h> | ||
14 | |||
15 | #include <asm/machvec.h> | ||
16 | #include <asm/system.h> | ||
17 | |||
18 | #ifdef CONFIG_DMAR | ||
19 | |||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/string.h> | ||
22 | |||
23 | #include <asm/page.h> | ||
24 | #include <asm/iommu.h> | ||
25 | |||
26 | dma_addr_t bad_dma_address __read_mostly; | ||
27 | EXPORT_SYMBOL(bad_dma_address); | ||
28 | |||
29 | static int iommu_sac_force __read_mostly; | ||
30 | |||
31 | int no_iommu __read_mostly; | ||
32 | #ifdef CONFIG_IOMMU_DEBUG | ||
33 | int force_iommu __read_mostly = 1; | ||
34 | #else | ||
35 | int force_iommu __read_mostly; | ||
36 | #endif | ||
37 | |||
38 | /* Set this to 1 if there is a HW IOMMU in the system */ | ||
39 | int iommu_detected __read_mostly; | ||
40 | |||
41 | /* Dummy device used for NULL arguments (normally ISA). Better would | ||
42 | be probably a smaller DMA mask, but this is bug-to-bug compatible | ||
43 | to i386. */ | ||
44 | struct device fallback_dev = { | ||
45 | .bus_id = "fallback device", | ||
46 | .coherent_dma_mask = DMA_32BIT_MASK, | ||
47 | .dma_mask = &fallback_dev.coherent_dma_mask, | ||
48 | }; | ||
49 | |||
50 | void __init pci_iommu_alloc(void) | ||
51 | { | ||
52 | /* | ||
53 | * The order of these functions is important for | ||
54 | * fall-back/fail-over reasons | ||
55 | */ | ||
56 | detect_intel_iommu(); | ||
57 | |||
58 | #ifdef CONFIG_SWIOTLB | ||
59 | pci_swiotlb_init(); | ||
60 | #endif | ||
61 | } | ||
62 | |||
63 | static int __init pci_iommu_init(void) | ||
64 | { | ||
65 | if (iommu_detected) | ||
66 | intel_iommu_init(); | ||
67 | |||
68 | return 0; | ||
69 | } | ||
70 | |||
71 | /* Must execute after PCI subsystem */ | ||
72 | fs_initcall(pci_iommu_init); | ||
73 | |||
74 | void pci_iommu_shutdown(void) | ||
75 | { | ||
76 | return; | ||
77 | } | ||
78 | |||
79 | void __init | ||
80 | iommu_dma_init(void) | ||
81 | { | ||
82 | return; | ||
83 | } | ||
84 | |||
85 | struct dma_mapping_ops *dma_ops; | ||
86 | EXPORT_SYMBOL(dma_ops); | ||
87 | |||
88 | int iommu_dma_supported(struct device *dev, u64 mask) | ||
89 | { | ||
90 | struct dma_mapping_ops *ops = get_dma_ops(dev); | ||
91 | |||
92 | #ifdef CONFIG_PCI | ||
93 | if (mask > 0xffffffff && forbid_dac > 0) { | ||
94 | dev_info(dev, "Disallowing DAC for device\n"); | ||
95 | return 0; | ||
96 | } | ||
97 | #endif | ||
98 | |||
99 | if (ops->dma_supported_op) | ||
100 | return ops->dma_supported_op(dev, mask); | ||
101 | |||
102 | /* Copied from i386. Doesn't make much sense, because it will | ||
103 | only work for pci_alloc_coherent. | ||
104 | The caller just has to use GFP_DMA in this case. */ | ||
105 | if (mask < DMA_24BIT_MASK) | ||
106 | return 0; | ||
107 | |||
108 | /* Tell the device to use SAC when IOMMU force is on. This | ||
109 | allows the driver to use cheaper accesses in some cases. | ||
110 | |||
111 | Problem with this is that if we overflow the IOMMU area and | ||
112 | return DAC as fallback address the device may not handle it | ||
113 | correctly. | ||
114 | |||
115 | As a special case some controllers have a 39bit address | ||
116 | mode that is as efficient as 32bit (aic79xx). Don't force | ||
117 | SAC for these. Assume all masks <= 40 bits are of this | ||
118 | type. Normally this doesn't make any difference, but gives | ||
119 | more gentle handling of IOMMU overflow. */ | ||
120 | if (iommu_sac_force && (mask >= DMA_40BIT_MASK)) { | ||
121 | dev_info(dev, "Force SAC with mask %lx\n", mask); | ||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | return 1; | ||
126 | } | ||
127 | EXPORT_SYMBOL(iommu_dma_supported); | ||
128 | |||
129 | #endif | ||
diff --git a/arch/ia64/kernel/pci-swiotlb.c b/arch/ia64/kernel/pci-swiotlb.c new file mode 100644 index 000000000000..16c50516dbc1 --- /dev/null +++ b/arch/ia64/kernel/pci-swiotlb.c | |||
@@ -0,0 +1,46 @@ | |||
1 | /* Glue code to lib/swiotlb.c */ | ||
2 | |||
3 | #include <linux/pci.h> | ||
4 | #include <linux/cache.h> | ||
5 | #include <linux/module.h> | ||
6 | #include <linux/dma-mapping.h> | ||
7 | |||
8 | #include <asm/swiotlb.h> | ||
9 | #include <asm/dma.h> | ||
10 | #include <asm/iommu.h> | ||
11 | #include <asm/machvec.h> | ||
12 | |||
13 | int swiotlb __read_mostly; | ||
14 | EXPORT_SYMBOL(swiotlb); | ||
15 | |||
16 | struct dma_mapping_ops swiotlb_dma_ops = { | ||
17 | .mapping_error = swiotlb_dma_mapping_error, | ||
18 | .alloc_coherent = swiotlb_alloc_coherent, | ||
19 | .free_coherent = swiotlb_free_coherent, | ||
20 | .map_single = swiotlb_map_single, | ||
21 | .unmap_single = swiotlb_unmap_single, | ||
22 | .sync_single_for_cpu = swiotlb_sync_single_for_cpu, | ||
23 | .sync_single_for_device = swiotlb_sync_single_for_device, | ||
24 | .sync_single_range_for_cpu = swiotlb_sync_single_range_for_cpu, | ||
25 | .sync_single_range_for_device = swiotlb_sync_single_range_for_device, | ||
26 | .sync_sg_for_cpu = swiotlb_sync_sg_for_cpu, | ||
27 | .sync_sg_for_device = swiotlb_sync_sg_for_device, | ||
28 | .map_sg = swiotlb_map_sg, | ||
29 | .unmap_sg = swiotlb_unmap_sg, | ||
30 | .dma_supported_op = swiotlb_dma_supported, | ||
31 | }; | ||
32 | |||
33 | void __init pci_swiotlb_init(void) | ||
34 | { | ||
35 | if (!iommu_detected) { | ||
36 | #ifdef CONFIG_IA64_GENERIC | ||
37 | swiotlb = 1; | ||
38 | printk(KERN_INFO "PCI-DMA: Re-initialize machine vector.\n"); | ||
39 | machvec_init("dig"); | ||
40 | swiotlb_init(); | ||
41 | dma_ops = &swiotlb_dma_ops; | ||
42 | #else | ||
43 | panic("Unable to find Intel IOMMU"); | ||
44 | #endif | ||
45 | } | ||
46 | } | ||
diff --git a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c index de636b215677..2a67a74a48fe 100644 --- a/arch/ia64/kernel/setup.c +++ b/arch/ia64/kernel/setup.c | |||
@@ -116,6 +116,13 @@ unsigned int num_io_spaces; | |||
116 | */ | 116 | */ |
117 | #define I_CACHE_STRIDE_SHIFT 5 /* Safest way to go: 32 bytes by 32 bytes */ | 117 | #define I_CACHE_STRIDE_SHIFT 5 /* Safest way to go: 32 bytes by 32 bytes */ |
118 | unsigned long ia64_i_cache_stride_shift = ~0; | 118 | unsigned long ia64_i_cache_stride_shift = ~0; |
119 | /* | ||
120 | * "clflush_cache_range()" needs to know what processor dependent stride size to | ||
121 | * use when it flushes cache lines including both d-cache and i-cache. | ||
122 | */ | ||
123 | /* Safest way to go: 32 bytes by 32 bytes */ | ||
124 | #define CACHE_STRIDE_SHIFT 5 | ||
125 | unsigned long ia64_cache_stride_shift = ~0; | ||
119 | 126 | ||
120 | /* | 127 | /* |
121 | * The merge_mask variable needs to be set to (max(iommu_page_size(iommu)) - 1). This | 128 | * The merge_mask variable needs to be set to (max(iommu_page_size(iommu)) - 1). This |
@@ -847,13 +854,14 @@ setup_per_cpu_areas (void) | |||
847 | } | 854 | } |
848 | 855 | ||
849 | /* | 856 | /* |
850 | * Calculate the max. cache line size. | 857 | * Do the following calculations: |
851 | * | 858 | * |
852 | * In addition, the minimum of the i-cache stride sizes is calculated for | 859 | * 1. the max. cache line size. |
853 | * "flush_icache_range()". | 860 | * 2. the minimum of the i-cache stride sizes for "flush_icache_range()". |
861 | * 3. the minimum of the cache stride sizes for "clflush_cache_range()". | ||
854 | */ | 862 | */ |
855 | static void __cpuinit | 863 | static void __cpuinit |
856 | get_max_cacheline_size (void) | 864 | get_cache_info(void) |
857 | { | 865 | { |
858 | unsigned long line_size, max = 1; | 866 | unsigned long line_size, max = 1; |
859 | u64 l, levels, unique_caches; | 867 | u64 l, levels, unique_caches; |
@@ -867,12 +875,14 @@ get_max_cacheline_size (void) | |||
867 | max = SMP_CACHE_BYTES; | 875 | max = SMP_CACHE_BYTES; |
868 | /* Safest setup for "flush_icache_range()" */ | 876 | /* Safest setup for "flush_icache_range()" */ |
869 | ia64_i_cache_stride_shift = I_CACHE_STRIDE_SHIFT; | 877 | ia64_i_cache_stride_shift = I_CACHE_STRIDE_SHIFT; |
878 | /* Safest setup for "clflush_cache_range()" */ | ||
879 | ia64_cache_stride_shift = CACHE_STRIDE_SHIFT; | ||
870 | goto out; | 880 | goto out; |
871 | } | 881 | } |
872 | 882 | ||
873 | for (l = 0; l < levels; ++l) { | 883 | for (l = 0; l < levels; ++l) { |
874 | status = ia64_pal_cache_config_info(l, /* cache_type (data_or_unified)= */ 2, | 884 | /* cache_type (data_or_unified)=2 */ |
875 | &cci); | 885 | status = ia64_pal_cache_config_info(l, 2, &cci); |
876 | if (status != 0) { | 886 | if (status != 0) { |
877 | printk(KERN_ERR | 887 | printk(KERN_ERR |
878 | "%s: ia64_pal_cache_config_info(l=%lu, 2) failed (status=%ld)\n", | 888 | "%s: ia64_pal_cache_config_info(l=%lu, 2) failed (status=%ld)\n", |
@@ -880,15 +890,21 @@ get_max_cacheline_size (void) | |||
880 | max = SMP_CACHE_BYTES; | 890 | max = SMP_CACHE_BYTES; |
881 | /* The safest setup for "flush_icache_range()" */ | 891 | /* The safest setup for "flush_icache_range()" */ |
882 | cci.pcci_stride = I_CACHE_STRIDE_SHIFT; | 892 | cci.pcci_stride = I_CACHE_STRIDE_SHIFT; |
893 | /* The safest setup for "clflush_cache_range()" */ | ||
894 | ia64_cache_stride_shift = CACHE_STRIDE_SHIFT; | ||
883 | cci.pcci_unified = 1; | 895 | cci.pcci_unified = 1; |
896 | } else { | ||
897 | if (cci.pcci_stride < ia64_cache_stride_shift) | ||
898 | ia64_cache_stride_shift = cci.pcci_stride; | ||
899 | |||
900 | line_size = 1 << cci.pcci_line_size; | ||
901 | if (line_size > max) | ||
902 | max = line_size; | ||
884 | } | 903 | } |
885 | line_size = 1 << cci.pcci_line_size; | 904 | |
886 | if (line_size > max) | ||
887 | max = line_size; | ||
888 | if (!cci.pcci_unified) { | 905 | if (!cci.pcci_unified) { |
889 | status = ia64_pal_cache_config_info(l, | 906 | /* cache_type (instruction)=1*/ |
890 | /* cache_type (instruction)= */ 1, | 907 | status = ia64_pal_cache_config_info(l, 1, &cci); |
891 | &cci); | ||
892 | if (status != 0) { | 908 | if (status != 0) { |
893 | printk(KERN_ERR | 909 | printk(KERN_ERR |
894 | "%s: ia64_pal_cache_config_info(l=%lu, 1) failed (status=%ld)\n", | 910 | "%s: ia64_pal_cache_config_info(l=%lu, 1) failed (status=%ld)\n", |
@@ -942,7 +958,7 @@ cpu_init (void) | |||
942 | } | 958 | } |
943 | #endif | 959 | #endif |
944 | 960 | ||
945 | get_max_cacheline_size(); | 961 | get_cache_info(); |
946 | 962 | ||
947 | /* | 963 | /* |
948 | * We can't pass "local_cpu_data" to identify_cpu() because we haven't called | 964 | * We can't pass "local_cpu_data" to identify_cpu() because we haven't called |