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 | |
Glauber de Oliveira Costa | 771263d | 2008-03-19 14:26:09 -0300 | [diff] [blame^] | 53 | #include <asm/acpi.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 54 | #include <asm/mtrr.h> |
| 55 | #include <asm/pgalloc.h> |
| 56 | #include <asm/desc.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 57 | #include <asm/tlbflush.h> |
| 58 | #include <asm/proto.h> |
Andi Kleen | 7515211 | 2005-05-16 21:53:34 -0700 | [diff] [blame] | 59 | #include <asm/nmi.h> |
Al Viro | 9cdd304 | 2005-09-12 18:49:25 +0200 | [diff] [blame] | 60 | #include <asm/irq.h> |
| 61 | #include <asm/hw_irq.h> |
Ravikiran G Thirumalai | 488fc08 | 2006-02-07 12:58:23 -0800 | [diff] [blame] | 62 | #include <asm/numa.h> |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 63 | |
Glauber de Oliveira Costa | 8d77010 | 2008-03-19 14:25:31 -0300 | [diff] [blame] | 64 | #include <mach_wakecpu.h> |
Glauber de Oliveira Costa | f6bc402 | 2008-03-19 14:25:53 -0300 | [diff] [blame] | 65 | #include <mach_apic.h> |
Glauber de Oliveira Costa | eb44d0a | 2008-03-19 14:25:32 -0300 | [diff] [blame] | 66 | #include <smpboot_hooks.h> |
Glauber de Oliveira Costa | 0717826 | 2008-03-19 14:25:54 -0300 | [diff] [blame] | 67 | #include <mach_apic.h> |
Glauber de Oliveira Costa | 8d77010 | 2008-03-19 14:25:31 -0300 | [diff] [blame] | 68 | |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 69 | /* Set when the idlers are all forked */ |
| 70 | int smp_threads_ready; |
| 71 | |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 72 | cycles_t cacheflush_time; |
| 73 | unsigned long cache_decay_ticks; |
| 74 | |
Glauber de Oliveira Costa | 1db17f5 | 2008-03-19 14:26:07 -0300 | [diff] [blame] | 75 | static int boot_cpu_logical_apicid; |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 76 | /* |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 77 | * Fall back to non SMP mode after errors. |
| 78 | * |
| 79 | * RED-PEN audit/test this more. I bet there is more state messed up here. |
| 80 | */ |
Ashok Raj | e6982c6 | 2005-06-25 14:54:58 -0700 | [diff] [blame] | 81 | static __init void disable_smp(void) |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 82 | { |
| 83 | cpu_present_map = cpumask_of_cpu(0); |
| 84 | cpu_possible_map = cpumask_of_cpu(0); |
| 85 | if (smp_found_config) |
Glauber de Oliveira Costa | c70dcb7 | 2008-03-19 14:25:58 -0300 | [diff] [blame] | 86 | phys_cpu_present_map = |
| 87 | physid_mask_of_physid(boot_cpu_physical_apicid); |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 88 | else |
| 89 | phys_cpu_present_map = physid_mask_of_physid(0); |
Mike Travis | d5a7430 | 2007-10-16 01:24:05 -0700 | [diff] [blame] | 90 | cpu_set(0, per_cpu(cpu_sibling_map, 0)); |
Mike Travis | 0835761 | 2007-10-16 01:24:04 -0700 | [diff] [blame] | 91 | cpu_set(0, per_cpu(cpu_core_map, 0)); |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 92 | } |
| 93 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 94 | /* |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 95 | * Various sanity checks. |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 96 | */ |
Ashok Raj | e6982c6 | 2005-06-25 14:54:58 -0700 | [diff] [blame] | 97 | static int __init smp_sanity_check(unsigned max_cpus) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 98 | { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 99 | if (!physid_isset(hard_smp_processor_id(), phys_cpu_present_map)) { |
| 100 | printk("weird, boot CPU (#%d) not listed by the BIOS.\n", |
| 101 | hard_smp_processor_id()); |
| 102 | physid_set(hard_smp_processor_id(), phys_cpu_present_map); |
| 103 | } |
| 104 | |
| 105 | /* |
| 106 | * If we couldn't find an SMP configuration at boot time, |
| 107 | * get out of here now! |
| 108 | */ |
Glauber de Oliveira Costa | 771263d | 2008-03-19 14:26:09 -0300 | [diff] [blame^] | 109 | if (!smp_found_config && !acpi_lapic) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 110 | printk(KERN_NOTICE "SMP motherboard not detected.\n"); |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 111 | disable_smp(); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 112 | if (APIC_init_uniprocessor()) |
| 113 | printk(KERN_NOTICE "Local APIC not detected." |
| 114 | " Using dummy APIC emulation.\n"); |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 115 | return -1; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 116 | } |
| 117 | |
| 118 | /* |
| 119 | * Should not be necessary because the MP table should list the boot |
| 120 | * CPU too, but we do it for the sake of robustness anyway. |
| 121 | */ |
Glauber de Oliveira Costa | 771263d | 2008-03-19 14:26:09 -0300 | [diff] [blame^] | 122 | if (!check_phys_apicid_present(boot_cpu_physical_apicid)) { |
Glauber de Oliveira Costa | c70dcb7 | 2008-03-19 14:25:58 -0300 | [diff] [blame] | 123 | printk(KERN_NOTICE |
| 124 | "weird, boot CPU (#%d) not listed by the BIOS.\n", |
| 125 | boot_cpu_physical_apicid); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 126 | physid_set(hard_smp_processor_id(), phys_cpu_present_map); |
| 127 | } |
| 128 | |
| 129 | /* |
| 130 | * If we couldn't find a local APIC, then get out of here now! |
| 131 | */ |
Glauber de Oliveira Costa | 771263d | 2008-03-19 14:26:09 -0300 | [diff] [blame^] | 132 | if (APIC_INTEGRATED(apic_version[boot_cpu_physical_apicid]) && |
| 133 | !cpu_has_apic) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 134 | 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] | 135 | boot_cpu_physical_apicid); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 136 | 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] | 137 | nr_ioapics = 0; |
| 138 | return -1; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 139 | } |
| 140 | |
Glauber de Oliveira Costa | 771263d | 2008-03-19 14:26:09 -0300 | [diff] [blame^] | 141 | verify_local_APIC(); |
| 142 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 143 | /* |
| 144 | * If SMP should be disabled, then really disable it! |
| 145 | */ |
| 146 | if (!max_cpus) { |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 147 | 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] | 148 | nr_ioapics = 0; |
| 149 | return -1; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 150 | } |
| 151 | |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 152 | return 0; |
| 153 | } |
| 154 | |
Yinghai Lu | 949ec32 | 2008-01-30 13:30:46 +0100 | [diff] [blame] | 155 | static void __init smp_cpu_index_default(void) |
| 156 | { |
| 157 | int i; |
| 158 | struct cpuinfo_x86 *c; |
| 159 | |
| 160 | for_each_cpu_mask(i, cpu_possible_map) { |
| 161 | c = &cpu_data(i); |
| 162 | /* mark all to hotplug */ |
| 163 | c->cpu_index = NR_CPUS; |
| 164 | } |
| 165 | } |
| 166 | |
Mike Travis | 71fff5e | 2007-10-19 20:35:03 +0200 | [diff] [blame] | 167 | /* |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 168 | * Prepare for SMP bootup. The MP table or ACPI has been read |
| 169 | * earlier. Just do some sanity checking here and enable APIC mode. |
| 170 | */ |
Glauber Costa | 7557da6 | 2008-03-03 14:12:38 -0300 | [diff] [blame] | 171 | void __init native_smp_prepare_cpus(unsigned int max_cpus) |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 172 | { |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 173 | nmi_watchdog_default(); |
Yinghai Lu | 949ec32 | 2008-01-30 13:30:46 +0100 | [diff] [blame] | 174 | smp_cpu_index_default(); |
Glauber de Oliveira Costa | 1db17f5 | 2008-03-19 14:26:07 -0300 | [diff] [blame] | 175 | cpu_callin_map = cpumask_of_cpu(0); |
| 176 | mb(); |
| 177 | |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 178 | current_cpu_data = boot_cpu_data; |
Glauber de Oliveira Costa | 1db17f5 | 2008-03-19 14:26:07 -0300 | [diff] [blame] | 179 | boot_cpu_logical_apicid = logical_smp_processor_id(); |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 180 | current_thread_info()->cpu = 0; /* needed? */ |
Siddha, Suresh B | 94605ef | 2005-11-05 17:25:54 +0100 | [diff] [blame] | 181 | set_cpu_sibling_map(0); |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 182 | |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 183 | if (smp_sanity_check(max_cpus) < 0) { |
| 184 | printk(KERN_INFO "SMP disabled\n"); |
| 185 | disable_smp(); |
| 186 | return; |
| 187 | } |
| 188 | |
Glauber de Oliveira Costa | 1db17f5 | 2008-03-19 14:26:07 -0300 | [diff] [blame] | 189 | if (GET_APIC_ID(apic_read(APIC_ID)) != boot_cpu_physical_apicid) { |
| 190 | panic("Boot APIC ID in local APIC unexpected (%d vs %d)", |
| 191 | GET_APIC_ID(apic_read(APIC_ID)), boot_cpu_physical_apicid); |
| 192 | /* Or can we switch back to PIC here? */ |
| 193 | } |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 194 | |
| 195 | /* |
| 196 | * Switch from PIC to APIC mode. |
| 197 | */ |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 198 | setup_local_APIC(); |
| 199 | |
Andi Kleen | 739f33b | 2008-01-30 13:30:40 +0100 | [diff] [blame] | 200 | /* |
| 201 | * Enable IO APIC before setting up error vector |
| 202 | */ |
| 203 | if (!skip_ioapic_setup && nr_ioapics) |
| 204 | enable_IO_APIC(); |
| 205 | end_local_APIC_setup(); |
| 206 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 207 | /* |
Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 208 | * Set up local APIC timer on boot CPU. |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 209 | */ |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 210 | |
Glauber de Oliveira Costa | 746ef0c | 2008-01-30 13:31:11 +0100 | [diff] [blame] | 211 | setup_boot_clock(); |
Glauber de Oliveira Costa | 4f3ab19 | 2008-03-19 14:25:01 -0300 | [diff] [blame] | 212 | printk(KERN_INFO "CPU%d: ", 0); |
| 213 | print_cpu_info(&cpu_data(0)); |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 214 | } |