Move read/write[bwl] from smp.c to util.h.
[seabios.git] / src / smp.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 LGPLv3 license.
7
8 #include "util.h" // dprintf
9 #include "config.h" // CONFIG_*
10 #include "cmos.h" // CMOS_BIOS_SMP_COUNT
11 #include "farptr.h" // ASSERT32
12 #include "paravirt.h"
13
14 #define APIC_ICR_LOW ((u8*)BUILD_APIC_ADDR + 0x300)
15 #define APIC_SVR     ((u8*)BUILD_APIC_ADDR + 0x0F0)
16
17 #define APIC_ENABLED 0x0100
18
19 struct { u32 ecx, eax, edx; } smp_mtrr[16] VAR16VISIBLE;
20 u32 smp_mtrr_count VAR16VISIBLE;
21
22 void
23 wrmsr_smp(u32 index, u64 val)
24 {
25     wrmsr(index, val);
26     if (smp_mtrr_count >= ARRAY_SIZE(smp_mtrr))
27         return;
28     smp_mtrr[smp_mtrr_count].ecx = index;
29     smp_mtrr[smp_mtrr_count].eax = val;
30     smp_mtrr[smp_mtrr_count].edx = val >> 32;
31     smp_mtrr_count++;
32 }
33
34 u32 CountCPUs VAR16VISIBLE;
35 u32 MaxCountCPUs VAR16VISIBLE;
36 extern void smp_ap_boot_code();
37 ASM16(
38     "  .global smp_ap_boot_code\n"
39     "smp_ap_boot_code:\n"
40
41     // Setup data segment
42     "  movw $" __stringify(SEG_BIOS) ", %ax\n"
43     "  movw %ax, %ds\n"
44
45     // MTRR setup
46     "  movl $smp_mtrr, %esi\n"
47     "  movl smp_mtrr_count, %ebx\n"
48     "1:testl %ebx, %ebx\n"
49     "  jz 2f\n"
50     "  movl 0(%esi), %ecx\n"
51     "  movl 4(%esi), %eax\n"
52     "  movl 8(%esi), %edx\n"
53     "  wrmsr\n"
54     "  addl $12, %esi\n"
55     "  decl %ebx\n"
56     "  jmp 1b\n"
57     "2:\n"
58
59     // Increment the cpu counter
60     "  lock incl CountCPUs\n"
61
62     // Halt the processor.
63     "1:hlt\n"
64     "  jmp 1b\n"
65     );
66
67 // find and initialize the CPUs by launching a SIPI to them
68 void
69 smp_probe(void)
70 {
71     ASSERT32();
72     u32 eax, ebx, ecx, cpuid_features;
73     cpuid(1, &eax, &ebx, &ecx, &cpuid_features);
74     if (! (cpuid_features & CPUID_APIC)) {
75         // No apic - only the main cpu is present.
76         CountCPUs= 1;
77         return;
78     }
79
80     // Init the counter.
81     writel(&CountCPUs, 1);
82
83     // Setup jump trampoline to counter code.
84     u64 old = *(u64*)BUILD_AP_BOOT_ADDR;
85     // ljmpw $SEG_BIOS, $(smp_ap_boot_code - BUILD_BIOS_ADDR)
86     u64 new = (0xea | ((u64)SEG_BIOS<<24)
87                | (((u32)smp_ap_boot_code - BUILD_BIOS_ADDR) << 8));
88     *(u64*)BUILD_AP_BOOT_ADDR = new;
89
90     // enable local APIC
91     u32 val = readl(APIC_SVR);
92     writel(APIC_SVR, val | APIC_ENABLED);
93
94     // broadcast SIPI
95     writel(APIC_ICR_LOW, 0x000C4500);
96     u32 sipi_vector = BUILD_AP_BOOT_ADDR >> 12;
97     writel(APIC_ICR_LOW, 0x000C4600 | sipi_vector);
98
99     // Wait for other CPUs to process the SIPI.
100     if (CONFIG_COREBOOT)
101         mdelay(10);
102     else
103         while (inb_cmos(CMOS_BIOS_SMP_COUNT) + 1 != readl(&CountCPUs))
104             ;
105
106     // Restore memory.
107     *(u64*)BUILD_AP_BOOT_ADDR = old;
108
109     MaxCountCPUs = qemu_cfg_get_max_cpus();
110     if (!MaxCountCPUs || MaxCountCPUs < CountCPUs)
111         MaxCountCPUs = CountCPUs;
112
113     dprintf(1, "Found %d cpu(s) max supported %d cpu(s)\n", readl(&CountCPUs),
114         MaxCountCPUs);
115 }
116
117 // Reset variables to zero
118 void
119 smp_probe_setup(void)
120 {
121     CountCPUs = 0;
122     smp_mtrr_count = 0;
123 }