3 * SMP support for BIOS.
5 * Copyright (C) 2008 Nguyen Anh Quynh <aquynh@gmail.com>
6 * Copyright (C) 2002 MandrakeSoft S.A.
8 * This file may be distributed under the terms of the GNU GPLv3 license.
16 #include "acpi.h" // ACPI_DATA_SIZE
21 writel(void *addr, u32 val)
23 *(volatile u32 *)addr = val;
27 writew(void *addr, u16 val)
29 *(volatile u16 *)addr = val;
33 readl(const void *addr)
35 return *(volatile const u32 *)addr;
39 readw(const void *addr)
41 return *(volatile const u16 *)addr;
48 for(i = 0; i < n; i++) {
51 for(j = 0; j < 1000000; j++);
56 r1 = inb(0x61) & 0x10;
58 r2 = inb(0x61) & 0x10;
70 ".globl smp_ap_boot_code_start \n"
71 ".globl smp_ap_boot_code_end \n"
74 "smp_ap_boot_code_start: \n"
77 " //incw CPU_COUNT_ADDR \n"
82 "smp_ap_boot_code_end: \n"
86 extern u8 smp_ap_boot_code_start;
87 extern u8 smp_ap_boot_code_end;
89 /* find the number of CPUs by launching a SIPI to them */
96 if (cpuid_features & CPUID_APIC) {
98 /* enable local APIC */
99 val = readl(APIC_BASE + APIC_SVR);
101 writel(APIC_BASE + APIC_SVR, val);
103 writew((void *)CPU_COUNT_ADDR, 1);
105 /* copy AP boot code */
106 memcpy((void *)AP_BOOT_ADDR, &smp_ap_boot_code_start,
107 &smp_ap_boot_code_end - &smp_ap_boot_code_start);
110 writel(APIC_BASE + APIC_ICR_LOW, 0x000C4500);
111 sipi_vector = AP_BOOT_ADDR >> 12;
112 writel(APIC_BASE + APIC_ICR_LOW, 0x000C4600 | sipi_vector);
116 smp_cpus = readw((void *)CPU_COUNT_ADDR);
119 BX_INFO("Found %d cpu(s)\n", smp_cpus);
122 /****************************************************/
123 /* Multi Processor table init */
126 putb(u8 **pp, int val)
135 putstr(u8 **pp, const char *str)
145 putle16(u8 **pp, int val)
155 putle32(u8 **pp, int val)
167 mpf_checksum(const u8 *data, int len)
172 for(i = 0; i < len; i++)
181 u8 *mp_config_table, *q, *float_pointer_struct;
182 int ioapic_id, i, len;
183 int mp_config_table_size;
190 #ifdef CONFIG_USE_EBDA_TABLES
191 mp_config_table = (u8 *)(ram_size - ACPI_DATA_SIZE - MPTABLE_MAX_SIZE);
193 bios_table_cur_addr = align(bios_table_cur_addr, 16);
194 mp_config_table = (u8 *)bios_table_cur_addr;
197 putstr(&q, "PCMP"); /* "PCMP signature */
198 putle16(&q, 0); /* table length (patched later) */
199 putb(&q, 4); /* spec rev */
200 putb(&q, 0); /* checksum (patched later) */
202 putstr(&q, "QEMUCPU "); /* OEM id */
204 putstr(&q, "BOCHSCPU");
206 putstr(&q, "0.1 "); /* vendor id */
207 putle32(&q, 0); /* OEM table ptr */
208 putle16(&q, 0); /* OEM table size */
209 putle16(&q, smp_cpus + 18); /* entry count */
210 putle32(&q, 0xfee00000); /* local APIC addr */
211 putle16(&q, 0); /* ext table length */
212 putb(&q, 0); /* ext table checksum */
213 putb(&q, 0); /* reserved */
215 for(i = 0; i < smp_cpus; i++) {
216 putb(&q, 0); /* entry type = processor */
217 putb(&q, i); /* APIC id */
218 putb(&q, 0x11); /* local APIC version number */
220 putb(&q, 3); /* cpu flags: enabled, bootstrap cpu */
222 putb(&q, 1); /* cpu flags: enabled */
223 putb(&q, 0); /* cpu signature */
227 putle16(&q, 0x201); /* feature flags */
230 putle16(&q, 0); /* reserved */
237 putb(&q, 1); /* entry type = bus */
238 putb(&q, 0); /* bus ID */
242 ioapic_id = smp_cpus;
243 putb(&q, 2); /* entry type = I/O APIC */
244 putb(&q, ioapic_id); /* apic ID */
245 putb(&q, 0x11); /* I/O APIC version number */
246 putb(&q, 1); /* enable */
247 putle32(&q, 0xfec00000); /* I/O APIC addr */
250 for (i = 0; i < 16; i++) {
251 putb(&q, 3); /* entry type = I/O interrupt */
252 putb(&q, 0); /* interrupt type = vectored interrupt */
253 putb(&q, 0); /* flags: po=0, el=0 */
255 putb(&q, 0); /* source bus ID = ISA */
256 putb(&q, i); /* source bus IRQ */
257 putb(&q, ioapic_id); /* dest I/O APIC ID */
258 putb(&q, i); /* dest I/O APIC interrupt in */
261 len = q - mp_config_table;
262 mp_config_table[4] = len;
263 mp_config_table[5] = len >> 8;
264 mp_config_table[7] = -mpf_checksum(mp_config_table, q - mp_config_table);
266 mp_config_table_size = q - mp_config_table;
268 #ifndef CONFIG_USE_EBDA_TABLES
269 bios_table_cur_addr += mp_config_table_size;
272 /* floating pointer structure */
273 #ifdef CONFIG_USE_EBDA_TABLES
274 ebda_cur_addr = align(ebda_cur_addr, 16);
275 float_pointer_struct = (u8 *)ebda_cur_addr;
277 bios_table_cur_addr = align(bios_table_cur_addr, 16);
278 float_pointer_struct = (u8 *)bios_table_cur_addr;
281 q = float_pointer_struct;
283 /* pointer to MP config table */
284 putle32(&q, (unsigned long)mp_config_table);
286 putb(&q, 1); /* length in 16 byte units */
287 putb(&q, 4); /* MP spec revision */
288 putb(&q, 0); /* checksum (patched later) */
289 putb(&q, 0); /* MP feature byte 1 */
295 float_pointer_struct[10] =
296 -mpf_checksum(float_pointer_struct, q - float_pointer_struct);
298 #ifdef CONFIG_USE_EBDA_TABLES
299 ebda_cur_addr += (q - float_pointer_struct);
301 bios_table_cur_addr += (q - float_pointer_struct);
304 BX_INFO("MP table addr=0x%08lx MPC table addr=0x%08lx size=0x%x\n",
305 (unsigned long)float_pointer_struct,
306 (unsigned long)mp_config_table,
307 mp_config_table_size);