|  | /* This file is part of the UCB release of Plan 9. It is subject to the license | 
|  | * terms in the LICENSE file found in the top-level directory of this | 
|  | * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No | 
|  | * part of the UCB release of Plan 9, including this file, may be copied, | 
|  | * modified, propagated, or distributed except according to the terms contained | 
|  | * in the LICENSE file. */ | 
|  |  | 
|  | #include <slab.h> | 
|  | #include <kmalloc.h> | 
|  | #include <kref.h> | 
|  | #include <string.h> | 
|  | #include <stdio.h> | 
|  | #include <assert.h> | 
|  | #include <error.h> | 
|  | #include <cpio.h> | 
|  | #include <pmap.h> | 
|  | #include <smp.h> | 
|  | #include <net/ip.h> | 
|  | #include <arch/mptables.h> | 
|  | #include <arch/ioapic.h> | 
|  |  | 
|  | /* | 
|  | * MultiProcessor Specification Version 1.[14]. | 
|  | */ | 
|  | typedef struct {				/* MP Floating Pointer */ | 
|  | uint8_t signature[4];			/* "_MP_" */ | 
|  | uint8_t addr[4];			/* PCMP */ | 
|  | uint8_t length;				/* 1 */ | 
|  | uint8_t revision;			/* [14] */ | 
|  | uint8_t checksum; | 
|  | uint8_t feature[5]; | 
|  | } _MP_; | 
|  |  | 
|  | typedef struct {				/* MP Configuration Table */ | 
|  | uint8_t signature[4];			/* "PCMP" */ | 
|  | uint8_t length[2]; | 
|  | uint8_t revision;			/* [14] */ | 
|  | uint8_t checksum; | 
|  | uint8_t string[20];			/* OEM + Product ID */ | 
|  | uint8_t oaddr[4];			/* OEM table pointer */ | 
|  | uint8_t olength[2];			/* OEM table length */ | 
|  | uint8_t entry[2];			/* entry count */ | 
|  | uint8_t apicpa[4];			/* local APIC address */ | 
|  | uint8_t xlength[2];			/* extended table length */ | 
|  | uint8_t xchecksum;			/* extended table checksum */ | 
|  | uint8_t reserved; | 
|  |  | 
|  | uint8_t entries[]; | 
|  | } PCMP; | 
|  |  | 
|  | typedef struct { | 
|  | char type[6]; | 
|  | int polarity;				/* default for this bus */ | 
|  | int trigger;				/* default for this bus */ | 
|  | } Mpbus; | 
|  |  | 
|  | static Mpbus mpbusdef[] = { | 
|  | {"PCI   ", IPlow, TMlevel,}, | 
|  | {"ISA   ", IPhigh, TMedge,}, | 
|  | }; | 
|  |  | 
|  | /* Editable version of the MP tables so we can fix botched entries.  Kmalloced, | 
|  | * never freed.  Might be NULL if pcmp checks failed.*/ | 
|  | static PCMP *pcmp; | 
|  |  | 
|  | static Mpbus *mpbus[Nbus]; | 
|  | int mpisabusno = -1; | 
|  | #define MP_VERBOSE_DEBUG 0 | 
|  |  | 
|  | static void mpintrprint(char *s, uint8_t * p) | 
|  | { | 
|  | char buf[128], *b, *e; | 
|  | char format[] = " type %d flags %p bus %d IRQ %d APIC %d INTIN %d\n"; | 
|  |  | 
|  | b = buf; | 
|  | e = b + sizeof(buf); | 
|  | /* can't use seprintf yet! | 
|  | b = seprintf(b, e, "mpparse: intr:"); | 
|  | if(s != NULL) | 
|  | b = seprintf(b, e, " %s:", s); | 
|  | seprintf(b, e, format, p[1], l16get(p+2), p[4], p[5], p[6], p[7]); | 
|  | printd(buf); | 
|  | */ | 
|  | printk("mpparse: intr:"); | 
|  | if (s != NULL) | 
|  | printk(" %s:", s); | 
|  | printk(format, p[1], l16get(p + 2), p[4], p[5], p[6], p[7]); | 
|  | } | 
|  |  | 
|  | /* I've seen busted MP tables routes with invalid IOAPIC ids and INTINs that are | 
|  | * out of range.  We can look at the INTINs to try to figure out which IOAPIC | 
|  | * they meant, and then adjust the INTINs too. | 
|  | * | 
|  | * Specifically, the machine I saw had two IOAPICs, neither of which had good | 
|  | * iointr APIC IDs.  ACPI and the MP tables said I had IOAPICS 8 and 9.  The | 
|  | * IOINTRs APIC IDs were 0 and 2.  Additionally, 2's INTINs were all beyond the | 
|  | * range of the 24 nrtds for that IOAPIC.  However, that IOAPIC's ibase was 24 | 
|  | * too. | 
|  | * | 
|  | * Combined, these two clues mean the INTINs are in the global ibase/route | 
|  | * space, and we can tell which IOAPIC to use based on the INTIN.  This works at | 
|  | * least for the IOAPIC 0 (8) on my hardware (IRQ routing works).  I haven't | 
|  | * been able to test on devices on the upper APIC (9). */ | 
|  | static int repair_iointr(uint8_t *iointr) | 
|  | { | 
|  | struct apic *ioapic; | 
|  | int ioapic_id; | 
|  | int intin = iointr[7]; | 
|  |  | 
|  | for (int i = 0; i < Napic; i++) { | 
|  | ioapic = &xioapic[i]; | 
|  | if (!ioapic->useable) | 
|  | continue; | 
|  | if (ioapic->ibase <= intin && | 
|  | intin < ioapic->ibase + ioapic->nrdt) { | 
|  | iointr[6] = i; | 
|  | iointr[7] = intin - ioapic->ibase; | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | static uint32_t mpmkintr(uint8_t * p) | 
|  | { | 
|  | uint32_t v; | 
|  | struct apic *apic; | 
|  | int n, polarity, trigger; | 
|  |  | 
|  | /* | 
|  | * Check valid bus, interrupt input pin polarity | 
|  | * and trigger mode. If the APIC ID is 0xff it means | 
|  | * all APICs of this type so those checks for useable | 
|  | * APIC and valid INTIN must also be done later in | 
|  | * the appropriate init routine in that case. It's hard | 
|  | * to imagine routing a signal to all IOAPICs, the | 
|  | * usual case is routing NMI and ExtINT to all LAPICs. | 
|  | */ | 
|  | if (mpbus[p[4]] == NULL) { | 
|  | mpintrprint("no source bus", p); | 
|  | return 0; | 
|  | } | 
|  | if (p[6] != 0xff) { | 
|  | if (Napic < 256 && p[6] >= Napic) { | 
|  | mpintrprint("APIC ID out of range", p); | 
|  | return 0; | 
|  | } | 
|  | switch (p[0]) { | 
|  | default: | 
|  | mpintrprint("INTIN botch", p); | 
|  | return 0; | 
|  | case 3:	/* IOINTR */ | 
|  | apic = &xioapic[p[6]]; | 
|  | if (!apic->useable) { | 
|  | mpintrprint("unuseable ioapic", p); | 
|  | if (repair_iointr(p)) { | 
|  | mpintrprint("unrepairable iointr", p); | 
|  | return 0; | 
|  | } | 
|  | mpintrprint("repaired iointr", p); | 
|  | /* Repair found a usable apic */ | 
|  | apic = &xioapic[p[6]]; | 
|  | } | 
|  | if (p[7] >= apic->nrdt) { | 
|  | mpintrprint("IO INTIN out of range", p); | 
|  | return 0; | 
|  | } | 
|  | break; | 
|  | case 4:	/* LINTR */ | 
|  | apic = &xlapic[p[6]]; | 
|  | if (!apic->useable) { | 
|  | mpintrprint("unuseable lapic", p); | 
|  | return 0; | 
|  | } | 
|  | if (p[7] >= ARRAY_SIZE(apic->lvt)) { | 
|  | mpintrprint("LOCAL INTIN out of range", p); | 
|  | return 0; | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | n = l16get(p + 2); | 
|  | if ((polarity = (n & 0x03)) == 2 || (trigger = ((n >> 2) & 0x03)) == 2) | 
|  | { | 
|  | mpintrprint("invalid polarity/trigger", p); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Create the low half of the vector table entry (LVT or RDT). | 
|  | * For the NMI, SMI and ExtINT cases, the polarity and trigger | 
|  | * are fixed (but are not always consistent over IA-32 generations). | 
|  | * For the INT case, either the polarity/trigger are given or | 
|  | * it defaults to that of the source bus; | 
|  | * whether INT is Fixed or Lowest Priority is left until later. | 
|  | */ | 
|  | v = Im; | 
|  | switch (p[1]) { | 
|  | default: | 
|  | mpintrprint("invalid type", p); | 
|  | return 0; | 
|  | case 0:	/* INT */ | 
|  | switch (polarity) { | 
|  | case 0: | 
|  | v |= mpbus[p[4]]->polarity; | 
|  | break; | 
|  | case 1: | 
|  | v |= IPhigh; | 
|  | break; | 
|  | case 3: | 
|  | v |= IPlow; | 
|  | break; | 
|  | } | 
|  | switch (trigger) { | 
|  | case 0: | 
|  | v |= mpbus[p[4]]->trigger; | 
|  | break; | 
|  | case 1: | 
|  | v |= TMedge; | 
|  | break; | 
|  | case 3: | 
|  | v |= TMlevel; | 
|  | break; | 
|  | } | 
|  | break; | 
|  | case 1:	/* NMI */ | 
|  | v |= TMedge | IPhigh | MTnmi; | 
|  | break; | 
|  | case 2:	/* SMI */ | 
|  | v |= TMedge | IPhigh | MTsmi; | 
|  | break; | 
|  | case 3:	/* ExtINT */ | 
|  | v |= TMedge | IPhigh | MTei; | 
|  | break; | 
|  | } | 
|  |  | 
|  | return v; | 
|  | } | 
|  |  | 
|  | static int mpparse(PCMP * pcmp, int maxcores) | 
|  | { | 
|  | uint32_t lo; | 
|  | uint8_t *e, *p; | 
|  | int devno, i, n; | 
|  |  | 
|  | p = pcmp->entries; | 
|  | e = ((uint8_t *) pcmp) + l16get(pcmp->length); | 
|  | while (p < e) | 
|  | switch (*p) { | 
|  | default: | 
|  | printd("mpparse: unknown PCMP type %d (e-p %#ld)\n", *p, | 
|  | e - p); | 
|  | for (i = 0; p < e; i++) { | 
|  | if (i && ((i & 0x0f) == 0)) | 
|  | printd("\n"); | 
|  | printd(" 0x%#2.2x", *p); | 
|  | p++; | 
|  | } | 
|  | printd("\n"); | 
|  | break; | 
|  | case 0:	/* processor */ | 
|  | /* | 
|  | * Initialise the APIC if it is enabled (p[3] & 0x01). | 
|  | * p[1] is the APIC ID, the memory mapped address comes | 
|  | * from the PCMP structure as the addess is local to the | 
|  | * CPU and identical for all. Indicate whether this is | 
|  | * the bootstrap processor (p[3] & 0x02). | 
|  | */ | 
|  | printd("mpparse: cpu %d pa %p bp %d\n", | 
|  | p[1], l32get(pcmp->apicpa), p[3] & 0x02); | 
|  | if ((p[3] & 0x01) != 0 && maxcores > 0) { | 
|  | maxcores--; | 
|  | apicinit(p[1], l32get(pcmp->apicpa), p[3] & | 
|  | 0x02); | 
|  | } | 
|  | p += 20; | 
|  | break; | 
|  | case 1:	/* bus */ | 
|  | printd("mpparse: bus: %d type %6.6s\n", p[1], (char *)p | 
|  | + 2); | 
|  | if (p[1] >= Nbus) { | 
|  | printk("mpparse: bus %d out of range\n", p[1]); | 
|  | p += 8; | 
|  | break; | 
|  | } | 
|  | if (mpbus[p[1]] != NULL) { | 
|  | printk("mpparse: bus %d already allocated\n", | 
|  | p[1]); | 
|  | p += 8; | 
|  | break; | 
|  | } | 
|  | for (i = 0; i < ARRAY_SIZE(mpbusdef); i++) { | 
|  | if (memcmp(p + 2, mpbusdef[i].type, 6) != 0) | 
|  | continue; | 
|  | if (memcmp(p + 2, "ISA   ", 6) == 0) { | 
|  | if (mpisabusno != -1) { | 
|  | printk("mpparse: bus %d already have ISA bus %d\n", | 
|  | p[1], mpisabusno); | 
|  | continue; | 
|  | } | 
|  | mpisabusno = p[1]; | 
|  | } | 
|  | mpbus[p[1]] = &mpbusdef[i]; | 
|  | break; | 
|  | } | 
|  | if (mpbus[p[1]] == NULL) | 
|  | printk("mpparse: bus %d type %6.6s unknown\n", | 
|  | p[1], (char *)p + 2); | 
|  |  | 
|  | p += 8; | 
|  | break; | 
|  | case 2:	/* IOAPIC */ | 
|  | /* | 
|  | * Initialise the IOAPIC if it is enabled (p[3] & 0x01). | 
|  | * p[1] is the APIC ID, p[4-7] is the memory mapped | 
|  | * address. | 
|  | */ | 
|  | if (p[3] & 0x01) | 
|  | ioapicinit(p[1], -1, l32get(p + 4)); | 
|  |  | 
|  | p += 8; | 
|  | break; | 
|  | case 3:	/* IOINTR */ | 
|  | /* | 
|  | * p[1] is the interrupt type; | 
|  | * p[2-3] contains the polarity and trigger mode; | 
|  | * p[4] is the source bus; | 
|  | * p[5] is the IRQ on the source bus; | 
|  | * p[6] is the destination IOAPIC; | 
|  | * p[7] is the INITIN pin on the destination IOAPIC. | 
|  | */ | 
|  | if (p[6] == 0xff) { | 
|  | mpintrprint("routed to all IOAPICs", p); | 
|  | p += 8; | 
|  | break; | 
|  | } | 
|  | if ((lo = mpmkintr(p)) == 0) { | 
|  | if (MP_VERBOSE_DEBUG) | 
|  | mpintrprint("iointr skipped", p); | 
|  | p += 8; | 
|  | break; | 
|  | } | 
|  | if (MP_VERBOSE_DEBUG) | 
|  | mpintrprint("iointr", p); | 
|  |  | 
|  | /* | 
|  | * Always present the device number in the style | 
|  | * of a PCI Interrupt Assignment Entry. For the ISA | 
|  | * bus the IRQ is the device number but unencoded. | 
|  | * May need to handle other buses here in the future | 
|  | * (but unlikely). | 
|  | * | 
|  | * For PCI devices, this field's lowest two bits are | 
|  | * INT#A == 0, INT#B == 1, etc.  Bits 2-6 are the PCI | 
|  | * device number. | 
|  | */ | 
|  | devno = p[5]; | 
|  | if (memcmp(mpbus[p[4]]->type, "PCI   ", 6) != 0) | 
|  | devno <<= 2; | 
|  | ioapicintrinit(p[4], p[6], p[7], devno, lo); | 
|  |  | 
|  | p += 8; | 
|  | break; | 
|  | case 4:	/* LINTR */ | 
|  | /* | 
|  | * Format is the same as IOINTR above. | 
|  | */ | 
|  | if ((lo = mpmkintr(p)) == 0) { | 
|  | p += 8; | 
|  | break; | 
|  | } | 
|  | if (MP_VERBOSE_DEBUG) | 
|  | mpintrprint("LINTR", p); | 
|  |  | 
|  | /* | 
|  | * Everything was checked in mpmkintr above. | 
|  | */ | 
|  | if (p[6] == 0xff) { | 
|  | for (i = 0; i < Napic; i++) { | 
|  | if (!xlapic[i].useable || | 
|  | xlapic[i].addr) | 
|  | continue; | 
|  | xlapic[i].lvt[p[7]] = lo; | 
|  | } | 
|  | } else | 
|  | xlapic[p[6]].lvt[p[7]] = lo; | 
|  | p += 8; | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * There's nothing of interest in the extended table, | 
|  | * but check it for consistency. | 
|  | */ | 
|  | p = e; | 
|  | e = p + l16get(pcmp->xlength); | 
|  | while (p < e) | 
|  | switch (*p) { | 
|  | default: | 
|  | n = p[1]; | 
|  | printd("mpparse: unknown extended entry %d length %d\n", | 
|  | *p, n); | 
|  | for (i = 0; i < n; i++) { | 
|  | if (i && ((i & 0x0f) == 0)) | 
|  | printd("\n"); | 
|  | printd(" %#2.2ux", *p); | 
|  | p++; | 
|  | } | 
|  | printd("\n"); | 
|  | break; | 
|  | case 128: | 
|  | printd("address space mapping\n"); | 
|  | printd(" bus %d type %d base %p length %p\n", | 
|  | p[2], p[3], l64get(p + 4), l64get(p + 12)); | 
|  | p += p[1]; | 
|  | break; | 
|  | case 129: | 
|  | printd("bus hierarchy descriptor\n"); | 
|  | printd(" bus %d sd %d parent bus %d\n", p[2], p[3], | 
|  | p[4]); | 
|  | p += p[1]; | 
|  | break; | 
|  | case 130: | 
|  | printd("compatibility bus address space modifier\n"); | 
|  | printd(" bus %d pr %d range list %d\n", | 
|  | p[2], p[3], l32get(p + 4)); | 
|  | p += p[1]; | 
|  | break; | 
|  | } | 
|  | return maxcores; | 
|  | } | 
|  |  | 
|  | static void *sigsearch(char *signature) | 
|  | { | 
|  | uintptr_t p; | 
|  | uint8_t *bda; | 
|  | void *r; | 
|  | #if 0 | 
|  | /* | 
|  | * Search for the data structure: | 
|  | * 1) in the first KB of the EBDA; | 
|  | * 2) in the last KB of system base memory; | 
|  | * 3) in the BIOS ROM between 0xe0000 and 0xfffff. | 
|  | */ | 
|  | bda = BIOSSEG(0x40); | 
|  | if (memcmp(KADDR(0xfffd9), "EISA", 4) == 0) { | 
|  | if ((p = (bda[0x0f] << 8) | bda[0x0e])) { | 
|  | if ((r = sigscan(BIOSSEG(p), 1024, signature)) != NULL) | 
|  | return r; | 
|  | } | 
|  | } | 
|  |  | 
|  | p = ((bda[0x14] << 8) | bda[0x13]) * 1024; | 
|  | if ((r = sigscan(KADDR(p - 1024), 1024, signature)) != NULL) | 
|  | return r; | 
|  | #endif | 
|  | r = sigscan(KADDR(0xe0000), 0x20000, signature); | 
|  | printk("Found MP table at %p\n", r); | 
|  | if (r != NULL) | 
|  | return r; | 
|  |  | 
|  | return NULL; | 
|  | /* and virtualbox hidden mp tables... */ | 
|  | //  return sigscan(KADDR(0xa0000 - 1024), 1024, signature); | 
|  | } | 
|  |  | 
|  | static PCMP *copy_pcmp(PCMP *pcmp) | 
|  | { | 
|  | PCMP *new_pcmp; | 
|  | size_t n = l16get(pcmp->length) + l16get(pcmp->xlength); | 
|  |  | 
|  | new_pcmp = kmalloc(n, MEM_ATOMIC); | 
|  | assert(new_pcmp); | 
|  | memcpy(new_pcmp, pcmp, n); | 
|  | return new_pcmp; | 
|  | } | 
|  |  | 
|  | int mpsinit(int maxcores) | 
|  | { | 
|  | uint8_t *p; | 
|  | int i; | 
|  | _MP_ *mp; | 
|  |  | 
|  | if ((mp = sigsearch("_MP_")) == NULL) { | 
|  | printk("No mp tables found, might have issues!\n"); | 
|  | return maxcores; | 
|  | } | 
|  | /* TODO: if an IMCR exists, we should set it to 1, though i've heard | 
|  | * that ACPI-capable HW doesn't have the IMCR anymore. */ | 
|  |  | 
|  | if (MP_VERBOSE_DEBUG) { | 
|  | printk("_MP_ @ %#p, addr %p length %ud rev %d", | 
|  | mp, l32get(mp->addr), mp->length, mp->revision); | 
|  | for (i = 0; i < sizeof(mp->feature); i++) | 
|  | printk(" %2.2p", mp->feature[i]); | 
|  | printk("\n"); | 
|  | } | 
|  | if (mp->revision != 1 && mp->revision != 4) | 
|  | return maxcores; | 
|  | if (sigchecksum(mp, mp->length * 16) != 0) | 
|  | return maxcores; | 
|  | if ((pcmp = KADDR_NOCHECK(l32get(mp->addr))) == NULL) | 
|  | return maxcores; | 
|  | if (pcmp->revision != 1 && pcmp->revision != 4) { | 
|  | pcmp = NULL; | 
|  | return maxcores; | 
|  | } | 
|  | if (sigchecksum(pcmp, l16get(pcmp->length)) != 0) { | 
|  | pcmp = NULL; | 
|  | return maxcores; | 
|  | } | 
|  |  | 
|  | pcmp = copy_pcmp(pcmp); | 
|  |  | 
|  | if (MP_VERBOSE_DEBUG) { | 
|  | printk("PCMP @ %#p length %p revision %d\n", | 
|  | pcmp, l16get(pcmp->length), pcmp->revision); | 
|  | printk(" %20.20s oaddr %p olength %p\n", | 
|  | (char *)pcmp->string, l32get(pcmp->oaddr), | 
|  | l16get(pcmp->olength)); | 
|  | printk(" entry %d apicpa %p\n", | 
|  | l16get(pcmp->entry), l32get(pcmp->apicpa)); | 
|  |  | 
|  | printk(" xlength %p xchecksum %p\n", | 
|  | l16get(pcmp->xlength), pcmp->xchecksum); | 
|  | } | 
|  | if (pcmp->xchecksum != 0) { | 
|  | p = ((uint8_t *) pcmp) + l16get(pcmp->length); | 
|  | i = sigchecksum(p, l16get(pcmp->xlength)); | 
|  | if (((i + pcmp->xchecksum) & 0xff) != 0) { | 
|  | printd("extended table checksums to %p\n", i); | 
|  | return maxcores; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Parse the PCMP table and set up the datastructures | 
|  | * for later interrupt enabling and application processor | 
|  | * startup. | 
|  | */ | 
|  | return mpparse(pcmp, maxcores); | 
|  | } |