blob: 46d1da1784cf97942838c2ced56e3223bda15811 [file] [log] [blame]
Kevin O'Connor0673b782014-05-24 10:49:50 -04001// QEMU multi-CPU initialization code
Kevin O'Connor84ad59a2008-07-04 05:47:26 -04002//
3// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
4// Copyright (C) 2006 Fabrice Bellard
5//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05006// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connor84ad59a2008-07-04 05:47:26 -04007
Kevin O'Connor9521e262008-07-04 13:04:29 -04008#include "config.h" // CONFIG_*
Kevin O'Connor8b7861c2013-09-15 02:29:06 -04009#include "hw/rtc.h" // CMOS_BIOS_SMP_COUNT
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040010#include "output.h" // dprintf
Kevin O'Connor41639f82013-09-14 19:37:36 -040011#include "romfile.h" // romfile_loadint
Kevin O'Connor3df600b2013-09-14 19:28:55 -040012#include "stacks.h" // yield
Haozhong Zhang20f83d52016-06-22 14:53:24 +080013#include "util.h" // smp_setup, msr_feature_control_setup
Kevin O'Connorb9c6a962013-09-14 13:01:30 -040014#include "x86.h" // wrmsr
Igor Mammedov023b1d02016-10-13 14:38:27 +020015#include "paravirt.h" // qemu_*_present_cpus_count
Kevin O'Connor84ad59a2008-07-04 05:47:26 -040016
Kevin O'Connorf5c11612008-12-14 10:11:45 -050017#define APIC_ICR_LOW ((u8*)BUILD_APIC_ADDR + 0x300)
18#define APIC_SVR ((u8*)BUILD_APIC_ADDR + 0x0F0)
Kevin O'Connor19c1a762009-11-14 13:43:01 -050019#define APIC_LINT0 ((u8*)BUILD_APIC_ADDR + 0x350)
20#define APIC_LINT1 ((u8*)BUILD_APIC_ADDR + 0x360)
Kevin O'Connor84ad59a2008-07-04 05:47:26 -040021
22#define APIC_ENABLED 0x0100
Igor Mammedovcb75c912016-10-13 14:38:28 +020023#define MSR_IA32_APIC_BASE 0x01B
24#define MSR_LOCAL_APIC_ID 0x802
25#define MSR_IA32_APICBASE_EXTD (1ULL << 10) /* Enable x2APIC mode */
Kevin O'Connor84ad59a2008-07-04 05:47:26 -040026
Haozhong Zhang20f83d52016-06-22 14:53:24 +080027static struct { u32 index; u64 val; } smp_msr[32];
28static u32 smp_msr_count;
Kevin O'Connore97ca7b2009-06-21 09:10:28 -040029
30void
31wrmsr_smp(u32 index, u64 val)
32{
33 wrmsr(index, val);
Haozhong Zhang20f83d52016-06-22 14:53:24 +080034 if (smp_msr_count >= ARRAY_SIZE(smp_msr)) {
Kevin O'Connore0f87ce2011-07-29 19:21:07 -040035 warn_noalloc();
Kevin O'Connore97ca7b2009-06-21 09:10:28 -040036 return;
Kevin O'Connore0f87ce2011-07-29 19:21:07 -040037 }
Haozhong Zhang20f83d52016-06-22 14:53:24 +080038 smp_msr[smp_msr_count].index = index;
39 smp_msr[smp_msr_count].val = val;
40 smp_msr_count++;
Kevin O'Connore97ca7b2009-06-21 09:10:28 -040041}
42
Paolo Bonzini54e3a882016-07-07 16:00:40 +020043static void
44smp_write_msrs(void)
45{
46 // MTRR and MSR_IA32_FEATURE_CONTROL setup
47 int i;
48 for (i=0; i<smp_msr_count; i++)
49 wrmsr(smp_msr[i].index, smp_msr[i].val);
50}
51
Kevin O'Connorf96ff442013-08-10 10:37:50 -040052u32 MaxCountCPUs;
Kevin O'Connor0673b782014-05-24 10:49:50 -040053static u32 CountCPUs;
Eduardo Habkost008c1fc2012-07-25 15:45:30 -030054// 256 bits for the found APIC IDs
Kevin O'Connor0673b782014-05-24 10:49:50 -040055static u32 FoundAPICIDs[256/32];
Kevin O'Connor84ad59a2008-07-04 05:47:26 -040056
Eduardo Habkost008c1fc2012-07-25 15:45:30 -030057int apic_id_is_present(u8 apic_id)
58{
Eduardo Habkost7f036852012-08-31 15:11:16 -030059 return !!(FoundAPICIDs[apic_id/32] & (1ul << (apic_id % 32)));
Eduardo Habkost008c1fc2012-07-25 15:45:30 -030060}
61
Kevin O'Connoreb516e42016-10-13 14:38:26 +020062static int
63apic_id_init(void)
64{
Kevin O'Connoreb516e42016-10-13 14:38:26 +020065 u32 eax, ebx, ecx, cpuid_features;
66 cpuid(1, &eax, &ebx, &ecx, &cpuid_features);
Igor Mammedovcb75c912016-10-13 14:38:28 +020067 u32 apic_id = ebx>>24;
68 if (MaxCountCPUs < 256) { // xAPIC mode
69 // Track found apic id for use in legacy internal bios tables
70 FoundAPICIDs[apic_id/32] |= 1 << (apic_id % 32);
71 } else if (ecx & CPUID_X2APIC) {
72 // switch to x2APIC mode
73 u64 apic_base = rdmsr(MSR_IA32_APIC_BASE);
74 wrmsr(MSR_IA32_APIC_BASE, apic_base | MSR_IA32_APICBASE_EXTD);
75 apic_id = rdmsr(MSR_LOCAL_APIC_ID);
76 } else {
77 // x2APIC is masked by CPUID
78 apic_id = -1;
79 }
Kevin O'Connoreb516e42016-10-13 14:38:26 +020080 return apic_id;
81}
82
Kevin O'Connor0673b782014-05-24 10:49:50 -040083void VISIBLE32FLAT
84handle_smp(void)
85{
Kevin O'Connord71d52b2014-06-05 11:08:44 -040086 if (!CONFIG_QEMU)
87 return;
88
Kevin O'Connoreb516e42016-10-13 14:38:26 +020089 // Track this CPU and detect the apic_id
90 int apic_id = apic_id_init();
91 dprintf(DEBUG_HDL_smp, "handle_smp: apic_id=0x%x\n", apic_id);
Kevin O'Connor0673b782014-05-24 10:49:50 -040092
Paolo Bonzini54e3a882016-07-07 16:00:40 +020093 smp_write_msrs();
Kevin O'Connor0673b782014-05-24 10:49:50 -040094
Kevin O'Connor0673b782014-05-24 10:49:50 -040095 CountCPUs++;
96}
97
98// Atomic lock for shared stack across processors.
99u32 SMPLock __VISIBLE;
100u32 SMPStack __VISIBLE;
101
Kevin O'Connore97ca7b2009-06-21 09:10:28 -0400102// find and initialize the CPUs by launching a SIPI to them
Paolo Bonzini54e3a882016-07-07 16:00:40 +0200103static void
104smp_scan(void)
Kevin O'Connor84ad59a2008-07-04 05:47:26 -0400105{
Kevin O'Connor52a300f2009-12-26 23:32:57 -0500106 ASSERT32FLAT();
Kevin O'Connor84ad59a2008-07-04 05:47:26 -0400107 u32 eax, ebx, ecx, cpuid_features;
108 cpuid(1, &eax, &ebx, &ecx, &cpuid_features);
Lubomir Rintel22f63782010-08-20 13:37:54 +0200109 if (eax < 1 || !(cpuid_features & CPUID_APIC)) {
Kevin O'Connora06bfb62008-12-06 19:37:56 -0500110 // No apic - only the main cpu is present.
Kevin O'Connorb49e1e32009-11-24 09:41:06 -0500111 dprintf(1, "No apic - only the main cpu is present.\n");
Kevin O'Connore97ca7b2009-06-21 09:10:28 -0400112 CountCPUs= 1;
113 return;
Kevin O'Connor84ad59a2008-07-04 05:47:26 -0400114 }
Kevin O'Connor84ad59a2008-07-04 05:47:26 -0400115
Eduardo Habkost008c1fc2012-07-25 15:45:30 -0300116 // mark the BSP initial APIC ID as found, too:
Kevin O'Connor0673b782014-05-24 10:49:50 -0400117 CountCPUs = 1;
Kevin O'Connora06bfb62008-12-06 19:37:56 -0500118
119 // Setup jump trampoline to counter code.
120 u64 old = *(u64*)BUILD_AP_BOOT_ADDR;
Kevin O'Connor0673b782014-05-24 10:49:50 -0400121 // ljmpw $SEG_BIOS, $(entry_smp - BUILD_BIOS_ADDR)
122 extern void entry_smp(void);
Kevin O'Connora06bfb62008-12-06 19:37:56 -0500123 u64 new = (0xea | ((u64)SEG_BIOS<<24)
Kevin O'Connor0673b782014-05-24 10:49:50 -0400124 | (((u32)entry_smp - BUILD_BIOS_ADDR) << 8));
Kevin O'Connora06bfb62008-12-06 19:37:56 -0500125 *(u64*)BUILD_AP_BOOT_ADDR = new;
126
127 // enable local APIC
Kevin O'Connorf5c11612008-12-14 10:11:45 -0500128 u32 val = readl(APIC_SVR);
129 writel(APIC_SVR, val | APIC_ENABLED);
Kevin O'Connora06bfb62008-12-06 19:37:56 -0500130
Kevin O'Connor5dbf1732013-02-09 23:58:55 -0500131 /* Set LINT0 as Ext_INT, level triggered */
132 writel(APIC_LINT0, 0x8700);
Kevin O'Connor19c1a762009-11-14 13:43:01 -0500133
Kevin O'Connor5dbf1732013-02-09 23:58:55 -0500134 /* Set LINT1 as NMI, level triggered */
135 writel(APIC_LINT1, 0x8400);
Kevin O'Connor19c1a762009-11-14 13:43:01 -0500136
Kevin O'Connor0673b782014-05-24 10:49:50 -0400137 // Init the lock.
138 writel(&SMPLock, 1);
139
Kevin O'Connora06bfb62008-12-06 19:37:56 -0500140 // broadcast SIPI
Kevin O'Connor808939c2010-03-10 22:32:26 -0500141 barrier();
Kevin O'Connorf5c11612008-12-14 10:11:45 -0500142 writel(APIC_ICR_LOW, 0x000C4500);
Kevin O'Connora06bfb62008-12-06 19:37:56 -0500143 u32 sipi_vector = BUILD_AP_BOOT_ADDR >> 12;
Kevin O'Connorf5c11612008-12-14 10:11:45 -0500144 writel(APIC_ICR_LOW, 0x000C4600 | sipi_vector);
Kevin O'Connora06bfb62008-12-06 19:37:56 -0500145
Igor Mammedovcb75c912016-10-13 14:38:28 +0200146 // switch to x2APIC mode after sending SIPI so that
147 // x2APIC and xAPIC mode could share AP wake up code
148 apic_id_init();
149
Kevin O'Connora06bfb62008-12-06 19:37:56 -0500150 // Wait for other CPUs to process the SIPI.
Igor Mammedov023b1d02016-10-13 14:38:27 +0200151 u16 expected_cpus_count = qemu_get_present_cpus_count();
152 while (expected_cpus_count != CountCPUs)
Kevin O'Connor0673b782014-05-24 10:49:50 -0400153 asm volatile(
154 // Release lock and allow other processors to use the stack.
155 " movl %%esp, %1\n"
156 " movl $0, %0\n"
157 // Reacquire lock and take back ownership of stack.
158 "1:rep ; nop\n"
159 " lock btsl $0, %0\n"
160 " jc 1b\n"
161 : "+m" (SMPLock), "+m" (SMPStack)
162 : : "cc", "memory");
163 yield();
Kevin O'Connora06bfb62008-12-06 19:37:56 -0500164
165 // Restore memory.
Kevin O'Connora06bfb62008-12-06 19:37:56 -0500166 *(u64*)BUILD_AP_BOOT_ADDR = old;
167
Kevin O'Connor0673b782014-05-24 10:49:50 -0400168 dprintf(1, "Found %d cpu(s) max supported %d cpu(s)\n", CountCPUs,
169 MaxCountCPUs);
Kevin O'Connor84ad59a2008-07-04 05:47:26 -0400170}
Paolo Bonzini54e3a882016-07-07 16:00:40 +0200171
172void
173smp_setup(void)
174{
175 if (!CONFIG_QEMU)
176 return;
177
178 MaxCountCPUs = romfile_loadint("etc/max-cpus", 0);
Igor Mammedov88916972016-11-11 16:35:15 +0100179 u16 smp_count = qemu_get_present_cpus_count();
Igor Mammedov023b1d02016-10-13 14:38:27 +0200180 if (MaxCountCPUs < smp_count)
181 MaxCountCPUs = smp_count;
Paolo Bonzini54e3a882016-07-07 16:00:40 +0200182
183 smp_scan();
184}
185
186void
187smp_resume(void)
188{
189 if (!CONFIG_QEMU)
190 return;
191
192 smp_write_msrs();
193 smp_scan();
194}