Back to home page

Quest Cross Reference

 
 

    


Warning, cross-references for /kernel/smp/intel.c need to be fixed.

0001 /*                    The Quest Operating System
0002  *  Copyright (C) 2005-2010  Richard West, Boston University
0003  *
0004  *  This program is free software: you can redistribute it and/or modify
0005  *  it under the terms of the GNU General Public License as published by
0006  *  the Free Software Foundation, either version 3 of the License, or
0007  *  (at your option) any later version.
0008  *
0009  *  This program is distributed in the hope that it will be useful,
0010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0012  *  GNU General Public License for more details.
0013  *
0014  *  You should have received a copy of the GNU General Public License
0015  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
0016  */
0017 
0018 /* Based on:
0019  * http://www.uruk.org/mps/, http://www.osdev.org/, http://www.osdever.net/
0020  * and the Intel Multiprocessing Specification v1.4.
0021  */
0022 #include "arch/i386.h"
0023 #include "kernel.h"
0024 #include "mem/mem.h"
0025 #include "smp/smp.h"
0026 #include "smp/intel_mps.h"
0027 #include "smp/apic.h"
0028 #include "smp/spinlock.h"
0029 #include "drivers/pci/pci.h"
0030 #include "util/printf.h"
0031 
0032 static int process_mp_fp (struct mp_fp *, bool);
0033 static int process_mp_config (struct mp_config *, bool);
0034 static int add_processor (struct mp_config_processor_entry *);
0035 static struct mp_fp *probe_mp_fp (uint32, uint32);
0036 
0037 /*******************************************************
0038  * Support for the Intel Multiprocessing Specification *
0039  *******************************************************/
0040 
0041 /* The Intel MPS is a legacy standard which specifies a set of tables
0042  * populated by the BIOS that describe the system well enough to find
0043  * and start the Application Processors and IO-APICs. */
0044 
0045 /* It is officially superseded by ACPI, but every system still
0046  * supports it.  It has the benefit of being simpler.  However, it is
0047  * not as informative, particularly when it comes to distinguishing
0048  * non-uniform architectures and also hyperthreading vs multicore
0049  * systems. */
0050 
0051 /* Quest will attempt to make use of ACPI first, then fall-back to the
0052  * Intel MPS.  I do not plan on supporting systems pre-MPS, which
0053  * means Quest is effectively limited to booting on Pentium-Pro+ most
0054  * likely. */
0055 
0056 /* intel_mps_init() -- Returns the number of processors found if
0057  * successfully initialized, or else 0. */
0058 uint32
0059 intel_mps_init(bool pci_irq_only)
0060 {
0061   struct mp_fp *ptr;
0062 
0063   if ((ptr = probe_mp_fp (0x9F800, 0xA0000)));
0064   else if ((ptr = probe_mp_fp (0x0040E, 0x0140E)));
0065   else if ((ptr = probe_mp_fp (0xE0000, 0xFFFFF)));
0066   else return 0;
0067 
0068   if(ptr)
0069     return process_mp_fp(ptr, pci_irq_only);
0070   else
0071     return 0;
0072 }
0073 
0074 /* The Intel MPS tables are allowed to be placed in a variety of
0075  * memory regions available to the BIOS.  Most commonly found in the
0076  * ShadowROM area.  This function will search for the 4-byte signature
0077  * in a given memory region and return a pointer to the so-called
0078  * "Floating Pointer" table. */
0079 static struct mp_fp *
0080 probe_mp_fp (uint32 start, uint32 end)
0081 {
0082   uint32 i;
0083   start &= ~0xF;                /* 16-byte aligned */
0084   for (i = start; i < end; i += 0x10) {
0085     if (*((volatile uint32 *) i) == MP_FP_SIGNATURE) {
0086       /* found it */
0087       return (struct mp_fp *) i;
0088     }
0089   }
0090   return NULL;
0091 }
0092 
0093 /* Once found, the pointer to the table is examined for sanity by this
0094  * function.  It makes further calls to interpret the information
0095  * found in the tables and begin SMP initialization. */
0096 int
0097 process_mp_fp (struct mp_fp *ptr, bool pci_irq_only)
0098 {
0099   struct mp_config *cfg;
0100 
0101   /* Sanity checks */
0102   if (ptr == NULL) {
0103     print ("No SMP support detected.\n");
0104     return 1;
0105   }
0106 
0107   if (ptr->signature != MP_FP_SIGNATURE) {
0108     print ("MP floating pointer structure signature invalid.\n");
0109     return 1;
0110   }
0111 
0112   if (ptr->length != 1) {
0113     print ("MP floating pointer structure reports length != 16 bytes.\n");
0114     return 1;
0115   }
0116 
0117   switch (ptr->version) {
0118   case 1:
0119     print ("Intel MP specification v1.1\n");
0120     break;
0121   case 4:
0122     print ("Intel MP specification v1.4\n");
0123     break;
0124   default:
0125     print ("Unknown MP specification reported.\n");
0126     return 1;
0127   }
0128 
0129   if (checksum ((uint8 *) ptr, sizeof (struct mp_fp)) != 0) {
0130     print ("MP floating pointer structure failed checksum.\n");
0131     return 1;
0132   }
0133 
0134   /* Check MP config table given by floating pointer struct */
0135   cfg = (struct mp_config *) ptr->mpconfig_ptr;
0136 
0137   process_mp_config (cfg, pci_irq_only);
0138 
0139   return mp_num_cpus;
0140 }
0141 
0142 /* The MP Configuration table is pointed to by the MP Floating Pointer
0143  * table and contains the information in question that tells us how
0144  * many processors, IO-APICs, and various bits of useful details about
0145  * them. */
0146 #define printf com1_printf
0147 static int
0148 process_mp_config (struct mp_config *cfg, bool pci_irq_only)
0149 {
0150   int i;
0151   uint8 *ptr;
0152   extern uint32 mp_LAPIC_addr, mp_IOAPIC_addr;
0153   extern uint32 mp_num_IOAPICs;
0154   extern mp_IOAPIC_info mp_IOAPICs[];
0155   extern mp_int_override mp_overrides[];
0156   extern uint32 mp_num_overrides;
0157 
0158 
0159   /* Sanity checks */
0160   if (cfg == NULL) {
0161     printf ("MP config pointer is NULL.\n");
0162     return 1;
0163   }
0164 
0165   if (cfg->signature != MP_CFG_SIGNATURE) {
0166     printf ("MP config signature invalid.\n");
0167     return 1;
0168   }
0169 
0170   if (cfg->specification_revision != 1 && cfg->specification_revision != 4) {
0171     printf ("Unknown MP specification reported by MP config table.\n");
0172     return 1;
0173   }
0174 
0175   if (checksum ((uint8 *) cfg, cfg->base_table_length) != 0) {
0176     printf ("MP config table failed checksum.\n");
0177     return 1;
0178   }
0179 
0180   printf ("Manufacturer: %.8s Product: %.12s Local APIC: %.8X\n",
0181           cfg->OEM_id, cfg->product_id, cfg->local_APIC);
0182   if (cfg->local_APIC)
0183     mp_LAPIC_addr = cfg->local_APIC;
0184 
0185   /* Check entries */
0186   ptr = (uint8 *) cfg->entries;
0187   for (i = 0; i < cfg->entry_count; i++) {
0188     struct mp_config_entry *entry = (struct mp_config_entry *) ptr;
0189     switch (*ptr) {
0190     case MP_CFG_TYPE_PROCESSOR:        /* Processor entry */
0191       if (!pci_irq_only) {
0192         printf ("Processor APIC-id: %X version: %X %s%s",
0193                 entry->processor.APIC_id,
0194                 entry->processor.APIC_version,
0195                 (entry->processor.flags & 1) ? "(enabled)" : "(disabled)",
0196                 (entry->processor.flags & 2) ? " (bootstrap)" : "");
0197 
0198         if (add_processor (&entry->processor))    /* Try to boot it if necessary */
0199           printf (" (booted)");
0200         printf ("\n");
0201       }
0202       ptr += sizeof (struct mp_config_processor_entry);
0203       break;
0204 
0205     case MP_CFG_TYPE_BUS:      /* Bus entry, find out which one is ISA */
0206       printf ("Bus entry-id: %X type: %.6s\n",
0207               entry->bus.id, entry->bus.bus_type);
0208       if (entry->bus.bus_type[0] == 'I' &&
0209           entry->bus.bus_type[1] == 'S' && entry->bus.bus_type[2] == 'A') {
0210         mp_ISA_bus_id = entry->bus.id;
0211       }
0212       ptr += sizeof (struct mp_config_bus_entry);
0213       break;
0214 
0215     case MP_CFG_TYPE_IO_APIC:  /* IO-APIC entry */
0216       printf ("IO APIC-id: 0x%X version: 0x%X address: 0x%.8X",
0217               entry->IO_APIC.id,
0218               entry->IO_APIC.version, entry->IO_APIC.address);
0219       if (entry->IO_APIC.flags & 1) {
0220         mp_IOAPIC_addr = entry->IO_APIC.address;
0221         printf ("\n");
0222 
0223         if (mp_num_IOAPICs == MAX_IOAPICS)
0224           panic ("Too many IO-APICs.");
0225         mp_IOAPICs[mp_num_IOAPICs].id = entry->IO_APIC.id;
0226         mp_IOAPICs[mp_num_IOAPICs].address = entry->IO_APIC.address;
0227         /* going to assume IO-APICs are listed in order */
0228         if (mp_num_IOAPICs == 0)
0229           mp_IOAPICs[mp_num_IOAPICs].startGSI = 0;
0230         else
0231           mp_IOAPICs[mp_num_IOAPICs].startGSI =
0232             mp_IOAPICs[mp_num_IOAPICs - 1].startGSI +
0233             mp_IOAPICs[mp_num_IOAPICs - 1].numGSIs;
0234 
0235         mp_IOAPICs[mp_num_IOAPICs].numGSIs =
0236           IOAPIC_num_entries();
0237         printf ("  startGSI=0x%X numGSIs=%d\n",
0238                 mp_IOAPICs[mp_num_IOAPICs].startGSI,
0239                 mp_IOAPICs[mp_num_IOAPICs].numGSIs);
0240         mp_num_IOAPICs++;
0241       } else
0242         printf (" (disabled)\n");
0243 
0244       ptr += sizeof (struct mp_config_IO_APIC_entry);
0245       break;
0246 
0247     case MP_CFG_TYPE_IO_INT:   /* IO-Interrupt entry */
0248       printf
0249         ("IO interrupt type: %X flags: %X source: (bus: %X irq: %X) dest: (APIC: %X int: %X)\n",
0250          entry->IO_int.int_type, entry->IO_int.flags,
0251          entry->IO_int.source_bus_id, entry->IO_int.source_bus_irq,
0252          entry->IO_int.dest_APIC_id, entry->IO_int.dest_APIC_intin);
0253 
0254       if (entry->IO_int.source_bus_irq != entry->IO_int.dest_APIC_intin
0255           && entry->IO_int.int_type == 0
0256           /* overriding only applies to ISA IRQs */
0257           && entry->IO_int.source_bus_id == mp_ISA_bus_id) {
0258         /* not sure if this is the right condition */
0259         if (mp_num_overrides == MAX_INT_OVERRIDES)
0260           panic ("Too many interrupt overrides.");
0261         mp_overrides[mp_num_overrides].src_bus = entry->IO_int.source_bus_id;
0262         mp_overrides[mp_num_overrides].src_IRQ = entry->IO_int.source_bus_irq;
0263         mp_overrides[mp_num_overrides].dest_GSI =
0264           IOAPIC_lookup (entry->IO_int.dest_APIC_id)->startGSI +
0265           entry->IO_int.dest_APIC_intin;
0266         mp_num_overrides++;
0267       }
0268       if (entry->IO_int.source_bus_id != mp_ISA_bus_id) {
0269         /* assume it's PCI */
0270         pci_irq_t irq;
0271         irq.bus = entry->IO_int.source_bus_id;
0272         /* Section D.3:
0273          *   SOURCE BUS IRQ bits 0-1 are PCI PIN (counting from 0)
0274          *   SOURCE BUS IRQ bits 2-6 are PCI Device Number */
0275         irq.pin = (entry->IO_int.source_bus_irq & 0x03) + 1;
0276         irq.dev = (entry->IO_int.source_bus_irq & 0x7C) >> 2;
0277         irq.gsi =
0278           IOAPIC_lookup (entry->IO_int.dest_APIC_id)->startGSI +
0279           entry->IO_int.dest_APIC_intin;
0280         switch (entry->IO_int.flags & 0x3) {
0281         case 0: irq.polarity = POLARITY_DEFAULT; break;
0282         case 1: irq.polarity = POLARITY_HIGH; break;
0283         case 3: irq.polarity = POLARITY_LOW; break;
0284         }
0285         switch ((entry->IO_int.flags & 0xC) >> 2) {
0286         case 0: irq.trigger = TRIGGER_DEFAULT; break;
0287         case 1: irq.trigger = TRIGGER_EDGE; break;
0288         case 3: irq.trigger = TRIGGER_LEVEL; break;
0289         }
0290         pci_irq_register (&irq);
0291       }
0292       ptr += sizeof (struct mp_config_interrupt_entry);
0293       break;
0294 
0295     case MP_CFG_TYPE_LOCAL_INT:        /* Local-interrupt entry */
0296       if (!pci_irq_only) {
0297         printf
0298           ("Local interrupt type: %X flags: %X source: (bus: %X irq: %X) dest: (APIC: %X int: %X)\n",
0299            entry->local_int.int_type, entry->local_int.flags,
0300            entry->local_int.source_bus_id, entry->local_int.source_bus_irq,
0301            entry->local_int.dest_APIC_id, entry->local_int.dest_APIC_intin);
0302         /* It's conceivable that local interrupts could be overriden
0303          * like IO interrupts, but I have no good examples of it so I
0304          * will have to defer doing anything about it. */
0305       }
0306       ptr += sizeof (struct mp_config_interrupt_entry);
0307       break;
0308 
0309     default:
0310       printf ("Unknown entry type: %X at address: %p\n", *ptr, ptr);
0311       return 1;
0312     }
0313   }
0314 
0315   return mp_num_cpus;
0316 }
0317 
0318 #undef printf
0319 
0320 /* A small wrapper around smp_boot_cpu() which does some checks and
0321  * maintains two small tables. */
0322 static int
0323 add_processor (struct mp_config_processor_entry *proc)
0324 {
0325 #ifndef NO_SMP
0326   uint8 apic_id = proc->APIC_id;
0327 
0328   if (!(proc->flags & 1))
0329     return 0;                   /* disabled processor */
0330   if (proc->flags & 2)
0331     return 0;                   /* bootstrap processor */
0332 
0333   if (smp_boot_cpu (apic_id, proc->APIC_version)) {
0334     CPU_to_APIC[mp_num_cpus] = apic_id;
0335     APIC_to_CPU[apic_id] = mp_num_cpus;
0336     mp_num_cpus++;
0337     return 1;
0338   } else
0339     return 0;
0340 #else
0341   return 0;
0342 #endif
0343 }
0344 
0345 /* End Intel Multiprocessing Specification implementation */
0346 
0347 /*
0348  * Local Variables:
0349  * indent-tabs-mode: nil
0350  * mode: C
0351  * c-file-style: "gnu"
0352  * c-basic-offset: 2
0353  * End:
0354  */
0355 
0356 /* vi: set et sw=2 sts=2: */