Execute smp detect code in place instead of copying it.
[seabios.git] / src / smpdetect.c
1 // CPU count detection
2 //
3 // Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2006 Fabrice Bellard
5 //
6 // This file may be distributed under the terms of the GNU GPLv3 license.
7
8 #include "util.h" // dprintf
9 #include "config.h" // CONFIG_*
10
11 #define CPUID_APIC (1 << 9)
12
13 #define APIC_BASE    ((u8 *)0xfee00000)
14 #define APIC_ICR_LOW 0x300
15 #define APIC_SVR     0x0F0
16 #define APIC_ID      0x020
17 #define APIC_LVT3    0x370
18
19 #define APIC_ENABLED 0x0100
20
21 static inline void writel(void *addr, u32 val)
22 {
23     *(volatile u32 *)addr = val;
24 }
25
26 static inline void writew(void *addr, u16 val)
27 {
28     *(volatile u16 *)addr = val;
29 }
30
31 static inline void writeb(void *addr, u8 val)
32 {
33     *(volatile u8 *)addr = val;
34 }
35
36 static inline u32 readl(const void *addr)
37 {
38     return *(volatile const u32 *)addr;
39 }
40
41 static inline u16 readw(const void *addr)
42 {
43     return *(volatile const u16 *)addr;
44 }
45
46 static inline u8 readb(const void *addr)
47 {
48     return *(volatile const u8 *)addr;
49 }
50
51 extern void smp_ap_boot_code();
52 extern u32 smp_cpus;
53 #if MODE16
54 u32 smp_cpus VISIBLE16;
55 asm(
56     "  .global smp_ap_boot_code\n"
57     "smp_ap_boot_code:\n"
58     // Increament the cpu counter
59     "  movw $" __stringify(SEG_BIOS) ", %ax\n"
60     "  movw %ax, %ds\n"
61     "  lock incl smp_cpus\n"
62     // Halt the processor.
63     "  jmp permanent_halt\n"
64     );
65 #endif
66
67 /* find the number of CPUs by launching a SIPI to them */
68 int
69 smp_probe(void)
70 {
71     if (smp_cpus)
72         return smp_cpus;
73
74     u32 eax, ebx, ecx, cpuid_features;
75     cpuid(1, &eax, &ebx, &ecx, &cpuid_features);
76     if (! (cpuid_features & CPUID_APIC)) {
77         // No apic - only the main cpu is present.
78         smp_cpus = 1;
79         return 1;
80     }
81
82     // Init the counter.
83     writel(&smp_cpus, 1);
84
85     // Setup jump trampoline to counter code.
86     u64 old = *(u64*)BUILD_AP_BOOT_ADDR;
87     // ljmpw $SEG_BIOS, $(smp_ap_boot_code - BUILD_BIOS_ADDR)
88     u64 new = (0xea | ((u64)SEG_BIOS<<24)
89                | (((u32)smp_ap_boot_code - BUILD_BIOS_ADDR) << 8));
90     *(u64*)BUILD_AP_BOOT_ADDR = new;
91
92     // enable local APIC
93     u32 val = readl(APIC_BASE + APIC_SVR);
94     writel(APIC_BASE + APIC_SVR, val | APIC_ENABLED);
95
96     // broadcast SIPI
97     writel(APIC_BASE + APIC_ICR_LOW, 0x000C4500);
98     u32 sipi_vector = BUILD_AP_BOOT_ADDR >> 12;
99     writel(APIC_BASE + APIC_ICR_LOW, 0x000C4600 | sipi_vector);
100
101     // Wait for other CPUs to process the SIPI.
102     mdelay(10);
103
104     // Restore memory.
105     writel(APIC_BASE + APIC_SVR, val);
106     *(u64*)BUILD_AP_BOOT_ADDR = old;
107
108     u32 count = readl(&smp_cpus);
109     dprintf(1, "Found %d cpu(s)\n", count);
110     return count;
111 }
112
113 // Reset smp_cpus to zero (forces a recheck on reboots).
114 void
115 smp_probe_setup(void)
116 {
117     smp_cpus = 0;
118 }