1 // QEMU multi-CPU initialization code
3 // Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2006 Fabrice Bellard
6 // This file may be distributed under the terms of the GNU LGPLv3 license.
8 #include "config.h" // CONFIG_*
9 #include "hw/rtc.h" // CMOS_BIOS_SMP_COUNT
10 #include "output.h" // dprintf
11 #include "romfile.h" // romfile_loadint
12 #include "stacks.h" // yield
13 #include "util.h" // smp_setup
14 #include "x86.h" // wrmsr
16 #define APIC_ICR_LOW ((u8*)BUILD_APIC_ADDR + 0x300)
17 #define APIC_SVR ((u8*)BUILD_APIC_ADDR + 0x0F0)
18 #define APIC_LINT0 ((u8*)BUILD_APIC_ADDR + 0x350)
19 #define APIC_LINT1 ((u8*)BUILD_APIC_ADDR + 0x360)
21 #define APIC_ENABLED 0x0100
23 static struct { u32 index; u64 val; } smp_mtrr[32];
24 static u32 smp_mtrr_count;
27 wrmsr_smp(u32 index, u64 val)
30 if (smp_mtrr_count >= ARRAY_SIZE(smp_mtrr)) {
34 smp_mtrr[smp_mtrr_count].index = index;
35 smp_mtrr[smp_mtrr_count].val = val;
41 // 256 bits for the found APIC IDs
42 static u32 FoundAPICIDs[256/32];
44 int apic_id_is_present(u8 apic_id)
46 return !!(FoundAPICIDs[apic_id/32] & (1ul << (apic_id % 32)));
56 setcr0(getcr0() & ~(CR0_CD|CR0_NW));
59 u32 eax, ebx, ecx, cpuid_features;
60 cpuid(1, &eax, &ebx, &ecx, &cpuid_features);
62 dprintf(DEBUG_HDL_smp, "handle_smp: apic_id=%d\n", apic_id);
66 for (i=0; i<smp_mtrr_count; i++)
67 wrmsr(smp_mtrr[i].index, smp_mtrr[i].val);
69 // Set bit on FoundAPICIDs
70 FoundAPICIDs[apic_id/32] |= (1 << (apic_id % 32));
75 // Atomic lock for shared stack across processors.
76 u32 SMPLock __VISIBLE;
77 u32 SMPStack __VISIBLE;
79 // find and initialize the CPUs by launching a SIPI to them
87 u32 eax, ebx, ecx, cpuid_features;
88 cpuid(1, &eax, &ebx, &ecx, &cpuid_features);
89 if (eax < 1 || !(cpuid_features & CPUID_APIC)) {
90 // No apic - only the main cpu is present.
91 dprintf(1, "No apic - only the main cpu is present.\n");
97 // mark the BSP initial APIC ID as found, too:
99 FoundAPICIDs[apic_id/32] |= (1 << (apic_id % 32));
102 // Setup jump trampoline to counter code.
103 u64 old = *(u64*)BUILD_AP_BOOT_ADDR;
104 // ljmpw $SEG_BIOS, $(entry_smp - BUILD_BIOS_ADDR)
105 extern void entry_smp(void);
106 u64 new = (0xea | ((u64)SEG_BIOS<<24)
107 | (((u32)entry_smp - BUILD_BIOS_ADDR) << 8));
108 *(u64*)BUILD_AP_BOOT_ADDR = new;
111 u32 val = readl(APIC_SVR);
112 writel(APIC_SVR, val | APIC_ENABLED);
114 /* Set LINT0 as Ext_INT, level triggered */
115 writel(APIC_LINT0, 0x8700);
117 /* Set LINT1 as NMI, level triggered */
118 writel(APIC_LINT1, 0x8400);
125 writel(APIC_ICR_LOW, 0x000C4500);
126 u32 sipi_vector = BUILD_AP_BOOT_ADDR >> 12;
127 writel(APIC_ICR_LOW, 0x000C4600 | sipi_vector);
129 // Wait for other CPUs to process the SIPI.
130 u8 cmos_smp_count = rtc_read(CMOS_BIOS_SMP_COUNT) + 1;
131 while (cmos_smp_count != CountCPUs)
133 // Release lock and allow other processors to use the stack.
136 // Reacquire lock and take back ownership of stack.
138 " lock btsl $0, %0\n"
140 : "+m" (SMPLock), "+m" (SMPStack)
145 *(u64*)BUILD_AP_BOOT_ADDR = old;
147 MaxCountCPUs = romfile_loadint("etc/max-cpus", 0);
148 if (!MaxCountCPUs || MaxCountCPUs < CountCPUs)
149 MaxCountCPUs = CountCPUs;
151 dprintf(1, "Found %d cpu(s) max supported %d cpu(s)\n", CountCPUs,