Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | * x86 SMP booting functions |
| 3 | * |
| 4 | * (c) 1995 Alan Cox, Building #3 <alan@redhat.com> |
| 5 | * (c) 1998, 1999, 2000 Ingo Molnar <mingo@redhat.com> |
| 6 | * Copyright 2001 Andi Kleen, SuSE Labs. |
| 7 | * |
| 8 | * Much of the core SMP work is based on previous work by Thomas Radke, to |
| 9 | * whom a great many thanks are extended. |
| 10 | * |
| 11 | * Thanks to Intel for making available several different Pentium, |
| 12 | * Pentium Pro and Pentium-II/Xeon MP machines. |
| 13 | * Original development of Linux SMP code supported by Caldera. |
| 14 | * |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 15 | * This code is released under the GNU General Public License version 2 |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 16 | * |
| 17 | * Fixes |
| 18 | * Felix Koop : NR_CPUS used properly |
| 19 | * Jose Renau : Handle single CPU case. |
| 20 | * Alan Cox : By repeated request 8) - Total BogoMIP report. |
| 21 | * Greg Wright : Fix for kernel stacks panic. |
| 22 | * Erich Boleyn : MP v1.4 and additional changes. |
| 23 | * Matthias Sattler : Changes for 2.1 kernel map. |
| 24 | * Michel Lespinasse : Changes for 2.1 kernel map. |
| 25 | * Michael Chastain : Change trampoline.S to gnu as. |
| 26 | * Alan Cox : Dumb bug: 'B' step PPro's are fine |
| 27 | * Ingo Molnar : Added APIC timers, based on code |
| 28 | * from Jose Renau |
| 29 | * Ingo Molnar : various cleanups and rewrites |
| 30 | * Tigran Aivazian : fixed "0.00 in /proc/uptime on SMP" bug. |
| 31 | * Maciej W. Rozycki : Bits for genuine 82489DX APICs |
| 32 | * Andi Kleen : Changed for SMP boot into long mode. |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 33 | * Rusty Russell : Hacked into shape for new "hotplug" boot process. |
| 34 | * Andi Kleen : Converted to new state machine. |
| 35 | * Various cleanups. |
| 36 | * Probably mostly hotplug CPU ready now. |
Ashok Raj | 76e4f66 | 2005-06-25 14:55:00 -0700 | [diff] [blame] | 37 | * Ashok Raj : CPU hotplug support |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 38 | */ |
| 39 | |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 40 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 41 | #include <linux/init.h> |
| 42 | |
| 43 | #include <linux/mm.h> |
| 44 | #include <linux/kernel_stat.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 45 | #include <linux/bootmem.h> |
| 46 | #include <linux/thread_info.h> |
| 47 | #include <linux/module.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 48 | #include <linux/delay.h> |
| 49 | #include <linux/mc146818rtc.h> |
Andrew Morton | a3bc0db | 2006-09-25 23:32:33 -0700 | [diff] [blame] | 50 | #include <linux/smp.h> |
Christoph Hellwig | 1eeb66a | 2007-05-08 00:27:03 -0700 | [diff] [blame] | 51 | #include <linux/kdebug.h> |
Andrew Morton | a3bc0db | 2006-09-25 23:32:33 -0700 | [diff] [blame] | 52 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 53 | #include <asm/mtrr.h> |
| 54 | #include <asm/pgalloc.h> |
| 55 | #include <asm/desc.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 56 | #include <asm/tlbflush.h> |
| 57 | #include <asm/proto.h> |
Andi Kleen | 7515211 | 2005-05-16 21:53:34 -0700 | [diff] [blame] | 58 | #include <asm/nmi.h> |
Al Viro | 9cdd304 | 2005-09-12 18:49:25 +0200 | [diff] [blame] | 59 | #include <asm/irq.h> |
| 60 | #include <asm/hw_irq.h> |
Ravikiran G Thirumalai | 488fc08 | 2006-02-07 12:58:23 -0800 | [diff] [blame] | 61 | #include <asm/numa.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 62 | |
Glauber de Oliveira Costa | 8d77010 | 2008-03-19 14:25:31 -0300 | [diff] [blame] | 63 | #include <mach_wakecpu.h> |
Glauber de Oliveira Costa | f6bc402 | 2008-03-19 14:25:53 -0300 | [diff] [blame] | 64 | #include <mach_apic.h> |
Glauber de Oliveira Costa | eb44d0a | 2008-03-19 14:25:32 -0300 | [diff] [blame] | 65 | #include <smpboot_hooks.h> |
Glauber de Oliveira Costa | 0717826 | 2008-03-19 14:25:54 -0300 | [diff] [blame] | 66 | #include <mach_apic.h> |
Glauber de Oliveira Costa | 8d77010 | 2008-03-19 14:25:31 -0300 | [diff] [blame] | 67 | |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 68 | /* Set when the idlers are all forked */ |
| 69 | int smp_threads_ready; |
| 70 | |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 71 | cycles_t cacheflush_time; |
| 72 | unsigned long cache_decay_ticks; |
| 73 | |
Glauber de Oliveira Costa | 1db17f5 | 2008-03-19 14:26:07 -0300 | [diff] [blame^] | 74 | static int boot_cpu_logical_apicid; |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 75 | /* |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 76 | * Fall back to non SMP mode after errors. |
| 77 | * |
| 78 | * RED-PEN audit/test this more. I bet there is more state messed up here. |
| 79 | */ |
Ashok Raj | e6982c6 | 2005-06-25 14:54:58 -0700 | [diff] [blame] | 80 | static __init void disable_smp(void) |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 81 | { |
| 82 | cpu_present_map = cpumask_of_cpu(0); |
| 83 | cpu_possible_map = cpumask_of_cpu(0); |
| 84 | if (smp_found_config) |
Glauber de Oliveira Costa | c70dcb7 | 2008-03-19 14:25:58 -0300 | [diff] [blame] | 85 | phys_cpu_present_map = |
| 86 | physid_mask_of_physid(boot_cpu_physical_apicid); |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 87 | else |
| 88 | phys_cpu_present_map = physid_mask_of_physid(0); |
Mike Travis | d5a7430 | 2007-10-16 01:24:05 -0700 | [diff] [blame] | 89 | cpu_set(0, per_cpu(cpu_sibling_map, 0)); |
Mike Travis | 0835761 | 2007-10-16 01:24:04 -0700 | [diff] [blame] | 90 | cpu_set(0, per_cpu(cpu_core_map, 0)); |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 91 | } |
| 92 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 93 | /* |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 94 | * Various sanity checks. |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 95 | */ |
Ashok Raj | e6982c6 | 2005-06-25 14:54:58 -0700 | [diff] [blame] | 96 | static int __init smp_sanity_check(unsigned max_cpus) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 97 | { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 98 | if (!physid_isset(hard_smp_processor_id(), phys_cpu_present_map)) { |
| 99 | printk("weird, boot CPU (#%d) not listed by the BIOS.\n", |
| 100 | hard_smp_processor_id()); |
| 101 | physid_set(hard_smp_processor_id(), phys_cpu_present_map); |
| 102 | } |
| 103 | |
| 104 | /* |
| 105 | * If we couldn't find an SMP configuration at boot time, |
| 106 | * get out of here now! |
| 107 | */ |
| 108 | if (!smp_found_config) { |
| 109 | printk(KERN_NOTICE "SMP motherboard not detected.\n"); |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 110 | disable_smp(); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 111 | if (APIC_init_uniprocessor()) |
| 112 | printk(KERN_NOTICE "Local APIC not detected." |
| 113 | " Using dummy APIC emulation.\n"); |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 114 | return -1; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 115 | } |
| 116 | |
| 117 | /* |
| 118 | * Should not be necessary because the MP table should list the boot |
| 119 | * CPU too, but we do it for the sake of robustness anyway. |
| 120 | */ |
Glauber de Oliveira Costa | c70dcb7 | 2008-03-19 14:25:58 -0300 | [diff] [blame] | 121 | if (!physid_isset(boot_cpu_physical_apicid, phys_cpu_present_map)) { |
| 122 | printk(KERN_NOTICE |
| 123 | "weird, boot CPU (#%d) not listed by the BIOS.\n", |
| 124 | boot_cpu_physical_apicid); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 125 | physid_set(hard_smp_processor_id(), phys_cpu_present_map); |
| 126 | } |
| 127 | |
| 128 | /* |
| 129 | * If we couldn't find a local APIC, then get out of here now! |
| 130 | */ |
Andi Kleen | 11a8e77 | 2006-01-11 22:46:51 +0100 | [diff] [blame] | 131 | if (!cpu_has_apic) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 132 | printk(KERN_ERR "BIOS bug, local APIC #%d not detected!...\n", |
Glauber de Oliveira Costa | c70dcb7 | 2008-03-19 14:25:58 -0300 | [diff] [blame] | 133 | boot_cpu_physical_apicid); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 134 | printk(KERN_ERR "... forcing use of dummy APIC emulation. (tell your hw vendor)\n"); |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 135 | nr_ioapics = 0; |
| 136 | return -1; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 137 | } |
| 138 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 139 | /* |
| 140 | * If SMP should be disabled, then really disable it! |
| 141 | */ |
| 142 | if (!max_cpus) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 143 | printk(KERN_INFO "SMP mode deactivated, forcing use of dummy APIC emulation.\n"); |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 144 | nr_ioapics = 0; |
| 145 | return -1; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 146 | } |
| 147 | |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 148 | return 0; |
| 149 | } |
| 150 | |
Yinghai Lu | 949ec32 | 2008-01-30 13:30:46 +0100 | [diff] [blame] | 151 | static void __init smp_cpu_index_default(void) |
| 152 | { |
| 153 | int i; |
| 154 | struct cpuinfo_x86 *c; |
| 155 | |
| 156 | for_each_cpu_mask(i, cpu_possible_map) { |
| 157 | c = &cpu_data(i); |
| 158 | /* mark all to hotplug */ |
| 159 | c->cpu_index = NR_CPUS; |
| 160 | } |
| 161 | } |
| 162 | |
Mike Travis | 71fff5e | 2007-10-19 20:35:03 +0200 | [diff] [blame] | 163 | /* |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 164 | * Prepare for SMP bootup. The MP table or ACPI has been read |
| 165 | * earlier. Just do some sanity checking here and enable APIC mode. |
| 166 | */ |
Glauber Costa | 7557da6 | 2008-03-03 14:12:38 -0300 | [diff] [blame] | 167 | void __init native_smp_prepare_cpus(unsigned int max_cpus) |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 168 | { |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 169 | nmi_watchdog_default(); |
Yinghai Lu | 949ec32 | 2008-01-30 13:30:46 +0100 | [diff] [blame] | 170 | smp_cpu_index_default(); |
Glauber de Oliveira Costa | 1db17f5 | 2008-03-19 14:26:07 -0300 | [diff] [blame^] | 171 | cpu_callin_map = cpumask_of_cpu(0); |
| 172 | mb(); |
| 173 | |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 174 | current_cpu_data = boot_cpu_data; |
Glauber de Oliveira Costa | 1db17f5 | 2008-03-19 14:26:07 -0300 | [diff] [blame^] | 175 | boot_cpu_logical_apicid = logical_smp_processor_id(); |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 176 | current_thread_info()->cpu = 0; /* needed? */ |
Siddha, Suresh B | 94605ef | 2005-11-05 17:25:54 +0100 | [diff] [blame] | 177 | set_cpu_sibling_map(0); |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 178 | |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 179 | if (smp_sanity_check(max_cpus) < 0) { |
| 180 | printk(KERN_INFO "SMP disabled\n"); |
| 181 | disable_smp(); |
| 182 | return; |
| 183 | } |
| 184 | |
Glauber de Oliveira Costa | 1db17f5 | 2008-03-19 14:26:07 -0300 | [diff] [blame^] | 185 | if (GET_APIC_ID(apic_read(APIC_ID)) != boot_cpu_physical_apicid) { |
| 186 | panic("Boot APIC ID in local APIC unexpected (%d vs %d)", |
| 187 | GET_APIC_ID(apic_read(APIC_ID)), boot_cpu_physical_apicid); |
| 188 | /* Or can we switch back to PIC here? */ |
| 189 | } |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 190 | |
| 191 | /* |
| 192 | * Switch from PIC to APIC mode. |
| 193 | */ |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 194 | setup_local_APIC(); |
| 195 | |
Andi Kleen | 739f33b | 2008-01-30 13:30:40 +0100 | [diff] [blame] | 196 | /* |
| 197 | * Enable IO APIC before setting up error vector |
| 198 | */ |
| 199 | if (!skip_ioapic_setup && nr_ioapics) |
| 200 | enable_IO_APIC(); |
| 201 | end_local_APIC_setup(); |
| 202 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 203 | /* |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 204 | * Set up local APIC timer on boot CPU. |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 205 | */ |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 206 | |
Glauber de Oliveira Costa | 746ef0c | 2008-01-30 13:31:11 +0100 | [diff] [blame] | 207 | setup_boot_clock(); |
Glauber de Oliveira Costa | 4f3ab19 | 2008-03-19 14:25:01 -0300 | [diff] [blame] | 208 | printk(KERN_INFO "CPU%d: ", 0); |
| 209 | print_cpu_info(&cpu_data(0)); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 210 | } |