jos/kern/mpconfig.c

226 lines
6.2 KiB
C

// Search for and parse the multiprocessor configuration table
// See http://developer.intel.com/design/pentium/datashts/24201606.pdf
#include <inc/types.h>
#include <inc/string.h>
#include <inc/memlayout.h>
#include <inc/x86.h>
#include <inc/mmu.h>
#include <inc/env.h>
#include <kern/cpu.h>
#include <kern/pmap.h>
struct CpuInfo cpus[NCPU];
struct CpuInfo *bootcpu;
int ismp;
int ncpu;
// Per-CPU kernel stacks
unsigned char percpu_kstacks[NCPU][KSTKSIZE]
__attribute__ ((aligned(PGSIZE)));
// See MultiProcessor Specification Version 1.[14]
struct mp { // floating pointer [MP 4.1]
uint8_t signature[4]; // "_MP_"
physaddr_t physaddr; // phys addr of MP config table
uint8_t length; // 1
uint8_t specrev; // [14]
uint8_t checksum; // all bytes must add up to 0
uint8_t type; // MP system config type
uint8_t imcrp;
uint8_t reserved[3];
} __attribute__((__packed__));
struct mpconf { // configuration table header [MP 4.2]
uint8_t signature[4]; // "PCMP"
uint16_t length; // total table length
uint8_t version; // [14]
uint8_t checksum; // all bytes must add up to 0
uint8_t product[20]; // product id
physaddr_t oemtable; // OEM table pointer
uint16_t oemlength; // OEM table length
uint16_t entry; // entry count
physaddr_t lapicaddr; // address of local APIC
uint16_t xlength; // extended table length
uint8_t xchecksum; // extended table checksum
uint8_t reserved;
uint8_t entries[0]; // table entries
} __attribute__((__packed__));
struct mpproc { // processor table entry [MP 4.3.1]
uint8_t type; // entry type (0)
uint8_t apicid; // local APIC id
uint8_t version; // local APIC version
uint8_t flags; // CPU flags
uint8_t signature[4]; // CPU signature
uint32_t feature; // feature flags from CPUID instruction
uint8_t reserved[8];
} __attribute__((__packed__));
// mpproc flags
#define MPPROC_BOOT 0x02 // This mpproc is the bootstrap processor
// Table entry types
#define MPPROC 0x00 // One per processor
#define MPBUS 0x01 // One per bus
#define MPIOAPIC 0x02 // One per I/O APIC
#define MPIOINTR 0x03 // One per bus interrupt source
#define MPLINTR 0x04 // One per system interrupt source
static uint8_t
sum(void *addr, int len)
{
int i, sum;
sum = 0;
for (i = 0; i < len; i++)
sum += ((uint8_t *)addr)[i];
return sum;
}
// Look for an MP structure in the len bytes at physical address addr.
static struct mp *
mpsearch1(physaddr_t a, int len)
{
struct mp *mp = KADDR(a), *end = KADDR(a + len);
for (; mp < end; mp++)
if (memcmp(mp->signature, "_MP_", 4) == 0 &&
sum(mp, sizeof(*mp)) == 0)
return mp;
return NULL;
}
// Search for the MP Floating Pointer Structure, which according to
// [MP 4] is in one of the following three locations:
// 1) in the first KB of the EBDA;
// 2) if there is no EBDA, in the last KB of system base memory;
// 3) in the BIOS ROM between 0xE0000 and 0xFFFFF.
static struct mp *
mpsearch(void)
{
uint8_t *bda;
uint32_t p;
struct mp *mp;
static_assert(sizeof(*mp) == 16);
// The BIOS data area lives in 16-bit segment 0x40.
bda = (uint8_t *) KADDR(0x40 << 4);
// [MP 4] The 16-bit segment of the EBDA is in the two bytes
// starting at byte 0x0E of the BDA. 0 if not present.
if ((p = *(uint16_t *) (bda + 0x0E))) {
p <<= 4; // Translate from segment to PA
if ((mp = mpsearch1(p, 1024)))
return mp;
} else {
// The size of base memory, in KB is in the two bytes
// starting at 0x13 of the BDA.
p = *(uint16_t *) (bda + 0x13) * 1024;
if ((mp = mpsearch1(p - 1024, 1024)))
return mp;
}
return mpsearch1(0xF0000, 0x10000);
}
// Search for an MP configuration table. For now, don't accept the
// default configurations (physaddr == 0).
// Check for the correct signature, checksum, and version.
static struct mpconf *
mpconfig(struct mp **pmp)
{
struct mpconf *conf;
struct mp *mp;
if ((mp = mpsearch()) == 0)
return NULL;
if (mp->physaddr == 0 || mp->type != 0) {
cprintf("SMP: Default configurations not implemented\n");
return NULL;
}
conf = (struct mpconf *) KADDR(mp->physaddr);
if (memcmp(conf, "PCMP", 4) != 0) {
cprintf("SMP: Incorrect MP configuration table signature\n");
return NULL;
}
if (sum(conf, conf->length) != 0) {
cprintf("SMP: Bad MP configuration checksum\n");
return NULL;
}
if (conf->version != 1 && conf->version != 4) {
cprintf("SMP: Unsupported MP version %d\n", conf->version);
return NULL;
}
if ((sum((uint8_t *)conf + conf->length, conf->xlength) + conf->xchecksum) & 0xff) {
cprintf("SMP: Bad MP configuration extended checksum\n");
return NULL;
}
*pmp = mp;
return conf;
}
void
mp_init(void)
{
struct mp *mp;
struct mpconf *conf;
struct mpproc *proc;
uint8_t *p;
unsigned int i;
bootcpu = &cpus[0];
if ((conf = mpconfig(&mp)) == 0)
return;
ismp = 1;
lapicaddr = conf->lapicaddr;
for (p = conf->entries, i = 0; i < conf->entry; i++) {
switch (*p) {
case MPPROC:
proc = (struct mpproc *)p;
if (proc->flags & MPPROC_BOOT)
bootcpu = &cpus[ncpu];
if (ncpu < NCPU) {
cpus[ncpu].cpu_id = ncpu;
ncpu++;
} else {
cprintf("SMP: too many CPUs, CPU %d disabled\n",
proc->apicid);
}
p += sizeof(struct mpproc);
continue;
case MPBUS:
case MPIOAPIC:
case MPIOINTR:
case MPLINTR:
p += 8;
continue;
default:
cprintf("mpinit: unknown config type %x\n", *p);
ismp = 0;
i = conf->entry;
}
}
bootcpu->cpu_status = CPU_STARTED;
if (!ismp) {
// Didn't like what we found; fall back to no MP.
ncpu = 1;
lapicaddr = 0;
cprintf("SMP: configuration not found, SMP disabled\n");
return;
}
cprintf("SMP: CPU %d found %d CPU(s)\n", bootcpu->cpu_id, ncpu);
if (mp->imcrp) {
// [MP 3.2.6.1] If the hardware implements PIC mode,
// switch to getting interrupts from the LAPIC.
cprintf("SMP: Setting IMCR to switch from PIC mode to symmetric I/O mode\n");
outb(0x22, 0x70); // Select IMCR
outb(0x23, inb(0x23) | 1); // Mask external interrupts.
}
}