Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 1 | // CPU count detection |
| 2 | // |
| 3 | // Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net> |
| 4 | // Copyright (C) 2006 Fabrice Bellard |
| 5 | // |
Kevin O'Connor | b1b7c2a | 2009-01-15 20:52:58 -0500 | [diff] [blame] | 6 | // This file may be distributed under the terms of the GNU LGPLv3 license. |
Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 7 | |
| 8 | #include "util.h" // dprintf |
Kevin O'Connor | 9521e26 | 2008-07-04 13:04:29 -0400 | [diff] [blame] | 9 | #include "config.h" // CONFIG_* |
Kevin O'Connor | 31bfad6 | 2008-12-16 23:50:52 -0500 | [diff] [blame] | 10 | #include "cmos.h" // CMOS_BIOS_SMP_COUNT |
Kevin O'Connor | 8470585 | 2009-10-08 22:13:15 -0400 | [diff] [blame] | 11 | #include "paravirt.h" |
Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 12 | |
Kevin O'Connor | f5c1161 | 2008-12-14 10:11:45 -0500 | [diff] [blame] | 13 | #define APIC_ICR_LOW ((u8*)BUILD_APIC_ADDR + 0x300) |
| 14 | #define APIC_SVR ((u8*)BUILD_APIC_ADDR + 0x0F0) |
Kevin O'Connor | 19c1a76 | 2009-11-14 13:43:01 -0500 | [diff] [blame] | 15 | #define APIC_LINT0 ((u8*)BUILD_APIC_ADDR + 0x350) |
| 16 | #define APIC_LINT1 ((u8*)BUILD_APIC_ADDR + 0x360) |
Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 17 | |
| 18 | #define APIC_ENABLED 0x0100 |
| 19 | |
Marcelo Tosatti | bbce6d6 | 2011-07-29 19:40:51 -0300 | [diff] [blame] | 20 | struct { u32 ecx, eax, edx; } smp_mtrr[32] VAR16VISIBLE; |
Kevin O'Connor | 372e071 | 2009-09-09 09:51:31 -0400 | [diff] [blame] | 21 | u32 smp_mtrr_count VAR16VISIBLE; |
Kevin O'Connor | e97ca7b | 2009-06-21 09:10:28 -0400 | [diff] [blame] | 22 | |
| 23 | void |
| 24 | wrmsr_smp(u32 index, u64 val) |
| 25 | { |
| 26 | wrmsr(index, val); |
Kevin O'Connor | e0f87ce | 2011-07-29 19:21:07 -0400 | [diff] [blame^] | 27 | if (smp_mtrr_count >= ARRAY_SIZE(smp_mtrr)) { |
| 28 | warn_noalloc(); |
Kevin O'Connor | e97ca7b | 2009-06-21 09:10:28 -0400 | [diff] [blame] | 29 | return; |
Kevin O'Connor | e0f87ce | 2011-07-29 19:21:07 -0400 | [diff] [blame^] | 30 | } |
Kevin O'Connor | e97ca7b | 2009-06-21 09:10:28 -0400 | [diff] [blame] | 31 | smp_mtrr[smp_mtrr_count].ecx = index; |
| 32 | smp_mtrr[smp_mtrr_count].eax = val; |
| 33 | smp_mtrr[smp_mtrr_count].edx = val >> 32; |
| 34 | smp_mtrr_count++; |
| 35 | } |
| 36 | |
Kevin O'Connor | 372e071 | 2009-09-09 09:51:31 -0400 | [diff] [blame] | 37 | u32 CountCPUs VAR16VISIBLE; |
Kevin O'Connor | 8470585 | 2009-10-08 22:13:15 -0400 | [diff] [blame] | 38 | u32 MaxCountCPUs VAR16VISIBLE; |
Kevin O'Connor | 1ca05b0 | 2010-01-03 17:43:37 -0500 | [diff] [blame] | 39 | extern void smp_ap_boot_code(void); |
Kevin O'Connor | 4a754b3 | 2008-12-28 21:37:27 -0500 | [diff] [blame] | 40 | ASM16( |
Kevin O'Connor | a06bfb6 | 2008-12-06 19:37:56 -0500 | [diff] [blame] | 41 | " .global smp_ap_boot_code\n" |
| 42 | "smp_ap_boot_code:\n" |
Kevin O'Connor | e97ca7b | 2009-06-21 09:10:28 -0400 | [diff] [blame] | 43 | |
| 44 | // Setup data segment |
Kevin O'Connor | a06bfb6 | 2008-12-06 19:37:56 -0500 | [diff] [blame] | 45 | " movw $" __stringify(SEG_BIOS) ", %ax\n" |
Kevin O'Connor | 484270d | 2008-08-17 10:50:57 -0400 | [diff] [blame] | 46 | " movw %ax, %ds\n" |
Kevin O'Connor | e97ca7b | 2009-06-21 09:10:28 -0400 | [diff] [blame] | 47 | |
| 48 | // MTRR setup |
| 49 | " movl $smp_mtrr, %esi\n" |
| 50 | " movl smp_mtrr_count, %ebx\n" |
| 51 | "1:testl %ebx, %ebx\n" |
| 52 | " jz 2f\n" |
| 53 | " movl 0(%esi), %ecx\n" |
| 54 | " movl 4(%esi), %eax\n" |
| 55 | " movl 8(%esi), %edx\n" |
| 56 | " wrmsr\n" |
| 57 | " addl $12, %esi\n" |
| 58 | " decl %ebx\n" |
| 59 | " jmp 1b\n" |
| 60 | "2:\n" |
| 61 | |
| 62 | // Increment the cpu counter |
| 63 | " lock incl CountCPUs\n" |
| 64 | |
Kevin O'Connor | 484270d | 2008-08-17 10:50:57 -0400 | [diff] [blame] | 65 | // Halt the processor. |
Kevin O'Connor | 60b6999 | 2009-02-07 13:25:25 -0500 | [diff] [blame] | 66 | "1:hlt\n" |
| 67 | " jmp 1b\n" |
Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 68 | ); |
Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 69 | |
Kevin O'Connor | e97ca7b | 2009-06-21 09:10:28 -0400 | [diff] [blame] | 70 | // find and initialize the CPUs by launching a SIPI to them |
| 71 | void |
Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 72 | smp_probe(void) |
| 73 | { |
Kevin O'Connor | 52a300f | 2009-12-26 23:32:57 -0500 | [diff] [blame] | 74 | ASSERT32FLAT(); |
Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 75 | u32 eax, ebx, ecx, cpuid_features; |
| 76 | cpuid(1, &eax, &ebx, &ecx, &cpuid_features); |
Lubomir Rintel | 22f6378 | 2010-08-20 13:37:54 +0200 | [diff] [blame] | 77 | if (eax < 1 || !(cpuid_features & CPUID_APIC)) { |
Kevin O'Connor | a06bfb6 | 2008-12-06 19:37:56 -0500 | [diff] [blame] | 78 | // No apic - only the main cpu is present. |
Kevin O'Connor | b49e1e3 | 2009-11-24 09:41:06 -0500 | [diff] [blame] | 79 | dprintf(1, "No apic - only the main cpu is present.\n"); |
Kevin O'Connor | e97ca7b | 2009-06-21 09:10:28 -0400 | [diff] [blame] | 80 | CountCPUs= 1; |
Kevin O'Connor | b49e1e3 | 2009-11-24 09:41:06 -0500 | [diff] [blame] | 81 | MaxCountCPUs = 1; |
Kevin O'Connor | e97ca7b | 2009-06-21 09:10:28 -0400 | [diff] [blame] | 82 | return; |
Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 83 | } |
Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 84 | |
Kevin O'Connor | a06bfb6 | 2008-12-06 19:37:56 -0500 | [diff] [blame] | 85 | // Init the counter. |
Kevin O'Connor | e97ca7b | 2009-06-21 09:10:28 -0400 | [diff] [blame] | 86 | writel(&CountCPUs, 1); |
Kevin O'Connor | a06bfb6 | 2008-12-06 19:37:56 -0500 | [diff] [blame] | 87 | |
| 88 | // Setup jump trampoline to counter code. |
| 89 | u64 old = *(u64*)BUILD_AP_BOOT_ADDR; |
| 90 | // ljmpw $SEG_BIOS, $(smp_ap_boot_code - BUILD_BIOS_ADDR) |
| 91 | u64 new = (0xea | ((u64)SEG_BIOS<<24) |
| 92 | | (((u32)smp_ap_boot_code - BUILD_BIOS_ADDR) << 8)); |
| 93 | *(u64*)BUILD_AP_BOOT_ADDR = new; |
| 94 | |
| 95 | // enable local APIC |
Kevin O'Connor | f5c1161 | 2008-12-14 10:11:45 -0500 | [diff] [blame] | 96 | u32 val = readl(APIC_SVR); |
| 97 | writel(APIC_SVR, val | APIC_ENABLED); |
Kevin O'Connor | a06bfb6 | 2008-12-06 19:37:56 -0500 | [diff] [blame] | 98 | |
Kevin O'Connor | 19c1a76 | 2009-11-14 13:43:01 -0500 | [diff] [blame] | 99 | if (! CONFIG_COREBOOT) { |
| 100 | /* Set LINT0 as Ext_INT, level triggered */ |
| 101 | writel(APIC_LINT0, 0x8700); |
| 102 | |
| 103 | /* Set LINT1 as NMI, level triggered */ |
| 104 | writel(APIC_LINT1, 0x8400); |
| 105 | } |
| 106 | |
Kevin O'Connor | a06bfb6 | 2008-12-06 19:37:56 -0500 | [diff] [blame] | 107 | // broadcast SIPI |
Kevin O'Connor | 808939c | 2010-03-10 22:32:26 -0500 | [diff] [blame] | 108 | barrier(); |
Kevin O'Connor | f5c1161 | 2008-12-14 10:11:45 -0500 | [diff] [blame] | 109 | writel(APIC_ICR_LOW, 0x000C4500); |
Kevin O'Connor | a06bfb6 | 2008-12-06 19:37:56 -0500 | [diff] [blame] | 110 | u32 sipi_vector = BUILD_AP_BOOT_ADDR >> 12; |
Kevin O'Connor | f5c1161 | 2008-12-14 10:11:45 -0500 | [diff] [blame] | 111 | writel(APIC_ICR_LOW, 0x000C4600 | sipi_vector); |
Kevin O'Connor | a06bfb6 | 2008-12-06 19:37:56 -0500 | [diff] [blame] | 112 | |
| 113 | // Wait for other CPUs to process the SIPI. |
Kevin O'Connor | db98107 | 2009-11-09 19:21:44 -0500 | [diff] [blame] | 114 | if (CONFIG_COREBOOT) { |
Kevin O'Connor | 10ad799 | 2009-10-24 11:06:08 -0400 | [diff] [blame] | 115 | msleep(10); |
Kevin O'Connor | db98107 | 2009-11-09 19:21:44 -0500 | [diff] [blame] | 116 | } else { |
| 117 | u8 cmos_smp_count = inb_cmos(CMOS_BIOS_SMP_COUNT); |
| 118 | while (cmos_smp_count + 1 != readl(&CountCPUs)) |
Kevin O'Connor | 808939c | 2010-03-10 22:32:26 -0500 | [diff] [blame] | 119 | yield(); |
Kevin O'Connor | db98107 | 2009-11-09 19:21:44 -0500 | [diff] [blame] | 120 | } |
Kevin O'Connor | a06bfb6 | 2008-12-06 19:37:56 -0500 | [diff] [blame] | 121 | |
| 122 | // Restore memory. |
Kevin O'Connor | a06bfb6 | 2008-12-06 19:37:56 -0500 | [diff] [blame] | 123 | *(u64*)BUILD_AP_BOOT_ADDR = old; |
| 124 | |
Kevin O'Connor | 8470585 | 2009-10-08 22:13:15 -0400 | [diff] [blame] | 125 | MaxCountCPUs = qemu_cfg_get_max_cpus(); |
| 126 | if (!MaxCountCPUs || MaxCountCPUs < CountCPUs) |
| 127 | MaxCountCPUs = CountCPUs; |
| 128 | |
| 129 | dprintf(1, "Found %d cpu(s) max supported %d cpu(s)\n", readl(&CountCPUs), |
| 130 | MaxCountCPUs); |
Kevin O'Connor | 84ad59a | 2008-07-04 05:47:26 -0400 | [diff] [blame] | 131 | } |