// Search for and parse the multiprocessor configuration table // See http://developer.intel.com/design/pentium/datashts/24201606.pdf #include #include #include #include #include #include #include #include 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. } }