62c666566e
- kernel detects CPUs by searching ACPI tables for local apic nodes - each CPU has its own TSS that points to its own stack. All cpus boot on the same boot stack (in sequence) but switch to its private stack as soon as they can. - final booting code in main() placed in bsp_finish_booting() which is executed only after the BSP switches to its final stack - apic functions to send startup interrupts - assembler functions to handle CPU features not needed for single cpu mode like memory barries, HT detection etc. - new files kernel/smp.[ch], kernel/arch/i386/arch_smp.c and kernel/arch/i386/include/arch_smp.h - 16-bit trampoline code for the APs. It is executed by each AP after receiving startup IPIs it brings up the CPUs to 32bit mode and let them spin in an infinite loop so they don't do any damage. - implementation of kernel spinlock - CONFIG_SMP and CONFIG_MAX_CPUS set by the build system
239 lines
5 KiB
C
239 lines
5 KiB
C
/* This file contains essentially the MP handling code of the Minix kernel.
|
|
*
|
|
* Changes:
|
|
* Apr 1, 2008 Added SMP support.
|
|
*/
|
|
|
|
#define _SMP
|
|
|
|
#include "kernel/kernel.h"
|
|
#include "kernel/proc.h"
|
|
#include "arch_proto.h"
|
|
#include "kernel/glo.h"
|
|
#include <unistd.h>
|
|
#include <machine/cmos.h>
|
|
#include <machine/bios.h>
|
|
#include <minix/portio.h>
|
|
|
|
#include "kernel/spinlock.h"
|
|
#include "kernel/smp.h"
|
|
#include "apic.h"
|
|
#include "acpi.h"
|
|
|
|
#include "glo.h"
|
|
|
|
_PROTOTYPE(void trampoline, (void));
|
|
|
|
/*
|
|
* arguments for trampoline. We need to pass the logical cpu id, gdt and idt.
|
|
* They have to be in location which is reachable using absolute addressing in
|
|
* 16-bit mode
|
|
*/
|
|
extern volatile u32_t __ap_id;
|
|
extern volatile struct segdesc_s __ap_gdt, __ap_idt;
|
|
|
|
extern u32_t busclock[CONFIG_MAX_CPUS];
|
|
extern int panicking;
|
|
|
|
static int ap_cpu_ready;
|
|
|
|
/* there can be at most 255 local APIC ids, each fits in 8 bits */
|
|
PRIVATE unsigned char apicid2cpuid[255];
|
|
PUBLIC unsigned char cpuid2apicid[CONFIG_MAX_CPUS];
|
|
|
|
SPINLOCK_DEFINE(smp_cpu_lock)
|
|
SPINLOCK_DEFINE(dispq_lock)
|
|
|
|
FORWARD _PROTOTYPE(void smp_init_vars, (void));
|
|
FORWARD _PROTOTYPE(void smp_reinit_vars, (void));
|
|
|
|
PRIVATE void smp_start_aps(void)
|
|
{
|
|
/*
|
|
* Find an address and align it to a 4k boundary.
|
|
*/
|
|
unsigned cpu;
|
|
u32_t biosresetvector;
|
|
phys_bytes trampoline_base = vir2phys(trampoline);
|
|
|
|
/* TODO hack around the alignment problem */
|
|
|
|
phys_copy (0x467, vir2phys(&biosresetvector), sizeof(u32_t));
|
|
|
|
/* set the bios shutdown code to 0xA */
|
|
outb(RTC_INDEX, 0xF);
|
|
outb(RTC_IO, 0xA);
|
|
|
|
/* setup the warm reset vector */
|
|
phys_copy(vir2phys(&trampoline_base), 0x467, sizeof(u32_t));
|
|
|
|
/* prepare gdt and idt for the new cpus */
|
|
__ap_gdt = gdt[GDT_INDEX];
|
|
__ap_idt = gdt[IDT_INDEX];
|
|
|
|
/* okay, we're ready to go. boot all of the ap's now. we loop through
|
|
* using the processor's apic id values.
|
|
*/
|
|
for (cpu = 0; cpu < ncpus; cpu++) {
|
|
printf("Booting cpu %d\n", cpu);
|
|
ap_cpu_ready = -1;
|
|
/* Don't send INIT/SIPI to boot cpu. */
|
|
if((apicid() == cpuid2apicid[cpu]) &&
|
|
(apicid() == bsp_lapic_id)) {
|
|
cpu_set_flag(cpu, CPU_IS_READY);
|
|
printf("Skiping bsp\n");
|
|
continue;
|
|
}
|
|
|
|
__ap_id = cpu;
|
|
if (apic_send_init_ipi(cpu, trampoline_base) ||
|
|
apic_send_startup_ipi(cpu, trampoline_base)) {
|
|
printf("WARNING cannot boot cpu %d\n", cpu);
|
|
continue;
|
|
}
|
|
|
|
/* wait for 5 secs for the processors to boot */
|
|
lapic_set_timer_one_shot(5000000);
|
|
|
|
while (lapic_read(LAPIC_TIMER_CCR)) {
|
|
if (ap_cpu_ready == cpu) {
|
|
printf("CPU %d is up\n", cpu);
|
|
cpu_set_flag(cpu, CPU_IS_READY);
|
|
break;
|
|
}
|
|
}
|
|
if (ap_cpu_ready == -1) {
|
|
printf("WARNING : CPU %d didn't boot\n", cpu);
|
|
}
|
|
}
|
|
|
|
phys_copy(vir2phys(&biosresetvector),(phys_bytes)0x467,sizeof(u32_t));
|
|
|
|
outb(RTC_INDEX, 0xF);
|
|
outb(RTC_IO, 0);
|
|
|
|
bsp_finish_booting();
|
|
NOT_REACHABLE;
|
|
}
|
|
|
|
PUBLIC void smp_halt_cpu (void)
|
|
{
|
|
NOT_IMPLEMENTED;
|
|
}
|
|
|
|
PUBLIC void smp_shutdown_aps (void)
|
|
{
|
|
NOT_IMPLEMENTED;
|
|
}
|
|
|
|
PRIVATE void ap_finish_booting(void)
|
|
{
|
|
unsigned cpu = cpuid;
|
|
|
|
printf("CPU %d says hello world!\n", cpu);
|
|
/* inform the world of our presence. */
|
|
ap_cpu_ready = cpu;
|
|
|
|
while(!i386_paging_enabled)
|
|
arch_pause();
|
|
for(;;);
|
|
|
|
/* finish processor initialisation. */
|
|
lapic_enable(cpu);
|
|
|
|
switch_to_user();
|
|
NOT_REACHABLE;
|
|
}
|
|
|
|
PUBLIC void smp_ap_boot(void)
|
|
{
|
|
switch_k_stack((char *)get_k_stack_top(__ap_id) -
|
|
X86_STACK_TOP_RESERVED, ap_finish_booting);
|
|
}
|
|
|
|
PRIVATE void smp_reinit_vars(void)
|
|
{
|
|
int i;
|
|
lapic_addr = lapic_eoi_addr = 0;
|
|
ioapic_enabled = 0;
|
|
|
|
ncpus = 1;
|
|
}
|
|
|
|
PRIVATE void tss_init_all(void)
|
|
{
|
|
unsigned cpu;
|
|
|
|
for(cpu = 0; cpu < ncpus ; cpu++)
|
|
tss_init(cpu, get_k_stack_top(cpu));
|
|
}
|
|
|
|
PRIVATE int discover_cpus(void)
|
|
{
|
|
struct acpi_madt_lapic * cpu;
|
|
|
|
while (ncpus < CONFIG_MAX_CPUS && (cpu = acpi_get_lapic_next())) {
|
|
apicid2cpuid[cpu->apic_id] = ncpus;
|
|
cpuid2apicid[ncpus] = cpu->apic_id;
|
|
printf("CPU %3d local APIC id %3d\n", ncpus, cpu->apic_id);
|
|
ncpus++;
|
|
}
|
|
|
|
return ncpus;
|
|
}
|
|
|
|
PUBLIC void smp_init (void)
|
|
{
|
|
/* read the MP configuration */
|
|
if (!discover_cpus()) {
|
|
ncpus = 1;
|
|
goto uniproc_fallback;
|
|
}
|
|
|
|
lapic_addr = phys2vir(LOCAL_APIC_DEF_ADDR);
|
|
ioapic_enabled = 0;
|
|
|
|
tss_init_all();
|
|
|
|
/*
|
|
* we still run on the boot stack and we cannot use cpuid as its value
|
|
* wasn't set yet. apicid2cpuid initialized in mps_init()
|
|
*/
|
|
bsp_cpu_id = apicid2cpuid[apicid()];
|
|
|
|
if (!lapic_enable(bsp_cpu_id)) {
|
|
printf("ERROR : failed to initialize BSP Local APIC\n");
|
|
goto uniproc_fallback;
|
|
}
|
|
|
|
acpi_init();
|
|
|
|
if (!detect_ioapics()) {
|
|
lapic_disable();
|
|
lapic_addr = 0x0;
|
|
goto uniproc_fallback;
|
|
}
|
|
|
|
ioapic_enable_all();
|
|
|
|
if (ioapic_enabled)
|
|
machine.apic_enabled = 1;
|
|
|
|
/* set smp idt entries. */
|
|
apic_idt_init(0); /* Not a reset ! */
|
|
idt_reload();
|
|
|
|
BOOT_VERBOSE(printf("SMP initialized\n"));
|
|
|
|
switch_k_stack((char *)get_k_stack_top(bsp_cpu_id) -
|
|
X86_STACK_TOP_RESERVED, smp_start_aps);
|
|
|
|
return;
|
|
|
|
uniproc_fallback:
|
|
apic_idt_init(1); /* Reset to PIC idt ! */
|
|
idt_reload();
|
|
smp_reinit_vars (); /* revert to a single proc system. */
|
|
intr_init (INTS_MINIX, 0); /* no auto eoi */
|
|
printf("WARNING : SMP initialization failed\n");
|
|
}
|