-/*
- * smp.c
- * SMP support for BIOS.
- *
- * Copyright (C) 2008 Nguyen Anh Quynh <aquynh@gmail.com>
- * Copyright (C) 2002 MandrakeSoft S.A.
- *
- * This file may be distributed under the terms of the GNU GPLv3 license.
- */
+// CPU count detection
+//
+// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
+// Copyright (C) 2006 Fabrice Bellard
+//
+// This file may be distributed under the terms of the GNU LGPLv3 license.
-#include "config.h"
-#include "hardware.h"
-#include "smp.h"
-#include "types.h"
-#include "util.h"
-#include "acpi.h" // ACPI_DATA_SIZE
+#include "util.h" // dprintf
+#include "config.h" // CONFIG_*
+#include "cmos.h" // CMOS_BIOS_SMP_COUNT
+#include "paravirt.h"
-int smp_cpus;
+#define APIC_ICR_LOW ((u8*)BUILD_APIC_ADDR + 0x300)
+#define APIC_SVR ((u8*)BUILD_APIC_ADDR + 0x0F0)
+#define APIC_LINT0 ((u8*)BUILD_APIC_ADDR + 0x350)
+#define APIC_LINT1 ((u8*)BUILD_APIC_ADDR + 0x360)
-static inline void
-writel(void *addr, u32 val)
-{
- *(volatile u32 *)addr = val;
-}
+#define APIC_ENABLED 0x0100
-static inline void
-writew(void *addr, u16 val)
-{
- *(volatile u16 *)addr = val;
-}
-
-static inline u32
-readl(const void *addr)
-{
- return *(volatile const u32 *)addr;
-}
+struct { u32 ecx, eax, edx; } smp_mtrr[32] VAR16VISIBLE;
+u32 smp_mtrr_count VAR16VISIBLE;
-static inline u16
-readw(const void *addr)
-{
- return *(volatile const u16 *)addr;
-}
-
-static void
-delay_ms(int n)
+void
+wrmsr_smp(u32 index, u64 val)
{
- int i, j;
- for(i = 0; i < n; i++) {
-#ifdef QEMU_SUPPORT
- /* approximative ! */
- for(j = 0; j < 1000000; j++);
-#else
- {
- int r1, r2;
- j = 66;
- r1 = inb(0x61) & 0x10;
- do {
- r2 = inb(0x61) & 0x10;
- if (r1 != r2) {
- j--;
- r1 = r2;
- }
- } while (j > 0);
- }
-#endif
+ wrmsr(index, val);
+ if (smp_mtrr_count >= ARRAY_SIZE(smp_mtrr)) {
+ warn_noalloc();
+ return;
}
+ smp_mtrr[smp_mtrr_count].ecx = index;
+ smp_mtrr[smp_mtrr_count].eax = val;
+ smp_mtrr[smp_mtrr_count].edx = val >> 32;
+ smp_mtrr_count++;
}
-asm (
- ".globl smp_ap_boot_code_start \n"
- ".globl smp_ap_boot_code_end \n"
- " \n"
- " .code16 \n"
- "smp_ap_boot_code_start: \n"
- " xorw %ax, %ax \n"
- " movw %ax, %ds \n"
- " //incw CPU_COUNT_ADDR \n"
- " incw 0xf000 \n"
- "1: \n"
- " hlt \n"
- " jmp 1b \n"
- "smp_ap_boot_code_end: \n"
- " .code32 \n"
+u32 CountCPUs VAR16VISIBLE;
+u32 MaxCountCPUs VAR16VISIBLE;
+extern void smp_ap_boot_code(void);
+ASM16(
+ " .global smp_ap_boot_code\n"
+ "smp_ap_boot_code:\n"
+
+ // Setup data segment
+ " movw $" __stringify(SEG_BIOS) ", %ax\n"
+ " movw %ax, %ds\n"
+
+ // MTRR setup
+ " movl $smp_mtrr, %esi\n"
+ " movl smp_mtrr_count, %ebx\n"
+ "1:testl %ebx, %ebx\n"
+ " jz 2f\n"
+ " movl 0(%esi), %ecx\n"
+ " movl 4(%esi), %eax\n"
+ " movl 8(%esi), %edx\n"
+ " wrmsr\n"
+ " addl $12, %esi\n"
+ " decl %ebx\n"
+ " jmp 1b\n"
+ "2:\n"
+
+ // Increment the cpu counter
+ " lock incl CountCPUs\n"
+
+ // Halt the processor.
+ "1:hlt\n"
+ " jmp 1b\n"
);
-extern u8 smp_ap_boot_code_start;
-extern u8 smp_ap_boot_code_end;
-
-/* find the number of CPUs by launching a SIPI to them */
+// find and initialize the CPUs by launching a SIPI to them
void
smp_probe(void)
{
- u32 val, sipi_vector;
-
- smp_cpus = 1;
- if (cpuid_features & CPUID_APIC) {
-
- /* enable local APIC */
- val = readl(APIC_BASE + APIC_SVR);
- val |= APIC_ENABLED;
- writel(APIC_BASE + APIC_SVR, val);
-
- writew((void *)CPU_COUNT_ADDR, 1);
-
- /* copy AP boot code */
- memcpy((void *)AP_BOOT_ADDR, &smp_ap_boot_code_start,
- &smp_ap_boot_code_end - &smp_ap_boot_code_start);
-
- /* broadcast SIPI */
- writel(APIC_BASE + APIC_ICR_LOW, 0x000C4500);
- sipi_vector = AP_BOOT_ADDR >> 12;
- writel(APIC_BASE + APIC_ICR_LOW, 0x000C4600 | sipi_vector);
-
- delay_ms(10);
-
- smp_cpus = readw((void *)CPU_COUNT_ADDR);
+ ASSERT32FLAT();
+ u32 eax, ebx, ecx, cpuid_features;
+ cpuid(1, &eax, &ebx, &ecx, &cpuid_features);
+ if (eax < 1 || !(cpuid_features & CPUID_APIC)) {
+ // No apic - only the main cpu is present.
+ dprintf(1, "No apic - only the main cpu is present.\n");
+ CountCPUs= 1;
+ MaxCountCPUs = 1;
+ return;
}
- BX_INFO("Found %d cpu(s)\n", smp_cpus);
-}
+ // Init the counter.
+ writel(&CountCPUs, 1);
-/****************************************************/
-/* Multi Processor table init */
+ // Setup jump trampoline to counter code.
+ u64 old = *(u64*)BUILD_AP_BOOT_ADDR;
+ // ljmpw $SEG_BIOS, $(smp_ap_boot_code - BUILD_BIOS_ADDR)
+ u64 new = (0xea | ((u64)SEG_BIOS<<24)
+ | (((u32)smp_ap_boot_code - BUILD_BIOS_ADDR) << 8));
+ *(u64*)BUILD_AP_BOOT_ADDR = new;
-static void
-putb(u8 **pp, int val)
-{
- u8 *q;
- q = *pp;
- *q++ = val;
- *pp = q;
-}
+ // enable local APIC
+ u32 val = readl(APIC_SVR);
+ writel(APIC_SVR, val | APIC_ENABLED);
-static void
-putstr(u8 **pp, const char *str)
-{
- u8 *q;
- q = *pp;
- while (*str)
- *q++ = *str++;
- *pp = q;
-}
+ if (! CONFIG_COREBOOT) {
+ /* Set LINT0 as Ext_INT, level triggered */
+ writel(APIC_LINT0, 0x8700);
-static void
-putle16(u8 **pp, int val)
-{
- u8 *q;
- q = *pp;
- *q++ = val;
- *q++ = val >> 8;
- *pp = q;
-}
-
-static void
-putle32(u8 **pp, int val)
-{
- u8 *q;
- q = *pp;
- *q++ = val;
- *q++ = val >> 8;
- *q++ = val >> 16;
- *q++ = val >> 24;
- *pp = q;
-}
-
-static int
-mpf_checksum(const u8 *data, int len)
-{
- int sum, i;
-
- sum = 0;
- for(i = 0; i < len; i++)
- sum += data[i];
-
- return sum & 0xff;
-}
-
-void
-mptable_init(void)
-{
- u8 *mp_config_table, *q, *float_pointer_struct;
- int ioapic_id, i, len;
- int mp_config_table_size;
-
-#ifdef QEMU_SUPPORT
- if (smp_cpus <= 1)
- return;
-#endif
-
-#ifdef CONFIG_USE_EBDA_TABLES
- mp_config_table = (u8 *)(ram_size - ACPI_DATA_SIZE - MPTABLE_MAX_SIZE);
-#else
- bios_table_cur_addr = align(bios_table_cur_addr, 16);
- mp_config_table = (u8 *)bios_table_cur_addr;
-#endif
- q = mp_config_table;
- putstr(&q, "PCMP"); /* "PCMP signature */
- putle16(&q, 0); /* table length (patched later) */
- putb(&q, 4); /* spec rev */
- putb(&q, 0); /* checksum (patched later) */
-#ifdef QEMU_SUPPORT
- putstr(&q, "QEMUCPU "); /* OEM id */
-#else
- putstr(&q, "BOCHSCPU");
-#endif
- putstr(&q, "0.1 "); /* vendor id */
- putle32(&q, 0); /* OEM table ptr */
- putle16(&q, 0); /* OEM table size */
- putle16(&q, smp_cpus + 18); /* entry count */
- putle32(&q, 0xfee00000); /* local APIC addr */
- putle16(&q, 0); /* ext table length */
- putb(&q, 0); /* ext table checksum */
- putb(&q, 0); /* reserved */
-
- for(i = 0; i < smp_cpus; i++) {
- putb(&q, 0); /* entry type = processor */
- putb(&q, i); /* APIC id */
- putb(&q, 0x11); /* local APIC version number */
- if (i == 0)
- putb(&q, 3); /* cpu flags: enabled, bootstrap cpu */
- else
- putb(&q, 1); /* cpu flags: enabled */
- putb(&q, 0); /* cpu signature */
- putb(&q, 6);
- putb(&q, 0);
- putb(&q, 0);
- putle16(&q, 0x201); /* feature flags */
- putle16(&q, 0);
-
- putle16(&q, 0); /* reserved */
- putle16(&q, 0);
- putle16(&q, 0);
- putle16(&q, 0);
+ /* Set LINT1 as NMI, level triggered */
+ writel(APIC_LINT1, 0x8400);
}
- /* isa bus */
- putb(&q, 1); /* entry type = bus */
- putb(&q, 0); /* bus ID */
- putstr(&q, "ISA ");
-
- /* ioapic */
- ioapic_id = smp_cpus;
- putb(&q, 2); /* entry type = I/O APIC */
- putb(&q, ioapic_id); /* apic ID */
- putb(&q, 0x11); /* I/O APIC version number */
- putb(&q, 1); /* enable */
- putle32(&q, 0xfec00000); /* I/O APIC addr */
-
- /* irqs */
- for (i = 0; i < 16; i++) {
- putb(&q, 3); /* entry type = I/O interrupt */
- putb(&q, 0); /* interrupt type = vectored interrupt */
- putb(&q, 0); /* flags: po=0, el=0 */
- putb(&q, 0);
- putb(&q, 0); /* source bus ID = ISA */
- putb(&q, i); /* source bus IRQ */
- putb(&q, ioapic_id); /* dest I/O APIC ID */
- putb(&q, i); /* dest I/O APIC interrupt in */
+ // broadcast SIPI
+ barrier();
+ writel(APIC_ICR_LOW, 0x000C4500);
+ u32 sipi_vector = BUILD_AP_BOOT_ADDR >> 12;
+ writel(APIC_ICR_LOW, 0x000C4600 | sipi_vector);
+
+ // Wait for other CPUs to process the SIPI.
+ if (CONFIG_COREBOOT) {
+ msleep(10);
+ } else {
+ u8 cmos_smp_count = inb_cmos(CMOS_BIOS_SMP_COUNT);
+ while (cmos_smp_count + 1 != readl(&CountCPUs))
+ yield();
}
- /* patch length */
- len = q - mp_config_table;
- mp_config_table[4] = len;
- mp_config_table[5] = len >> 8;
- mp_config_table[7] = -mpf_checksum(mp_config_table, q - mp_config_table);
-
- mp_config_table_size = q - mp_config_table;
-
-#ifndef CONFIG_USE_EBDA_TABLES
- bios_table_cur_addr += mp_config_table_size;
-#endif
-
- /* floating pointer structure */
-#ifdef CONFIG_USE_EBDA_TABLES
- ebda_cur_addr = align(ebda_cur_addr, 16);
- float_pointer_struct = (u8 *)ebda_cur_addr;
-#else
- bios_table_cur_addr = align(bios_table_cur_addr, 16);
- float_pointer_struct = (u8 *)bios_table_cur_addr;
-#endif
-
- q = float_pointer_struct;
- putstr(&q, "_MP_");
- /* pointer to MP config table */
- putle32(&q, (unsigned long)mp_config_table);
-
- putb(&q, 1); /* length in 16 byte units */
- putb(&q, 4); /* MP spec revision */
- putb(&q, 0); /* checksum (patched later) */
- putb(&q, 0); /* MP feature byte 1 */
- putb(&q, 0);
- putb(&q, 0);
- putb(&q, 0);
- putb(&q, 0);
- float_pointer_struct[10] =
- -mpf_checksum(float_pointer_struct, q - float_pointer_struct);
+ // Restore memory.
+ *(u64*)BUILD_AP_BOOT_ADDR = old;
-#ifdef CONFIG_USE_EBDA_TABLES
- ebda_cur_addr += (q - float_pointer_struct);
-#else
- bios_table_cur_addr += (q - float_pointer_struct);
-#endif
+ MaxCountCPUs = qemu_cfg_get_max_cpus();
+ if (!MaxCountCPUs || MaxCountCPUs < CountCPUs)
+ MaxCountCPUs = CountCPUs;
- BX_INFO("MP table addr=0x%08lx MPC table addr=0x%08lx size=0x%x\n",
- (unsigned long)float_pointer_struct,
- (unsigned long)mp_config_table,
- mp_config_table_size);
+ dprintf(1, "Found %d cpu(s) max supported %d cpu(s)\n", readl(&CountCPUs),
+ MaxCountCPUs);
}