]>
Commit | Line | Data |
---|---|---|
2cb7cef9 BS |
1 | From: Suresh Siddha <suresh.b.siddha@intel.com> |
2 | Subject: x64, x2apic/intr-remap: add x2apic support, including enabling interrupt-remapping | |
3 | References: fate #303948 and fate #303984 | |
4 | Patch-Mainline: queued for .28 | |
5 | Commit-ID: 6e1cb38a2aef7680975e71f23de187859ee8b158 | |
6 | ||
7 | Signed-off-by: Thomas Renninger <trenn@suse.de> | |
8 | ||
9 | x2apic support. Interrupt-remapping must be enabled before enabling x2apic, | |
10 | this is needed to ensure that IO interrupts continue to work properly after the | |
11 | cpu mode is changed to x2apic(which uses 32bit extended physical/cluster | |
12 | apic id). | |
13 | ||
14 | On systems where apicid's are > 255, BIOS can handover the control to OS in | |
15 | x2apic mode. Or if the OS handover was in legacy xapic mode, check | |
16 | if it is capable of x2apic mode. And if we succeed in enabling | |
17 | Interrupt-remapping, then we can enable x2apic mode in the CPU. | |
18 | ||
19 | Signed-off-by: Suresh Siddha <suresh.b.siddha@intel.com> | |
20 | Cc: akpm@linux-foundation.org | |
21 | Cc: arjan@linux.intel.com | |
22 | Cc: andi@firstfloor.org | |
23 | Cc: ebiederm@xmission.com | |
24 | Cc: jbarnes@virtuousgeek.org | |
25 | Cc: steiner@sgi.com | |
26 | Signed-off-by: Ingo Molnar <mingo@elte.hu> | |
27 | ||
28 | --- | |
29 | Documentation/kernel-parameters.txt | 2 | |
30 | arch/x86/kernel/acpi/boot.c | 2 | |
31 | arch/x86/kernel/apic_64.c | 154 +++++++++++++++++++++++++++++++++++- | |
32 | arch/x86/kernel/cpu/common_64.c | 2 | |
33 | arch/x86/kernel/mpparse.c | 2 | |
34 | arch/x86/kernel/setup.c | 2 | |
35 | arch/x86/kernel/smpboot.c | 5 + | |
36 | include/asm-x86/apic.h | 14 +-- | |
37 | 8 files changed, 172 insertions(+), 11 deletions(-) | |
38 | ||
39 | --- a/Documentation/kernel-parameters.txt | |
40 | +++ b/Documentation/kernel-parameters.txt | |
82094b55 | 41 | @@ -1428,6 +1428,8 @@ and is between 256 and 4096 characters. |
2cb7cef9 BS |
42 | |
43 | nolapic_timer [X86-32,APIC] Do not use the local APIC timer. | |
44 | ||
45 | + nox2apic [X86-64,APIC] Do not enable x2APIC mode. | |
46 | + | |
47 | noltlbs [PPC] Do not use large page/tlb entries for kernel | |
48 | lowmem mapping on PPC40x. | |
49 | ||
50 | --- a/arch/x86/kernel/acpi/boot.c | |
51 | +++ b/arch/x86/kernel/acpi/boot.c | |
52 | @@ -1351,7 +1351,9 @@ static void __init acpi_process_madt(voi | |
53 | acpi_ioapic = 1; | |
54 | ||
55 | smp_found_config = 1; | |
56 | +#ifdef CONFIG_X86_32 | |
57 | setup_apic_routing(); | |
58 | +#endif | |
59 | } | |
60 | } | |
61 | if (error == -EINVAL) { | |
62 | --- a/arch/x86/kernel/apic_64.c | |
63 | +++ b/arch/x86/kernel/apic_64.c | |
64 | @@ -27,6 +27,7 @@ | |
65 | #include <linux/clockchips.h> | |
66 | #include <linux/acpi_pmtmr.h> | |
67 | #include <linux/module.h> | |
68 | +#include <linux/dmar.h> | |
69 | ||
70 | #include <asm/atomic.h> | |
71 | #include <asm/smp.h> | |
72 | @@ -39,6 +40,7 @@ | |
73 | #include <asm/proto.h> | |
74 | #include <asm/timex.h> | |
75 | #include <asm/apic.h> | |
76 | +#include <asm/i8259.h> | |
77 | ||
78 | #include <mach_ipi.h> | |
79 | #include <mach_apic.h> | |
80 | @@ -46,8 +48,12 @@ | |
81 | static int disable_apic_timer __cpuinitdata; | |
82 | static int apic_calibrate_pmtmr __initdata; | |
83 | int disable_apic; | |
84 | +int disable_x2apic; | |
85 | int x2apic; | |
86 | ||
87 | +/* x2apic enabled before OS handover */ | |
88 | +int x2apic_preenabled; | |
89 | + | |
90 | /* Local APIC timer works in C2 */ | |
91 | int local_apic_timer_c2_ok; | |
92 | EXPORT_SYMBOL_GPL(local_apic_timer_c2_ok); | |
82094b55 | 93 | @@ -925,6 +931,125 @@ void __cpuinit end_local_APIC_setup(void |
2cb7cef9 BS |
94 | apic_pm_activate(); |
95 | } | |
96 | ||
97 | +void check_x2apic(void) | |
98 | +{ | |
99 | + int msr, msr2; | |
100 | + | |
101 | + rdmsr(MSR_IA32_APICBASE, msr, msr2); | |
102 | + | |
103 | + if (msr & X2APIC_ENABLE) { | |
104 | + printk("x2apic enabled by BIOS, switching to x2apic ops\n"); | |
105 | + x2apic_preenabled = x2apic = 1; | |
106 | + apic_ops = &x2apic_ops; | |
107 | + } | |
108 | +} | |
109 | + | |
110 | +void enable_x2apic(void) | |
111 | +{ | |
112 | + int msr, msr2; | |
113 | + | |
114 | + rdmsr(MSR_IA32_APICBASE, msr, msr2); | |
115 | + if (!(msr & X2APIC_ENABLE)) { | |
116 | + printk("Enabling x2apic\n"); | |
117 | + wrmsr(MSR_IA32_APICBASE, msr | X2APIC_ENABLE, 0); | |
118 | + } | |
119 | +} | |
120 | + | |
121 | +void enable_IR_x2apic(void) | |
122 | +{ | |
123 | +#ifdef CONFIG_INTR_REMAP | |
124 | + int ret; | |
125 | + unsigned long flags; | |
126 | + | |
127 | + if (!cpu_has_x2apic) | |
128 | + return; | |
129 | + | |
130 | + if (!x2apic_preenabled && disable_x2apic) { | |
131 | + printk(KERN_INFO | |
132 | + "Skipped enabling x2apic and Interrupt-remapping " | |
133 | + "because of nox2apic\n"); | |
134 | + return; | |
135 | + } | |
136 | + | |
137 | + if (x2apic_preenabled && disable_x2apic) | |
138 | + panic("Bios already enabled x2apic, can't enforce nox2apic"); | |
139 | + | |
140 | + if (!x2apic_preenabled && skip_ioapic_setup) { | |
141 | + printk(KERN_INFO | |
142 | + "Skipped enabling x2apic and Interrupt-remapping " | |
143 | + "because of skipping io-apic setup\n"); | |
144 | + return; | |
145 | + } | |
146 | + | |
147 | + ret = dmar_table_init(); | |
148 | + if (ret) { | |
149 | + printk(KERN_INFO | |
150 | + "dmar_table_init() failed with %d:\n", ret); | |
151 | + | |
152 | + if (x2apic_preenabled) | |
153 | + panic("x2apic enabled by bios. But IR enabling failed"); | |
154 | + else | |
155 | + printk(KERN_INFO | |
156 | + "Not enabling x2apic,Intr-remapping\n"); | |
157 | + return; | |
158 | + } | |
159 | + | |
160 | + local_irq_save(flags); | |
161 | + mask_8259A(); | |
162 | + save_mask_IO_APIC_setup(); | |
163 | + | |
164 | + ret = enable_intr_remapping(1); | |
165 | + | |
166 | + if (ret && x2apic_preenabled) { | |
167 | + local_irq_restore(flags); | |
168 | + panic("x2apic enabled by bios. But IR enabling failed"); | |
169 | + } | |
170 | + | |
171 | + if (ret) | |
172 | + goto end; | |
173 | + | |
174 | + if (!x2apic) { | |
175 | + x2apic = 1; | |
176 | + apic_ops = &x2apic_ops; | |
177 | + enable_x2apic(); | |
178 | + } | |
179 | +end: | |
180 | + if (ret) | |
181 | + /* | |
182 | + * IR enabling failed | |
183 | + */ | |
184 | + restore_IO_APIC_setup(); | |
185 | + else | |
186 | + reinit_intr_remapped_IO_APIC(x2apic_preenabled); | |
187 | + | |
188 | + unmask_8259A(); | |
189 | + local_irq_restore(flags); | |
190 | + | |
191 | + if (!ret) { | |
192 | + if (!x2apic_preenabled) | |
193 | + printk(KERN_INFO | |
194 | + "Enabled x2apic and interrupt-remapping\n"); | |
195 | + else | |
196 | + printk(KERN_INFO | |
197 | + "Enabled Interrupt-remapping\n"); | |
198 | + } else | |
199 | + printk(KERN_ERR | |
200 | + "Failed to enable Interrupt-remapping and x2apic\n"); | |
201 | +#else | |
202 | + if (!cpu_has_x2apic) | |
203 | + return; | |
204 | + | |
205 | + if (x2apic_preenabled) | |
206 | + panic("x2apic enabled prior OS handover," | |
207 | + " enable CONFIG_INTR_REMAP"); | |
208 | + | |
209 | + printk(KERN_INFO "Enable CONFIG_INTR_REMAP for enabling intr-remapping " | |
210 | + " and x2apic\n"); | |
211 | +#endif | |
212 | + | |
213 | + return; | |
214 | +} | |
215 | + | |
216 | /* | |
217 | * Detect and enable local APICs on non-SMP boards. | |
218 | * Original code written by Keir Fraser. | |
82094b55 | 219 | @@ -972,6 +1097,11 @@ void __init early_init_lapic_mapping(voi |
2cb7cef9 BS |
220 | */ |
221 | void __init init_apic_mappings(void) | |
222 | { | |
223 | + if (x2apic) { | |
224 | + boot_cpu_physical_apicid = GET_APIC_ID(read_apic_id()); | |
225 | + return; | |
226 | + } | |
227 | + | |
228 | /* | |
229 | * If no local APIC can be found then set up a fake all | |
230 | * zeroes page to simulate the local APIC and another | |
82094b55 | 231 | @@ -1010,6 +1140,9 @@ int __init APIC_init_uniprocessor(void) |
2cb7cef9 BS |
232 | return -1; |
233 | } | |
234 | ||
235 | + enable_IR_x2apic(); | |
236 | + setup_apic_routing(); | |
237 | + | |
238 | verify_local_APIC(); | |
239 | ||
240 | connect_bsp_APIC(); | |
82094b55 | 241 | @@ -1263,10 +1396,14 @@ static int lapic_resume(struct sys_devic |
2cb7cef9 BS |
242 | maxlvt = lapic_get_maxlvt(); |
243 | ||
244 | local_irq_save(flags); | |
245 | - rdmsr(MSR_IA32_APICBASE, l, h); | |
246 | - l &= ~MSR_IA32_APICBASE_BASE; | |
247 | - l |= MSR_IA32_APICBASE_ENABLE | mp_lapic_addr; | |
248 | - wrmsr(MSR_IA32_APICBASE, l, h); | |
249 | + if (!x2apic) { | |
250 | + rdmsr(MSR_IA32_APICBASE, l, h); | |
251 | + l &= ~MSR_IA32_APICBASE_BASE; | |
252 | + l |= MSR_IA32_APICBASE_ENABLE | mp_lapic_addr; | |
253 | + wrmsr(MSR_IA32_APICBASE, l, h); | |
254 | + } else | |
255 | + enable_x2apic(); | |
256 | + | |
257 | apic_write(APIC_LVTERR, ERROR_APIC_VECTOR | APIC_LVT_MASKED); | |
258 | apic_write(APIC_ID, apic_pm_state.apic_id); | |
259 | apic_write(APIC_DFR, apic_pm_state.apic_dfr); | |
82094b55 | 260 | @@ -1406,6 +1543,15 @@ __cpuinit int apic_is_clustered_box(void |
2cb7cef9 BS |
261 | return (clusters > 2); |
262 | } | |
263 | ||
264 | +static __init int setup_nox2apic(char *str) | |
265 | +{ | |
266 | + disable_x2apic = 1; | |
267 | + clear_cpu_cap(&boot_cpu_data, X86_FEATURE_X2APIC); | |
268 | + return 0; | |
269 | +} | |
270 | +early_param("nox2apic", setup_nox2apic); | |
271 | + | |
272 | + | |
273 | /* | |
274 | * APIC command line parameters | |
275 | */ | |
276 | --- a/arch/x86/kernel/cpu/common_64.c | |
277 | +++ b/arch/x86/kernel/cpu/common_64.c | |
278 | @@ -636,6 +636,8 @@ void __cpuinit cpu_init(void) | |
279 | barrier(); | |
280 | ||
281 | check_efer(); | |
282 | + if (cpu != 0 && x2apic) | |
283 | + enable_x2apic(); | |
284 | ||
285 | /* | |
286 | * set up and load the per-CPU TSS | |
287 | --- a/arch/x86/kernel/mpparse.c | |
288 | +++ b/arch/x86/kernel/mpparse.c | |
289 | @@ -397,7 +397,9 @@ static int __init smp_read_mpc(struct mp | |
290 | generic_bigsmp_probe(); | |
291 | #endif | |
292 | ||
293 | +#ifdef CONFIG_X86_32 | |
294 | setup_apic_routing(); | |
295 | +#endif | |
296 | if (!num_processors) | |
297 | printk(KERN_ERR "MPTABLE: no processors registered!\n"); | |
298 | return num_processors; | |
299 | --- a/arch/x86/kernel/setup.c | |
300 | +++ b/arch/x86/kernel/setup.c | |
301 | @@ -769,6 +769,8 @@ void __init setup_arch(char **cmdline_p) | |
302 | #else | |
303 | num_physpages = max_pfn; | |
304 | ||
82094b55 AF |
305 | + if (cpu_has_x2apic) |
306 | + check_x2apic(); | |
2cb7cef9 BS |
307 | |
308 | /* How many end-of-memory variables you have, grandma! */ | |
309 | /* need this before calling reserve_initrd */ | |
310 | --- a/arch/x86/kernel/smpboot.c | |
311 | +++ b/arch/x86/kernel/smpboot.c | |
312 | @@ -1169,6 +1169,11 @@ void __init native_smp_prepare_cpus(unsi | |
313 | current_thread_info()->cpu = 0; /* needed? */ | |
314 | set_cpu_sibling_map(0); | |
315 | ||
316 | +#ifdef CONFIG_X86_64 | |
317 | + enable_IR_x2apic(); | |
318 | + setup_apic_routing(); | |
319 | +#endif | |
320 | + | |
321 | if (smp_sanity_check(max_cpus) < 0) { | |
322 | printk(KERN_INFO "SMP disabled\n"); | |
323 | disable_smp(); | |
324 | --- a/include/asm-x86/apic.h | |
325 | +++ b/include/asm-x86/apic.h | |
326 | @@ -93,12 +93,13 @@ static inline u32 native_apic_msr_read(u | |
327 | return low; | |
328 | } | |
329 | ||
330 | -#ifdef CONFIG_X86_32 | |
331 | -extern void apic_wait_icr_idle(void); | |
332 | -extern u32 safe_apic_wait_icr_idle(void); | |
333 | -extern void apic_icr_write(u32 low, u32 id); | |
334 | -#else | |
335 | -extern void x2apic_icr_write(u32 low, u32 id); | |
336 | +#ifndef CONFIG_X86_32 | |
337 | + extern int x2apic, x2apic_preenabled; | |
338 | + extern void check_x2apic(void); | |
339 | + extern void enable_x2apic(void); | |
340 | + extern void enable_IR_x2apic(void); | |
341 | + extern void x2apic_icr_write(u32 low, u32 id); | |
342 | +#endif | |
343 | ||
344 | struct apic_ops { | |
345 | u32 (*read)(u32 reg); | |
346 | @@ -119,7 +120,6 @@ extern struct apic_ops *apic_ops; | |
347 | #define apic_icr_write (apic_ops->icr_write) | |
348 | #define apic_wait_icr_idle (apic_ops->wait_icr_idle) | |
349 | #define safe_apic_wait_icr_idle (apic_ops->safe_wait_icr_idle) | |
350 | -#endif | |
351 | ||
352 | extern int get_physical_broadcast(void); | |
353 |