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 u32 eax, ebx, ecx, cpuid_features;
57 cpuid(1, &eax, &ebx, &ecx, &cpuid_features);
59 dprintf(DEBUG_HDL_smp, "handle_smp: apic_id=%d\n", apic_id);
63 for (i=0; i<smp_mtrr_count; i++)
64 wrmsr(smp_mtrr[i].index, smp_mtrr[i].val);
66 // Set bit on FoundAPICIDs
67 FoundAPICIDs[apic_id/32] |= (1 << (apic_id % 32));
72 // Atomic lock for shared stack across processors.
73 u32 SMPLock __VISIBLE;
74 u32 SMPStack __VISIBLE;
76 // find and initialize the CPUs by launching a SIPI to them
84 u32 eax, ebx, ecx, cpuid_features;
85 cpuid(1, &eax, &ebx, &ecx, &cpuid_features);
86 if (eax < 1 || !(cpuid_features & CPUID_APIC)) {
87 // No apic - only the main cpu is present.
88 dprintf(1, "No apic - only the main cpu is present.\n");
94 // mark the BSP initial APIC ID as found, too:
96 FoundAPICIDs[apic_id/32] |= (1 << (apic_id % 32));
99 // Setup jump trampoline to counter code.
100 u64 old = *(u64*)BUILD_AP_BOOT_ADDR;
101 // ljmpw $SEG_BIOS, $(entry_smp - BUILD_BIOS_ADDR)
102 extern void entry_smp(void);
103 u64 new = (0xea | ((u64)SEG_BIOS<<24)
104 | (((u32)entry_smp - BUILD_BIOS_ADDR) << 8));
105 *(u64*)BUILD_AP_BOOT_ADDR = new;
108 u32 val = readl(APIC_SVR);
109 writel(APIC_SVR, val | APIC_ENABLED);
111 /* Set LINT0 as Ext_INT, level triggered */
112 writel(APIC_LINT0, 0x8700);
114 /* Set LINT1 as NMI, level triggered */
115 writel(APIC_LINT1, 0x8400);
122 writel(APIC_ICR_LOW, 0x000C4500);
123 u32 sipi_vector = BUILD_AP_BOOT_ADDR >> 12;
124 writel(APIC_ICR_LOW, 0x000C4600 | sipi_vector);
126 // Wait for other CPUs to process the SIPI.
127 u8 cmos_smp_count = rtc_read(CMOS_BIOS_SMP_COUNT) + 1;
128 while (cmos_smp_count != CountCPUs)
130 // Release lock and allow other processors to use the stack.
133 // Reacquire lock and take back ownership of stack.
135 " lock btsl $0, %0\n"
137 : "+m" (SMPLock), "+m" (SMPStack)
142 *(u64*)BUILD_AP_BOOT_ADDR = old;
144 MaxCountCPUs = romfile_loadint("etc/max-cpus", 0);
145 if (!MaxCountCPUs || MaxCountCPUs < CountCPUs)
146 MaxCountCPUs = CountCPUs;
148 dprintf(1, "Found %d cpu(s) max supported %d cpu(s)\n", CountCPUs,