Changeset View
Changeset View
Standalone View
Standalone View
sys/i386/i386/mp_machdep.c
Show First 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | |||||
#include <machine/md_var.h> | #include <machine/md_var.h> | ||||
#include <machine/pcb.h> | #include <machine/pcb.h> | ||||
#include <machine/psl.h> | #include <machine/psl.h> | ||||
#include <machine/smp.h> | #include <machine/smp.h> | ||||
#include <machine/specialreg.h> | #include <machine/specialreg.h> | ||||
#include <machine/cpu.h> | #include <machine/cpu.h> | ||||
#define WARMBOOT_TARGET 0 | #define WARMBOOT_TARGET 0 | ||||
#define WARMBOOT_OFF (KERNBASE + 0x0467) | #define WARMBOOT_OFF (PMAP_MAP_LOW + 0x0467) | ||||
#define WARMBOOT_SEG (KERNBASE + 0x0469) | #define WARMBOOT_SEG (PMAP_MAP_LOW + 0x0469) | ||||
#define CMOS_REG (0x70) | #define CMOS_REG (0x70) | ||||
#define CMOS_DATA (0x71) | #define CMOS_DATA (0x71) | ||||
#define BIOS_RESET (0x0f) | #define BIOS_RESET (0x0f) | ||||
#define BIOS_WARM (0x0a) | #define BIOS_WARM (0x0a) | ||||
/* | /* | ||||
* this code MUST be enabled here and in mpboot.s. | * this code MUST be enabled here and in mpboot.s. | ||||
Show All 39 Lines | |||||
* Local data and functions. | * Local data and functions. | ||||
*/ | */ | ||||
static void install_ap_tramp(void); | static void install_ap_tramp(void); | ||||
static int start_all_aps(void); | static int start_all_aps(void); | ||||
static int start_ap(int apic_id); | static int start_ap(int apic_id); | ||||
static u_int boot_address; | static u_int boot_address; | ||||
static char *ap_tramp_stack_base; | |||||
/* | /* | ||||
* Calculate usable address in base memory for AP trampoline code. | * Calculate usable address in base memory for AP trampoline code. | ||||
*/ | */ | ||||
u_int | u_int | ||||
mp_bootaddress(u_int basemem) | mp_bootaddress(u_int basemem) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* AP CPU's call this to initialize themselves. | * AP CPU's call this to initialize themselves. | ||||
*/ | */ | ||||
void | void | ||||
init_secondary(void) | init_secondary(void) | ||||
{ | { | ||||
struct pcpu *pc; | struct pcpu *pc; | ||||
vm_offset_t addr; | struct i386tss *common_tssp; | ||||
int gsel_tss; | struct region_descriptor r_gdt, r_idt; | ||||
int x, myid; | int gsel_tss, myid, x; | ||||
u_int cr0; | u_int cr0; | ||||
/* bootAP is set in start_ap() to our ID. */ | /* bootAP is set in start_ap() to our ID. */ | ||||
myid = bootAP; | myid = bootAP; | ||||
/* Get per-cpu data */ | /* Get per-cpu data */ | ||||
pc = &__pcpu[myid]; | pc = &__pcpu[myid]; | ||||
/* prime data page for it to use */ | /* prime data page for it to use */ | ||||
pcpu_init(pc, myid, sizeof(struct pcpu)); | pcpu_init(pc, myid, sizeof(struct pcpu)); | ||||
dpcpu_init(dpcpu, myid); | dpcpu_init(dpcpu, myid); | ||||
pc->pc_apic_id = cpu_apic_ids[myid]; | pc->pc_apic_id = cpu_apic_ids[myid]; | ||||
pc->pc_prvspace = pc; | pc->pc_prvspace = pc; | ||||
pc->pc_curthread = 0; | pc->pc_curthread = 0; | ||||
pc->pc_common_tssp = common_tssp = &(__pcpu[0].pc_common_tssp)[myid]; | |||||
fix_cpuid(); | fix_cpuid(); | ||||
gdt_segs[GPRIV_SEL].ssd_base = (int) pc; | gdt_segs[GPRIV_SEL].ssd_base = (int)pc; | ||||
gdt_segs[GPROC0_SEL].ssd_base = (int) &pc->pc_common_tss; | gdt_segs[GPROC0_SEL].ssd_base = (int)common_tssp; | ||||
gdt_segs[GLDT_SEL].ssd_base = (int)ldt; | |||||
for (x = 0; x < NGDT; x++) { | for (x = 0; x < NGDT; x++) { | ||||
ssdtosd(&gdt_segs[x], &gdt[myid * NGDT + x].sd); | ssdtosd(&gdt_segs[x], &gdt[myid * NGDT + x].sd); | ||||
} | } | ||||
r_gdt.rd_limit = NGDT * sizeof(gdt[0]) - 1; | r_gdt.rd_limit = NGDT * sizeof(gdt[0]) - 1; | ||||
r_gdt.rd_base = (int) &gdt[myid * NGDT]; | r_gdt.rd_base = (int) &gdt[myid * NGDT]; | ||||
lgdt(&r_gdt); /* does magic intra-segment return */ | lgdt(&r_gdt); /* does magic intra-segment return */ | ||||
r_idt.rd_limit = sizeof(struct gate_descriptor) * NIDT - 1; | |||||
r_idt.rd_base = (int)idt; | |||||
lidt(&r_idt); | lidt(&r_idt); | ||||
lldt(_default_ldt); | lldt(_default_ldt); | ||||
PCPU_SET(currentldt, _default_ldt); | PCPU_SET(currentldt, _default_ldt); | ||||
PCPU_SET(trampstk, (uintptr_t)ap_tramp_stack_base + TRAMP_STACK_SZ - | |||||
VM86_STACK_SPACE); | |||||
gsel_tss = GSEL(GPROC0_SEL, SEL_KPL); | gsel_tss = GSEL(GPROC0_SEL, SEL_KPL); | ||||
gdt[myid * NGDT + GPROC0_SEL].sd.sd_type = SDT_SYS386TSS; | gdt[myid * NGDT + GPROC0_SEL].sd.sd_type = SDT_SYS386TSS; | ||||
PCPU_SET(common_tss.tss_esp0, 0); /* not used until after switch */ | common_tssp->tss_esp0 = PCPU_GET(trampstk); | ||||
PCPU_SET(common_tss.tss_ss0, GSEL(GDATA_SEL, SEL_KPL)); | common_tssp->tss_ss0 = GSEL(GDATA_SEL, SEL_KPL); | ||||
PCPU_SET(common_tss.tss_ioopt, (sizeof (struct i386tss)) << 16); | common_tssp->tss_ioopt = sizeof(struct i386tss) << 16; | ||||
PCPU_SET(tss_gdt, &gdt[myid * NGDT + GPROC0_SEL].sd); | PCPU_SET(tss_gdt, &gdt[myid * NGDT + GPROC0_SEL].sd); | ||||
PCPU_SET(common_tssd, *PCPU_GET(tss_gdt)); | PCPU_SET(common_tssd, *PCPU_GET(tss_gdt)); | ||||
ltr(gsel_tss); | ltr(gsel_tss); | ||||
PCPU_SET(fsgs_gdt, &gdt[myid * NGDT + GUFS_SEL].sd); | PCPU_SET(fsgs_gdt, &gdt[myid * NGDT + GUFS_SEL].sd); | ||||
/* | /* | ||||
* Set to a known state: | * Set to a known state: | ||||
Show All 10 Lines | init_secondary(void) | ||||
CHECK_WRITE(0x39, 6); | CHECK_WRITE(0x39, 6); | ||||
/* Spin until the BSP releases the AP's. */ | /* Spin until the BSP releases the AP's. */ | ||||
while (atomic_load_acq_int(&aps_ready) == 0) | while (atomic_load_acq_int(&aps_ready) == 0) | ||||
ia32_pause(); | ia32_pause(); | ||||
/* BSP may have changed PTD while we were waiting */ | /* BSP may have changed PTD while we were waiting */ | ||||
invltlb(); | invltlb(); | ||||
for (addr = 0; addr < NKPT * NBPDR - 1; addr += PAGE_SIZE) | |||||
invlpg(addr); | |||||
#if defined(I586_CPU) && !defined(NO_F00F_HACK) | #if defined(I586_CPU) && !defined(NO_F00F_HACK) | ||||
lidt(&r_idt); | lidt(&r_idt); | ||||
#endif | #endif | ||||
init_secondary_tail(); | init_secondary_tail(); | ||||
} | } | ||||
/* | /* | ||||
* start each AP in our list | * start each AP in our list | ||||
*/ | */ | ||||
/* Lowest 1MB is already mapped: don't touch*/ | |||||
#define TMPMAP_START 1 | #define TMPMAP_START 1 | ||||
static int | static int | ||||
start_all_aps(void) | start_all_aps(void) | ||||
{ | { | ||||
u_char mpbiosreason; | u_char mpbiosreason; | ||||
u_int32_t mpbioswarmvec; | u_int32_t mpbioswarmvec; | ||||
int apic_id, cpu, i; | int apic_id, cpu; | ||||
mtx_init(&ap_boot_mtx, "ap boot", NULL, MTX_SPIN); | mtx_init(&ap_boot_mtx, "ap boot", NULL, MTX_SPIN); | ||||
/* Remap lowest 1MB */ | |||||
IdlePTD[0] = IdlePTD[1]; | |||||
load_cr3(rcr3()); /* invalidate TLB */ | |||||
/* install the AP 1st level boot code */ | /* install the AP 1st level boot code */ | ||||
install_ap_tramp(); | install_ap_tramp(); | ||||
/* save the current value of the warm-start vector */ | /* save the current value of the warm-start vector */ | ||||
mpbioswarmvec = *((u_int32_t *) WARMBOOT_OFF); | mpbioswarmvec = *((u_int32_t *) WARMBOOT_OFF); | ||||
outb(CMOS_REG, BIOS_RESET); | outb(CMOS_REG, BIOS_RESET); | ||||
mpbiosreason = inb(CMOS_DATA); | mpbiosreason = inb(CMOS_DATA); | ||||
/* set up temporary P==V mapping for AP boot */ | /* take advantage of the P==V mapping for PTD[0] for AP boot */ | ||||
/* XXX this is a hack, we should boot the AP on its own stack/PTD */ | |||||
for (i = TMPMAP_START; i < NKPT; i++) | |||||
PTD[i] = PTD[KPTDI + i]; | |||||
invltlb(); | |||||
/* start each AP */ | /* start each AP */ | ||||
for (cpu = 1; cpu < mp_ncpus; cpu++) { | for (cpu = 1; cpu < mp_ncpus; cpu++) { | ||||
apic_id = cpu_apic_ids[cpu]; | apic_id = cpu_apic_ids[cpu]; | ||||
/* allocate and set up a boot stack data page */ | /* allocate and set up a boot stack data page */ | ||||
bootstacks[cpu] = | bootstacks[cpu] = | ||||
(char *)kmem_malloc(kernel_arena, kstack_pages * PAGE_SIZE, | (char *)kmem_malloc(kernel_arena, kstack_pages * PAGE_SIZE, | ||||
M_WAITOK | M_ZERO); | M_WAITOK | M_ZERO); | ||||
dpcpu = (void *)kmem_malloc(kernel_arena, DPCPU_SIZE, | dpcpu = (void *)kmem_malloc(kernel_arena, DPCPU_SIZE, | ||||
M_WAITOK | M_ZERO); | M_WAITOK | M_ZERO); | ||||
/* setup a vector to our boot code */ | /* setup a vector to our boot code */ | ||||
*((volatile u_short *) WARMBOOT_OFF) = WARMBOOT_TARGET; | *((volatile u_short *) WARMBOOT_OFF) = WARMBOOT_TARGET; | ||||
*((volatile u_short *) WARMBOOT_SEG) = (boot_address >> 4); | *((volatile u_short *) WARMBOOT_SEG) = (boot_address >> 4); | ||||
outb(CMOS_REG, BIOS_RESET); | outb(CMOS_REG, BIOS_RESET); | ||||
outb(CMOS_DATA, BIOS_WARM); /* 'warm-start' */ | outb(CMOS_DATA, BIOS_WARM); /* 'warm-start' */ | ||||
bootSTK = (char *)bootstacks[cpu] + kstack_pages * | bootSTK = (char *)bootstacks[cpu] + kstack_pages * | ||||
PAGE_SIZE - 4; | PAGE_SIZE - 4; | ||||
bootAP = cpu; | bootAP = cpu; | ||||
ap_tramp_stack_base = pmap_trm_alloc(TRAMP_STACK_SZ, M_NOWAIT); | |||||
/* attempt to start the Application Processor */ | /* attempt to start the Application Processor */ | ||||
CHECK_INIT(99); /* setup checkpoints */ | CHECK_INIT(99); /* setup checkpoints */ | ||||
if (!start_ap(apic_id)) { | if (!start_ap(apic_id)) { | ||||
printf("AP #%d (PHY# %d) failed!\n", cpu, apic_id); | printf("AP #%d (PHY# %d) failed!\n", cpu, apic_id); | ||||
CHECK_PRINT("trace"); /* show checkpoints */ | CHECK_PRINT("trace"); /* show checkpoints */ | ||||
/* better panic as the AP may be running loose */ | /* better panic as the AP may be running loose */ | ||||
printf("panic y/n? [y] "); | printf("panic y/n? [y] "); | ||||
if (cngetc() != 'n') | if (cngetc() != 'n') | ||||
panic("bye-bye"); | panic("bye-bye"); | ||||
} | } | ||||
CHECK_PRINT("trace"); /* show checkpoints */ | CHECK_PRINT("trace"); /* show checkpoints */ | ||||
CPU_SET(cpu, &all_cpus); /* record AP in CPU map */ | CPU_SET(cpu, &all_cpus); /* record AP in CPU map */ | ||||
} | } | ||||
/* Unmap lowest 1MB again */ | |||||
IdlePTD[0] = 0; | |||||
load_cr3(rcr3()); | |||||
/* restore the warmstart vector */ | /* restore the warmstart vector */ | ||||
*(u_int32_t *) WARMBOOT_OFF = mpbioswarmvec; | *(u_int32_t *) WARMBOOT_OFF = mpbioswarmvec; | ||||
outb(CMOS_REG, BIOS_RESET); | outb(CMOS_REG, BIOS_RESET); | ||||
outb(CMOS_DATA, mpbiosreason); | outb(CMOS_DATA, mpbiosreason); | ||||
/* Undo V==P hack from above */ | |||||
for (i = TMPMAP_START; i < NKPT; i++) | |||||
PTD[i] = 0; | |||||
pmap_invalidate_range(kernel_pmap, 0, NKPT * NBPDR - 1); | |||||
/* number of APs actually started */ | /* number of APs actually started */ | ||||
return mp_naps; | return mp_naps; | ||||
} | } | ||||
/* | /* | ||||
* load the 1st level AP boot code into base memory. | * load the 1st level AP boot code into base memory. | ||||
*/ | */ | ||||
/* targets for relocation */ | /* targets for relocation */ | ||||
extern void bigJump(void); | extern void bigJump(void); | ||||
extern void bootCodeSeg(void); | extern void bootCodeSeg(void); | ||||
extern void bootDataSeg(void); | extern void bootDataSeg(void); | ||||
extern void MPentry(void); | extern void MPentry(void); | ||||
extern u_int MP_GDT; | extern u_int MP_GDT; | ||||
extern u_int mp_gdtbase; | extern u_int mp_gdtbase; | ||||
static void | static void | ||||
install_ap_tramp(void) | install_ap_tramp(void) | ||||
{ | { | ||||
int x; | int x; | ||||
int size = *(int *) ((u_long) & bootMP_size); | int size = *(int *) ((u_long) & bootMP_size); | ||||
vm_offset_t va = boot_address + KERNBASE; | vm_offset_t va = boot_address; | ||||
u_char *src = (u_char *) ((u_long) bootMP); | u_char *src = (u_char *) ((u_long) bootMP); | ||||
u_char *dst = (u_char *) va; | u_char *dst = (u_char *) va; | ||||
u_int boot_base = (u_int) bootMP; | u_int boot_base = (u_int) bootMP; | ||||
u_int8_t *dst8; | u_int8_t *dst8; | ||||
u_int16_t *dst16; | u_int16_t *dst16; | ||||
u_int32_t *dst32; | u_int32_t *dst32; | ||||
KASSERT (size <= PAGE_SIZE, | KASSERT (size <= PAGE_SIZE, | ||||
Show All 13 Lines | install_ap_tramp(void) | ||||
dst = (u_char *) va; | dst = (u_char *) va; | ||||
/* modify the lgdt arg */ | /* modify the lgdt arg */ | ||||
dst32 = (u_int32_t *) (dst + ((u_int) & mp_gdtbase - boot_base)); | dst32 = (u_int32_t *) (dst + ((u_int) & mp_gdtbase - boot_base)); | ||||
*dst32 = boot_address + ((u_int) & MP_GDT - boot_base); | *dst32 = boot_address + ((u_int) & MP_GDT - boot_base); | ||||
/* modify the ljmp target for MPentry() */ | /* modify the ljmp target for MPentry() */ | ||||
dst32 = (u_int32_t *) (dst + ((u_int) bigJump - boot_base) + 1); | dst32 = (u_int32_t *) (dst + ((u_int) bigJump - boot_base) + 1); | ||||
*dst32 = ((u_int) MPentry - KERNBASE); | *dst32 = (u_int)MPentry; | ||||
/* modify the target for boot code segment */ | /* modify the target for boot code segment */ | ||||
dst16 = (u_int16_t *) (dst + ((u_int) bootCodeSeg - boot_base)); | dst16 = (u_int16_t *) (dst + ((u_int) bootCodeSeg - boot_base)); | ||||
dst8 = (u_int8_t *) (dst16 + 1); | dst8 = (u_int8_t *) (dst16 + 1); | ||||
*dst16 = (u_int) boot_address & 0xffff; | *dst16 = (u_int) boot_address & 0xffff; | ||||
*dst8 = ((u_int) boot_address >> 16) & 0xff; | *dst8 = ((u_int) boot_address >> 16) & 0xff; | ||||
/* modify the target for boot data segment */ | /* modify the target for boot data segment */ | ||||
Show All 35 Lines |