--- /dev/null
+/*
+ * Intel Multiprocessor Specification 1.1 and 1.4
+ * compliant MP-table parsing routines.
+ *
+ * (c) 1995 Alan Cox, Building #3 <alan@redhat.com>
+ * (c) 1998, 1999, 2000 Ingo Molnar <mingo@redhat.com>
+ *
+ * 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 <lwk/smp.h>
+#include <lwk/init.h>
+#include <lwk/bootmem.h>
+#include <lwk/cpuinfo.h>
+#include <lwk/params.h>
+#include <arch/io.h>
+#include <arch/mpspec.h>
+#include <arch/proto.h>
+#include <arch/io_apic.h>
+
+/**
+ * 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");
+}
+