X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?a=blobdiff_plain;f=kitten%2Farch%2Fx86_64%2Fkernel%2Fmpparse.c;fp=kitten%2Farch%2Fx86_64%2Fkernel%2Fmpparse.c;h=10495c6cb10a019c90f6bcf3331b529c6c1fcbda;hb=66a1a4c7a9edcd7d8bc207aca093d694a6e6b5b2;hp=0000000000000000000000000000000000000000;hpb=f7cf9c19ecb0a589dd45ae0d2c91814bd3c2acc2;p=palacios.git diff --git a/kitten/arch/x86_64/kernel/mpparse.c b/kitten/arch/x86_64/kernel/mpparse.c new file mode 100644 index 0000000..10495c6 --- /dev/null +++ b/kitten/arch/x86_64/kernel/mpparse.c @@ -0,0 +1,491 @@ +/* + * Intel Multiprocessor Specification 1.1 and 1.4 + * compliant MP-table parsing routines. + * + * (c) 1995 Alan Cox, Building #3 + * (c) 1998, 1999, 2000 Ingo Molnar + * + * Fixes + * Erich Boleyn : MP v1.4 and additional changes. + * Alan Cox : Added EBDA scanning + * Ingo Molnar : various cleanups and rewrites + * Maciej W. Rozycki: Bits for default MP configurations + * Paul Diefenbaugh: Added full ACPI support + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * Points to the MP table, once and if it is found. + * This gets initialized by find_mp_table(). + */ +static struct intel_mp_floating *mpf_found; + +/** + * Physical CPU ID of the bootstrap CPU (the BP). + */ +unsigned int __initdata boot_phys_cpu_id = -1U; + +/** + * The number of CPUs in the system. + */ +unsigned int __initdata num_cpus = 0; + +/** + * Map of all CPUs present. + * Bits set represent physical CPU IDs present. + */ +physid_mask_t phys_cpu_present_map = PHYSID_MASK_NONE; + +/** + * Version information for every Local APIC in the system. + * The array is indexed by APIC ID. + */ +unsigned char apic_version[MAX_APICS]; + +/** + * MP Bus information + */ +static int mp_current_pci_id = 0; +unsigned char mp_bus_id_to_type [MAX_MP_BUSSES] = { [0 ... MAX_MP_BUSSES-1] = -1 }; +int mp_bus_id_to_pci_bus [MAX_MP_BUSSES] = { [0 ... MAX_MP_BUSSES-1] = -1 }; + +/** + * MP IO APIC information + */ +int nr_ioapics = 0; +struct mpc_config_ioapic mp_ioapics[MAX_IO_APICS]; + +/** + * MP IRQ information + */ +int mp_irq_entries = 0; +struct mpc_config_intsrc mp_irqs[MAX_IRQ_SOURCES]; + + +/* TODO: move these */ +int pic_mode; + +/** + * Computes the checksum of an MP configuration block. + */ +static int __init +mpf_checksum(unsigned char *mp, int len) +{ + int sum = 0; + while (len--) + sum += *mp++; + return sum & 0xFF; +} + +/** + * Parses an MP table CPU entry. + */ +static void __init +MP_processor_info(struct mpc_config_processor *m) +{ + int cpu; + int is_bp; + unsigned char ver; + cpumask_t tmp_map; + + if (!(m->mpc_cpuflag & CPU_ENABLED)) + panic("A disabled CPU was encountered\n"); + + /* Count the new CPU */ + if (++num_cpus > NR_CPUS) + panic("NR_CPUS limit of %i reached.\n", NR_CPUS); + + /* + * Determine if this is the bootstrap processor... + * the one responsible for booting the other CPUs. + */ + is_bp = (m->mpc_cpuflag & CPU_BOOTPROCESSOR); + + /* + * Assign a logical CPU ID. + * The bootstrap CPU is always assigned logical ID 0. + * All other CPUs are assigned the lowest ID available. + */ + if (is_bp) { + cpu = 0; + } else { + cpus_complement(tmp_map, cpu_present_map); + cpu = first_cpu(tmp_map); + } + + /* Validate APIC version, fixing up if necessary. */ + ver = m->mpc_apicver; + if (ver == 0x0) { + printk(KERN_ERR "BIOS bug, APIC version is 0 for PhysCPU#%d! " + "fixing up to 0x10. (tell your hw vendor)\n", + m->mpc_apicid); + ver = 0x10; + } + + /* Remember the APIC's version */ + apic_version[m->mpc_apicid] = ver; + + /* Add the CPU to the map of physical CPU IDs present. */ + physid_set(m->mpc_apicid, phys_cpu_present_map); + + /* Remember the physical CPU ID of the bootstrap CPU. */ + if (is_bp) + boot_phys_cpu_id = m->mpc_apicid; + + /* Add the CPU to the map of logical CPU IDs present. */ + cpu_set(cpu, cpu_present_map); + + /* Store ID information. */ + cpu_info[cpu].logical_id = cpu; + cpu_info[cpu].physical_id = m->mpc_apicid; + cpu_info[cpu].arch.apic_id = m->mpc_apicid; + + printk(KERN_DEBUG + "Physical CPU #%d -> Logical CPU #%d, %d:%d APIC version %d%s\n", + m->mpc_apicid, + cpu, + (m->mpc_cpufeature & CPU_FAMILY_MASK) >> 8, + (m->mpc_cpufeature & CPU_MODEL_MASK) >> 4, + ver, + is_bp ? " (Bootstrap CPU)" : ""); +} + +/** + * Parses an MP table BUS entry. + */ +static void __init +MP_bus_info(struct mpc_config_bus *m) +{ + char str[7]; + + memcpy(str, m->mpc_bustype, 6); + str[6] = 0; + printk(KERN_DEBUG "Bus #%d is %s\n", m->mpc_busid, str); + + if (strncmp(str, "ISA", 3) == 0) { + mp_bus_id_to_type[m->mpc_busid] = MP_BUS_ISA; + } else if (strncmp(str, "EISA", 4) == 0) { + mp_bus_id_to_type[m->mpc_busid] = MP_BUS_EISA; + } else if (strncmp(str, "PCI", 3) == 0) { + mp_bus_id_to_type[m->mpc_busid] = MP_BUS_PCI; + mp_bus_id_to_pci_bus[m->mpc_busid] = mp_current_pci_id; + mp_current_pci_id++; + } else if (strncmp(str, "MCA", 3) == 0) { + mp_bus_id_to_type[m->mpc_busid] = MP_BUS_MCA; + } else { + printk(KERN_ERR "Unknown bustype %s\n", str); + } +} + +/** + * Parses an MP table BUS entry. + */ +static void __init +MP_ioapic_info(struct mpc_config_ioapic *m) +{ + if (!(m->mpc_flags & MPC_APIC_USABLE)) { + printk(KERN_DEBUG "Encountered unusable APIC, ignoring it.\n"); + return; + } + + printk(KERN_DEBUG "I/O APIC #%d Version %d at 0x%X\n", + m->mpc_apicid, m->mpc_apicver, m->mpc_apicaddr); + if (nr_ioapics >= MAX_IO_APICS) { + printk(KERN_ERR "Max # of I/O APICs (%d) exceeded (found %d).\n", + MAX_IO_APICS, nr_ioapics); + panic("Recompile kernel with bigger MAX_IO_APICS!.\n"); + } + if (!m->mpc_apicaddr) { + printk(KERN_ERR "WARNING: bogus zero I/O APIC address" + " found in MP table, skipping!\n"); + return; + } + mp_ioapics[nr_ioapics] = *m; + nr_ioapics++; + + ioapic_id[ioapic_num] = m->mpc_apicid; + ioapic_phys_addr[ioapic_num] = m->mpc_apicaddr; + ioapic_num++; +} + +/** + * Parses an MP table IRQ source entry. + */ +static void __init +MP_intsrc_info(struct mpc_config_intsrc *m) +{ + mp_irqs [mp_irq_entries] = *m; + printk(KERN_DEBUG + "Int: type %d, pol %d, trig %d, bus %d," + " IRQ %02x, APIC ID %x, APIC INT %02x\n", + m->mpc_irqtype, m->mpc_irqflag & 3, + (m->mpc_irqflag >> 2) & 3, m->mpc_srcbus, + m->mpc_srcbusirq, m->mpc_dstapic, m->mpc_dstirq); + if (++mp_irq_entries >= MAX_IRQ_SOURCES) + panic("Max # of irq sources exceeded!!\n"); +} + +/** + * Parses an MP table LINT entry. + */ +static void __init +MP_lintsrc_info (struct mpc_config_lintsrc *m) +{ + printk(KERN_DEBUG + "Lint: type %d, pol %d, trig %d, bus %d," + " IRQ %02x, APIC ID %x, APIC LINT %02x\n", + m->mpc_irqtype, m->mpc_irqflag & 3, + (m->mpc_irqflag >> 2) &3, m->mpc_srcbusid, + m->mpc_srcbusirq, m->mpc_destapic, m->mpc_destapiclint); + /* + * Well it seems all SMP boards in existence + * use ExtINT/LVT1 == LINT0 and + * NMI/LVT2 == LINT1 - the following check + * will show us if this assumptions is false. + * Until then we do not have to add baggage. + */ + if ((m->mpc_irqtype == mp_ExtINT) && + (m->mpc_destapiclint != 0)) + BUG(); + if ((m->mpc_irqtype == mp_NMI) && + (m->mpc_destapiclint != 1)) + BUG(); +} + +/** + * Parses the input MP table, storing various bits of information in global + * variables as it goes. + */ +static int __init +read_mpc(struct mp_config_table *mpc) +{ + char str[16]; + int count=sizeof(*mpc); + unsigned char *mpt=((unsigned char *)mpc)+count; + + if (memcmp(mpc->mpc_signature, MPC_SIGNATURE, 4)) { + printk(KERN_ERR "SMP mptable: bad signature [%c%c%c%c]!\n", + mpc->mpc_signature[0], + mpc->mpc_signature[1], + mpc->mpc_signature[2], + mpc->mpc_signature[3]); + return -1; + } + if (mpf_checksum((unsigned char *)mpc, mpc->mpc_length)) { + printk(KERN_ERR "SMP mptable: checksum error!\n"); + return -1; + } + if (mpc->mpc_spec!=0x01 && mpc->mpc_spec!=0x04) { + printk(KERN_ERR "SMP mptable: bad table version (%d)!\n", + mpc->mpc_spec); + return -1; + } + if (!mpc->mpc_lapic) { + printk(KERN_ERR "SMP mptable: null local APIC address!\n"); + return -1; + } + memcpy(str, mpc->mpc_oem, 8); + str[8]=0; + printk(KERN_DEBUG " OEM ID: %s\n", str); + + memcpy(str, mpc->mpc_productid, 12); + str[12]=0; + printk(KERN_DEBUG " Product ID: %s\n", str); + + printk(KERN_DEBUG " APIC at: 0x%X\n", mpc->mpc_lapic); + + /* Save the local APIC address, it might be non-default. */ + lapic_phys_addr = mpc->mpc_lapic; + + /* Now process all of the configuration blocks in the table. */ + while (count < mpc->mpc_length) { + switch(*mpt) { + case MP_PROCESSOR: + { + struct mpc_config_processor *m= + (struct mpc_config_processor *)mpt; + MP_processor_info(m); + mpt += sizeof(*m); + count += sizeof(*m); + break; + } + case MP_BUS: + { + struct mpc_config_bus *m= + (struct mpc_config_bus *)mpt; + MP_bus_info(m); + mpt += sizeof(*m); + count += sizeof(*m); + break; + } + case MP_IOAPIC: + { + struct mpc_config_ioapic *m= + (struct mpc_config_ioapic *)mpt; + MP_ioapic_info(m); + mpt+=sizeof(*m); + count+=sizeof(*m); + break; + } + case MP_INTSRC: + { + struct mpc_config_intsrc *m= + (struct mpc_config_intsrc *)mpt; + + MP_intsrc_info(m); + mpt+=sizeof(*m); + count+=sizeof(*m); + break; + } + case MP_LINTSRC: + { + struct mpc_config_lintsrc *m= + (struct mpc_config_lintsrc *)mpt; + MP_lintsrc_info(m); + mpt+=sizeof(*m); + count+=sizeof(*m); + break; + } + } + } + //clustered_apic_check(); + if (!num_cpus) + printk(KERN_ERR "SMP mptable: no CPUs registered!\n"); + return 0; +} + +/** + * Determines the multiprocessor configuration. + * The configuration information is stored in global variables so nothing is + * returned. find_mp_config() must be called before this function. + */ +void __init +get_mp_config(void) +{ + struct intel_mp_floating *mpf = mpf_found; + if (!mpf) { + printk(KERN_WARNING "Assuming 1 CPU.\n"); + num_cpus = 1; + /* Assign the only CPU logical=physical ID 0 */ + cpu_set(0, cpu_present_map); + physid_set(0, phys_cpu_present_map); + cpu_info[0].logical_id = 0; + cpu_info[0].physical_id = 0; + cpu_info[0].arch.apic_id = 0; + return; + } + + printk(KERN_DEBUG "Intel MultiProcessor Specification v1.%d\n", + mpf->mpf_specification); + if (mpf->mpf_feature2 & (1<<7)) { + printk(KERN_DEBUG " IMCR and PIC compatibility mode.\n"); + pic_mode = 1; + } else { + printk(KERN_DEBUG " Virtual Wire compatibility mode.\n"); + pic_mode = 0; + } + + /* + * We don't support the default MP configuration. + * All supported multi-CPU systems must provide a full MP table. + */ + if (mpf->mpf_feature1 != 0) + BUG(); + if (!mpf->mpf_physptr) + BUG(); + + /* + * Set this early so we don't allocate CPU0 if the + * MADT list doesn't list the bootstrap processor first. + * The bootstrap processor has to be logical ID 0... which + * we are reserving here. + */ + cpu_set(0, cpu_present_map); + + /* + * Parse the MP configuration + */ + if (read_mpc(phys_to_virt(mpf->mpf_physptr))) + panic("BIOS bug, MP table errors detected! (tell your hw vendor)\n"); +} + +/** + * Scans a region of memory for the MP table. + */ +static int __init +scan(unsigned long base, unsigned long length) +{ + unsigned int *bp = phys_to_virt(base); + struct intel_mp_floating *mpf; + + printk(KERN_DEBUG "Scan for MP table from 0x%p for %ld bytes\n", + bp, length); + + while (length > 0) { + mpf = (struct intel_mp_floating *)bp; + if ((*bp == SMP_MAGIC_IDENT) && + (mpf->mpf_length == 1) && + !mpf_checksum((unsigned char *)bp, 16) && + ((mpf->mpf_specification == 1) + || (mpf->mpf_specification == 4)) ) { + + reserve_bootmem(virt_to_phys(mpf), PAGE_SIZE); + if (mpf->mpf_physptr) + reserve_bootmem(mpf->mpf_physptr, PAGE_SIZE); + mpf_found = mpf; + printk(KERN_DEBUG "Found MP table at: 0x%p\n", mpf); + return 1; + } + bp += 4; + length -= 16; + } + return 0; +} + +/** + * Locates the MP table, if there is one. + * This does not parse the MP table... get_mp_config() does that. + */ +void __init +find_mp_config(void) +{ + /* + * 1) Scan the bottom 1K for a signature + * 2) Scan the top 1K of base RAM + * 3) Scan the 64K of bios + */ + if (scan(0x0,0x400) || + scan(639*0x400,0x400) || + scan(0xF0000,0x10000)) + return; + + /* + * If it is an MP machine we should know now. + * + * If not, make a final effort and scan the + * Extended BIOS Data Area. + * + * NOTE! There are Linux loaders that will corrupt the EBDA + * area, and as such this kind of MP config may be less + * trustworthy, simply because the MP table may have been + * stomped on during early boot. These loaders are buggy and + * should be fixed. + */ + if (scan(ebda_addr, 0x1000)) { + printk(KERN_WARNING "MP table found in EBDA\n"); + return; + } + + /* If we have come this far, we did not find an MP table */ + printk(KERN_DEBUG "No MP table found.\n"); +} +