Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:41

0001 /*
0002  * Author: Erich Boleyn  <erich@uruk.org>
0003  *         http://www.uruk.org/~erich/
0004  *
0005  * Copyright (c) 1997-2011 Erich Boleyn.  All rights reserved.
0006  *
0007  * Redistribution and use in source and binary forms, with or without
0008  * modification, are permitted provided that the following conditions
0009  * are met:
0010  * 1. Redistributions of source code must retain the above copyright
0011  *    notice, this list of conditions and the following disclaimer.
0012  * 2. Redistributions in binary form must reproduce the above copyright
0013  *    notice, this list of conditions and the following disclaimer in the
0014  *    documentation and/or other materials provided with the distribution.
0015  * 3. The name of the author may not be used to endorse or promote products
0016  *    derived from this software without specific prior written permission.
0017  *
0018  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0019  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0020  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
0021  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0022  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
0023  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0024  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0025  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0026  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0027  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0028  */
0029 
0030 /*
0031  *  Source file implementing Intel MultiProcessor Specification (MPS)
0032  *  version 1.1 and 1.4 SMP hardware control for Intel Architecture CPUs,
0033  *  with hooks for running correctly on a standard PC without the hardware.
0034  *
0035  *  This file was created from information in the Intel MPS version 1.4
0036  *  document, order number 242016-004, which can be ordered from the
0037  *  Intel literature center.
0038  *
0039  *  General limitations of this code:
0040  *
0041  *   (1) : This code has never been tested on an MPS-compatible system with
0042  *           486 CPUs, but is expected to work.
0043  *   (2) : Presumes "int", "long", and "unsigned" are 32 bits in size, and
0044  *       that 32-bit pointers and memory addressing is used uniformly.
0045  */
0046 
0047 #define _SMP_IMPS_C
0048 
0049 /*
0050  *  Includes here
0051  */
0052 #if 0
0053 #define IMPS_DEBUG
0054 #endif
0055 
0056 #include <bsp/apic.h>
0057 #include <bsp/smp-imps.h>
0058 #include <bsp/irq.h>
0059 #include <rtems/score/smpimpl.h>
0060 
0061 /*
0062  *  XXXXX  The following absolutely must be defined!!!
0063  *
0064  *  The "KERNEL_PRINT" could be made a null macro with no danger, of
0065  *  course, but pretty much nothing would work without the other
0066  *  ones defined.
0067  */
0068 
0069 #if 0
0070 #define KERNEL_PRINT(x)       /* some kind of print function */
0071 #define CMOS_WRITE_BYTE(x,y)  /* write unsigned char "y" at CMOS loc "x" */
0072 #define CMOS_READ_BYTE(x)     /* read unsigned char at CMOS loc "x" */
0073 #define PHYS_TO_VIRTUAL(x)    /* convert physical address "x" to virtual */
0074 #define VIRTUAL_TO_PHYS(x)    /* convert virtual address "x" to physical */
0075 #define UDELAY(x)             /* delay roughly at least "x" microsecs */
0076 #define READ_MSR_LO(x)        /* Read MSR low function */
0077 #else
0078 #include <string.h>
0079 #include <unistd.h>
0080 #include <rtems.h>
0081 #include <rtems/bspIo.h>
0082 #include <rtems/score/cpu.h>
0083 #include <assert.h>
0084 
0085 extern void _pc386_delay(void);
0086 extern uint32_t* gdtdesc;
0087 
0088 static int lapic_dummy = 0;
0089 unsigned imps_lapic_addr = ((unsigned)(&lapic_dummy)) - LAPIC_ID;
0090 
0091 /* #define KERNEL_PRINT(_format)       printk(_format) */
0092 
0093 static void CMOS_WRITE_BYTE(
0094   unsigned int  offset,
0095   unsigned char value
0096 )
0097 {
0098   if ( offset < 128 ) {
0099     outport_byte( 0x70, offset );
0100     outport_byte( 0x71, value );
0101   } else {
0102     outport_byte( 0x72, offset );
0103     outport_byte( 0x73, value );
0104   }
0105 }
0106 
0107 static unsigned char CMOS_READ_BYTE(
0108   unsigned int  offset
0109 )
0110 {
0111   unsigned char value;
0112   if ( offset < 128 ) {
0113     outport_byte( 0x70, offset );
0114     inport_byte( 0x71, value );
0115   } else {
0116     outport_byte( 0x72, offset );
0117     inport_byte( 0x73, value );
0118   }
0119   return value;
0120 }
0121 
0122 #define PHYS_TO_VIRTUAL(_x)    _x
0123 #define VIRTUAL_TO_PHYS(_x)    _x
0124 static void UDELAY(int x)
0125 { int _i = x;
0126   while ( _i-- )
0127     _pc386_delay();
0128 }
0129 
0130 #define READ_MSR_LO(_x) \
0131   (unsigned int)(read_msr(_x) & 0xffffffff)
0132 
0133 static inline unsigned long long read_msr(unsigned int msr)
0134 {
0135   unsigned long long value;
0136 
0137   asm volatile("rdmsr" : "=A" (value) : "c" (msr));
0138   return value;
0139 }
0140 #endif
0141 
0142 /*
0143  *  Defines that are here so as not to be in the global header file.
0144  */
0145 #define EBDA_SEG_ADDR       0x40E
0146 #define BIOS_RESET_VECTOR   0x467
0147 #define LAPIC_ADDR_DEFAULT  0xFEE00000uL
0148 #define IOAPIC_ADDR_DEFAULT 0xFEC00000uL
0149 #define CMOS_RESET_CODE     0xF
0150 #define CMOS_RESET_JUMP     0xa
0151 #define CMOS_BASE_MEMORY    0x15
0152 
0153 /*
0154  *  Static defines here for SMP use.
0155  */
0156 
0157 #define DEF_ENTRIES  23
0158 
0159 static struct {
0160   imps_processor proc[2];
0161   imps_bus bus[2];
0162   imps_ioapic ioapic;
0163   imps_interrupt intin[16];
0164   imps_interrupt lintin[2];
0165 } defconfig = {
0166   { { IMPS_BCT_PROCESSOR, 0, 0, 0, 0, 0},
0167     { IMPS_BCT_PROCESSOR, 1, 0, 0, 0, 0} },
0168   { { IMPS_BCT_BUS, 0, {'E', 'I', 'S', 'A', ' ', ' '}},
0169     { 255, 1, {'P', 'C', 'I', ' ', ' ', ' '}} },
0170   { IMPS_BCT_IOAPIC, 0, 0, IMPS_FLAG_ENABLED, IOAPIC_ADDR_DEFAULT },
0171   { { IMPS_BCT_IO_INTERRUPT, IMPS_INT_EXTINT, 0, 0, 0, 0xFF, 0},
0172     { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 1, 0xFF, 1},
0173     { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 0, 0xFF, 2},
0174     { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 3, 0xFF, 3},
0175     { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 4, 0xFF, 4},
0176     { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 5, 0xFF, 5},
0177     { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 6, 0xFF, 6},
0178     { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 7, 0xFF, 7},
0179     { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 8, 0xFF, 8},
0180     { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 9, 0xFF, 9},
0181     { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 10, 0xFF, 10},
0182     { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 11, 0xFF, 11},
0183     { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 12, 0xFF, 12},
0184     { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 13, 0xFF, 13},
0185     { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 14, 0xFF, 14},
0186     { IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 15, 0xFF, 15} },
0187   { { IMPS_BCT_LOCAL_INTERRUPT, IMPS_INT_EXTINT, 0, 0, 15, 0xFF, 0},
0188     { IMPS_BCT_LOCAL_INTERRUPT, IMPS_INT_NMI, 0, 0, 15, 0xFF, 1} }
0189 };
0190 
0191 /*
0192  *  Exported globals here.
0193  */
0194 
0195 volatile int imps_release_cpus = 0;
0196 int imps_enabled = 0;
0197 int imps_num_cpus = 1;
0198 unsigned char imps_cpu_apic_map[IMPS_MAX_CPUS];
0199 unsigned char imps_apic_cpu_map[IMPS_MAX_CPUS];
0200 
0201 /* now defined in getcpuid.c */
0202 extern unsigned imps_lapic_addr;
0203 
0204 static void secondary_cpu_initialize(void);
0205 
0206 /*
0207  *  MPS checksum function
0208  *
0209  *  Function finished.
0210  */
0211 static int
0212 get_checksum(unsigned start, int length)
0213 {
0214   unsigned sum = 0;
0215 
0216   while (length-- > 0) {
0217     sum += *((unsigned char *) (start++));
0218   }
0219 
0220   return (sum&0xFF);
0221 }
0222 
0223 /*
0224  *  APIC ICR write and status check function.
0225  */
0226 int
0227 send_ipi(unsigned int dst, unsigned int v)
0228 {
0229   int to, send_status, apicid;
0230 
0231   apicid = imps_cpu_apic_map[dst];
0232 
0233   IMPS_LAPIC_WRITE(LAPIC_ICR+0x10, (apicid << 24));
0234   IMPS_LAPIC_WRITE(LAPIC_ICR, v);
0235 
0236   /* Wait for send to finish */
0237   to = 0;
0238   do {
0239     UDELAY(100);
0240     send_status = IMPS_LAPIC_READ(LAPIC_ICR) & LAPIC_ICR_STATUS_PEND;
0241   } while (send_status && (to++ < 1000));
0242 
0243   return (to < 1000);
0244 }
0245 
0246 /*
0247  *  Primary function for booting individual CPUs.
0248  *
0249  *  This must be modified to perform whatever OS-specific initialization
0250  *  that is required.
0251  */
0252 static int
0253 boot_cpu(imps_processor *proc)
0254 {
0255   int apicid = proc->apic_id, success = 1;
0256   int cpuid;
0257   unsigned bootaddr;
0258   unsigned bios_reset_vector = PHYS_TO_VIRTUAL(BIOS_RESET_VECTOR);
0259 
0260   cpuid = imps_apic_cpu_map[apicid];
0261   /*
0262    * Copy boot code for secondary CPUs here.  Find it in between
0263    * "patch_code_start" and "patch_code_end" symbols.  The other CPUs
0264    * will start there in 16-bit real mode under the 1MB boundary.
0265    * "patch_code_start" should be placed at a 4K-aligned address
0266    * under the 1MB boundary.
0267    */
0268 
0269   volatile uint32_t *reset;
0270 
0271   bootaddr = (512-64)*1024;
0272   reset= (volatile uint32_t *)bootaddr;
0273 
0274   memcpy(
0275     (char *) bootaddr,
0276     _binary_appstart_bin_start,
0277     (size_t)_binary_appstart_bin_size
0278   );
0279 
0280   /* Pass start function, stack region and gdtdescr to AP
0281    * see startAP.S for location */
0282   reset[1] = (uint32_t)secondary_cpu_initialize;
0283   reset[2] = (uint32_t)_Per_CPU_Get_by_index(cpuid)->interrupt_stack_high;
0284   memcpy(
0285     (char*) &reset[3],
0286     &gdtdesc,
0287     6);
0288   /*
0289    *  Generic CPU startup sequence starts here.
0290    */
0291 
0292   /* set BIOS reset vector */
0293   CMOS_WRITE_BYTE(CMOS_RESET_CODE, CMOS_RESET_JUMP);
0294   *((volatile unsigned *) bios_reset_vector) = ((bootaddr & 0xFF000) << 12);
0295 
0296   /* clear the APIC error register */
0297   IMPS_LAPIC_WRITE(LAPIC_ESR, 0);
0298   IMPS_LAPIC_READ(LAPIC_ESR);
0299 
0300   /* assert INIT IPI */
0301   send_ipi(
0302     cpuid,
0303     LAPIC_ICR_TM_LEVEL | LAPIC_ICR_LEVELASSERT | LAPIC_ICR_DM_INIT
0304   );
0305   UDELAY(10000);
0306 
0307   /* de-assert INIT IPI */
0308   send_ipi(cpuid, LAPIC_ICR_TM_LEVEL | LAPIC_ICR_DM_INIT);
0309 
0310   UDELAY(10000);
0311 
0312   /*
0313    *  Send Startup IPIs if not an old pre-integrated APIC.
0314    */
0315 
0316   if (proc->apic_ver >= APIC_VER_NEW) {
0317     int i;
0318     for (i = 1; i <= 2; i++) {
0319       send_ipi(cpuid, LAPIC_ICR_DM_SIPI | ((bootaddr >> 12) & 0xFF));
0320       UDELAY(1000);
0321     }
0322   }
0323 
0324   /*
0325    *  Wait until AP is in protected mode before starting the next AP
0326    */
0327   while (reset[2] != 0);
0328 
0329   /*
0330    *  Generic CPU startup sequence ends here, the rest is cleanup.
0331    */
0332 
0333   /* clear the APIC error register */
0334   IMPS_LAPIC_WRITE(LAPIC_ESR, 0);
0335   IMPS_LAPIC_READ(LAPIC_ESR);
0336 
0337   /* clean up BIOS reset vector */
0338   CMOS_WRITE_BYTE(CMOS_RESET_CODE, 0);
0339   *((volatile unsigned *) bios_reset_vector) = 0;
0340 
0341   return success;
0342 }
0343 
0344 /*
0345  *  read bios stuff and fill tables
0346  */
0347 static void
0348 add_processor(imps_processor *proc)
0349 {
0350   int apicid = proc->apic_id;
0351 
0352   printk("  Processor [APIC id %d ver %d]: ", apicid, proc->apic_ver);
0353   if (!(proc->flags & IMPS_FLAG_ENABLED)) {
0354     printk("DISABLED\n");
0355     return;
0356   }
0357   if (proc->flags & (IMPS_CPUFLAG_BOOT)) {
0358     printk("#0  BootStrap Processor (BSP)\n");
0359     return;
0360   }
0361   /* Setup the apic/cpu maps before booting the APs
0362    * otherwise calls to _Get_current_processor can deliver
0363    * wrong values if the BSP gets interrupted
0364    */
0365   imps_cpu_apic_map[imps_num_cpus] = apicid;
0366   imps_apic_cpu_map[apicid] = imps_num_cpus;
0367   if (boot_cpu(proc)) {
0368 
0369     /*  XXXXX  add OS-specific setup for secondary CPUs here */
0370 
0371     /* AP booted successfully, increase number of available cores */
0372     imps_num_cpus++;
0373     printk("#%d  Application Processor (AP)\n", imps_apic_cpu_map[apicid]);
0374   }
0375 }
0376 
0377 
0378 static void
0379 add_bus(imps_bus *bus)
0380 {
0381   char str[8];
0382 
0383   memcpy(str, bus->bus_type, 6);
0384   str[6] = 0;
0385   printk("  Bus id %d is %s\n", bus->id, str);
0386 
0387   /*  XXXXX  add OS-specific code here */
0388 }
0389 
0390 static void
0391 add_ioapic(imps_ioapic *ioapic)
0392 {
0393   printk("  I/O APIC id %d ver %d, address: 0x%x  ",
0394           ioapic->id, ioapic->ver, ioapic->addr);
0395   if (!(ioapic->flags & IMPS_FLAG_ENABLED)) {
0396     printk("DISABLED\n");
0397     return;
0398   }
0399   printk("\n");
0400 
0401   /*  XXXXX  add OS-specific code here */
0402 }
0403 
0404 static void
0405 imps_read_config_table(unsigned start, int count)
0406 {
0407   while (count-- > 0) {
0408     switch (*((unsigned char *)start)) {
0409     case IMPS_BCT_PROCESSOR:
0410       if ( imps_num_cpus < rtems_configuration_get_maximum_processors() ) {
0411         if (_SMP_Should_start_processor((uint32_t) imps_num_cpus)) {
0412           add_processor((imps_processor *)start);
0413         }
0414       } else
0415         imps_num_cpus++;
0416       start += 12;  /* 20 total */
0417       break;
0418     case IMPS_BCT_BUS:
0419       add_bus((imps_bus *)start);
0420       break;
0421     case IMPS_BCT_IOAPIC:
0422       add_ioapic((imps_ioapic *)start);
0423       break;
0424 #if 0  /*  XXXXX  uncomment this if "add_io_interrupt" is implemented */
0425     case IMPS_BCT_IO_INTERRUPT:
0426       add_io_interrupt((imps_interrupt *)start);
0427       break;
0428 #endif
0429 #if 0  /*  XXXXX  uncomment this if "add_local_interrupt" is implemented */
0430     case IMPS_BCT_LOCAL_INTERRUPT:
0431       add_local_interupt((imps_interrupt *)start);
0432       break;
0433 #endif
0434     default:
0435       break;
0436     }
0437     start += 8;
0438   }
0439   if ( imps_num_cpus > rtems_configuration_get_maximum_processors() ) {
0440     printk(
0441       "WARNING!! Found more CPUs (%d) than configured for (%d)!!\n",
0442       imps_num_cpus - 1,
0443       rtems_configuration_get_maximum_processors()
0444     );
0445     imps_num_cpus = rtems_configuration_get_maximum_processors();
0446     return;
0447   }
0448 }
0449 
0450 static int
0451 imps_bad_bios(imps_fps *fps_ptr)
0452 {
0453   int sum;
0454   imps_cth *local_cth_ptr
0455     = (imps_cth *) PHYS_TO_VIRTUAL(fps_ptr->cth_ptr);
0456 
0457   if (fps_ptr->feature_info[0] > IMPS_FPS_DEFAULT_MAX) {
0458     printk("    Invalid MP System Configuration type %d\n",
0459             fps_ptr->feature_info[0]);
0460     return 1;
0461   }
0462 
0463   if (fps_ptr->cth_ptr) {
0464     sum = get_checksum((unsigned)local_cth_ptr,
0465                                    local_cth_ptr->base_length);
0466     if (local_cth_ptr->sig != IMPS_CTH_SIGNATURE || sum) {
0467       printk(
0468         "    Bad MP Config Table sig 0x%x and/or checksum 0x%x\n",
0469         (unsigned)(fps_ptr->cth_ptr),
0470         sum
0471       );
0472       return 1;
0473     }
0474     if (local_cth_ptr->spec_rev != fps_ptr->spec_rev) {
0475       printk(
0476         "    Bad MP Config Table sub-revision # %d\n",
0477         local_cth_ptr->spec_rev
0478       );
0479       return 1;
0480     }
0481     if (local_cth_ptr->extended_length) {
0482       sum = (get_checksum(((unsigned)local_cth_ptr)
0483               + local_cth_ptr->base_length,
0484               local_cth_ptr->extended_length)
0485              + local_cth_ptr->extended_checksum) & 0xFF;
0486       if (sum) {
0487         printk("    Bad Extended MP Config Table checksum 0x%x\n", sum);
0488         return 1;
0489       }
0490     }
0491   } else if (!fps_ptr->feature_info[0]) {
0492     printk("    Missing configuration information\n");
0493     return 1;
0494   }
0495 
0496   return 0;
0497 }
0498 
0499 static void
0500 imps_read_bios(imps_fps *fps_ptr)
0501 {
0502   int apicid;
0503   unsigned cth_start, cth_count;
0504   imps_cth *local_cth_ptr
0505     = (imps_cth *)PHYS_TO_VIRTUAL(fps_ptr->cth_ptr);
0506   char *str_ptr;
0507 
0508   printk("Intel MultiProcessor Spec 1.%d BIOS support detected\n",
0509           fps_ptr->spec_rev);
0510 
0511   /*
0512    *  Do all checking of errors which would definitely
0513    *  lead to failure of the SMP boot here.
0514    */
0515   if (imps_bad_bios(fps_ptr)) {
0516     printk("    Disabling MPS support\n");
0517     return;
0518   }
0519 
0520   if (fps_ptr->feature_info[1] & IMPS_FPS_IMCRP_BIT) {
0521     str_ptr = "IMCR and PIC";
0522   } else {
0523     str_ptr = "Virtual Wire";
0524   }
0525   if (fps_ptr->cth_ptr) {
0526     imps_lapic_addr = local_cth_ptr->lapic_addr;
0527   } else {
0528     imps_lapic_addr = LAPIC_ADDR_DEFAULT;
0529   }
0530   printk("    APIC config: \"%s mode\"    Local APIC address: 0x%x\n",
0531           str_ptr, imps_lapic_addr);
0532   if (imps_lapic_addr != (READ_MSR_LO(0x1b) & 0xFFFFF000)) {
0533     printk("Inconsistent Local APIC address, Disabling SMP support\n");
0534     return;
0535   }
0536   imps_lapic_addr = PHYS_TO_VIRTUAL(imps_lapic_addr);
0537 
0538   /*
0539    *  Setup primary CPU.
0540    */
0541   apicid = IMPS_LAPIC_READ(LAPIC_SPIV);
0542   IMPS_LAPIC_WRITE(LAPIC_SPIV, apicid|LAPIC_SPIV_ENABLE_APIC);
0543   apicid = APIC_ID(IMPS_LAPIC_READ(LAPIC_ID));
0544   imps_cpu_apic_map[0] = apicid;
0545   imps_apic_cpu_map[apicid] = 0;
0546 
0547   if (fps_ptr->cth_ptr) {
0548     char str1[16], str2[16];
0549     memcpy(str1, local_cth_ptr->oem_id, 8);
0550     str1[8] = 0;
0551     memcpy(str2, local_cth_ptr->prod_id, 12);
0552     str2[12] = 0;
0553     printk("  OEM id: %s  Product id: %s\n", str1, str2);
0554     cth_start = ((unsigned) local_cth_ptr) + sizeof(imps_cth);
0555     cth_count = local_cth_ptr->entry_count;
0556   } else {
0557     *((volatile unsigned *) IOAPIC_ADDR_DEFAULT) =  IOAPIC_ID;
0558     defconfig.ioapic.id
0559       = APIC_ID(*((volatile unsigned *)
0560             (IOAPIC_ADDR_DEFAULT+IOAPIC_RW)));
0561     *((volatile unsigned *) IOAPIC_ADDR_DEFAULT) =  IOAPIC_VER;
0562     defconfig.ioapic.ver
0563       = APIC_VERSION(*((volatile unsigned *)
0564            (IOAPIC_ADDR_DEFAULT+IOAPIC_RW)));
0565     defconfig.proc[apicid].flags
0566       = IMPS_FLAG_ENABLED|IMPS_CPUFLAG_BOOT;
0567     defconfig.proc[!apicid].flags = IMPS_FLAG_ENABLED;
0568     imps_num_cpus = 2;
0569     if (fps_ptr->feature_info[0] == 1
0570      || fps_ptr->feature_info[0] == 5) {
0571       memcpy(defconfig.bus[0].bus_type, "ISA   ", 6);
0572     }
0573     if (fps_ptr->feature_info[0] == 4
0574      || fps_ptr->feature_info[0] == 7) {
0575       memcpy(defconfig.bus[0].bus_type, "MCA   ", 6);
0576     }
0577     if (fps_ptr->feature_info[0] > 4) {
0578       defconfig.proc[0].apic_ver = 0x10;
0579       defconfig.proc[1].apic_ver = 0x10;
0580       defconfig.bus[1].type = IMPS_BCT_BUS;
0581     }
0582     if (fps_ptr->feature_info[0] == 2) {
0583       defconfig.intin[2].type = 255;
0584       defconfig.intin[13].type = 255;
0585     }
0586     if (fps_ptr->feature_info[0] == 7) {
0587       defconfig.intin[0].type = 255;
0588     }
0589     cth_start = (unsigned) &defconfig;
0590     cth_count = DEF_ENTRIES;
0591   }
0592   imps_read_config_table(cth_start, cth_count);
0593 
0594   /* %%%%% ESB read extended entries here */
0595 
0596   imps_enabled = 1;
0597 }
0598 
0599 /*
0600  *  Given a region to check, this actually looks for the "MP Floating
0601  *  Pointer Structure".  The return value indicates if the correct
0602  *  signature and checksum for a floating pointer structure of the
0603  *  appropriate spec revision was found.  If so, then do not search
0604  *  further.
0605  *
0606  *  NOTE:  The memory scan will always be in the bottom 1 MB.
0607  *
0608  *  This function presumes that "start" will always be aligned to a 16-bit
0609  *  boundary.
0610  *
0611  *  Function finished.
0612  */
0613 static int
0614 imps_scan(unsigned start, unsigned length)
0615 {
0616   printk("Scanning from 0x%x for %d bytes\n", start, length);
0617 
0618   while (length > 0) {
0619     imps_fps *fps_ptr = (imps_fps *) PHYS_TO_VIRTUAL(start);
0620 
0621     if (fps_ptr->sig == IMPS_FPS_SIGNATURE
0622      && fps_ptr->length == 1
0623      && (fps_ptr->spec_rev == 1 || fps_ptr->spec_rev == 4)
0624      && !get_checksum(start, 16)) {
0625       printk("Found MP Floating Structure Pointer at %x\n", start);
0626       imps_read_bios(fps_ptr);
0627       return 1;
0628     }
0629 
0630     length -= 16;
0631     start += 16;
0632   }
0633 
0634   return 0;
0635 }
0636 
0637 #if !defined(__rtems__)
0638 /*
0639  *  This is the primary function to "force" SMP support, with
0640  *  the assumption that you have consecutively numbered APIC ids.
0641  */
0642 int
0643 imps_force(int ncpus)
0644 {
0645   int apicid, i;
0646   imps_processor p;
0647 
0648   printk("Intel MultiProcessor \"Force\" Support\n");
0649 
0650   imps_lapic_addr = (READ_MSR_LO(0x1b) & 0xFFFFF000);
0651   imps_lapic_addr = PHYS_TO_VIRTUAL(imps_lapic_addr);
0652 
0653   /*
0654    *  Setup primary CPU.
0655    */
0656   apicid = IMPS_LAPIC_READ(LAPIC_SPIV);
0657   IMPS_LAPIC_WRITE(LAPIC_SPIV, apicid|LAPIC_SPIV_ENABLE_APIC);
0658   apicid = APIC_ID(IMPS_LAPIC_READ(LAPIC_ID));
0659   imps_cpu_apic_map[0] = apicid;
0660   imps_apic_cpu_map[apicid] = 0;
0661 
0662   p.type = 0;
0663   p.apic_ver = 0x10;
0664   p.signature = p.features = 0;
0665 
0666   for (i = 0; i < ncpus; i++) {
0667     if (apicid == i) {
0668       p.flags = IMPS_FLAG_ENABLED | IMPS_CPUFLAG_BOOT;
0669     } else {
0670       p.flags = IMPS_FLAG_ENABLED;
0671     }
0672     p.apic_id = i;
0673     add_processor(&p);
0674   }
0675 
0676   return imps_num_cpus;
0677 }
0678 #endif
0679 
0680 /*
0681  *  This is the primary function for probing for MPS compatible hardware
0682  *  and BIOS information.  Call this during the early stages of OS startup,
0683  *  before memory can be messed up.
0684  *
0685  *  The probe looks for the "MP Floating Pointer Structure" at locations
0686  *  listed at the top of page 4-2 of the spec.
0687  *
0688  *  Environment requirements from the OS to run:
0689  *
0690  *   (1) : A non-linear virtual to physical memory mapping is probably OK,
0691  *       as (I think) the structures all fall within page boundaries,
0692  *       but a linear mapping is recommended.  Currently assumes that
0693  *       the mapping will remain identical over time (which should be
0694  *       OK since it only accesses memory which shouldn't be munged
0695  *       by the OS anyway).
0696  *   (2) : The OS only consumes memory which the BIOS says is OK to use,
0697  *       and not any of the BIOS standard areas (the areas 0x400 to
0698  *       0x600, the EBDA, 0xE0000 to 0xFFFFF, and unreported physical
0699  *       RAM).  Sometimes a small amount of physical RAM is not
0700  *       reported by the BIOS, to be used to store MPS and other
0701  *       information.
0702  *   (3) : It must be possible to read the CMOS.
0703  *   (4) : There must be between 512K and 640K of lower memory (this is a
0704  *       sanity check).
0705  *
0706  *  Function finished.
0707  */
0708 int
0709 imps_probe(void)
0710 {
0711   /*
0712    *  Determine possible address of the EBDA
0713    */
0714   unsigned ebda_addr = *((unsigned short *)
0715              PHYS_TO_VIRTUAL(EBDA_SEG_ADDR)) << 4;
0716 
0717   /*
0718    *  Determine amount of installed lower memory (not *available*
0719    *  lower memory).
0720    *
0721    *  NOTE:  This should work reliably as long as we verify the
0722    *         machine is at least a system that could possibly have
0723    *         MPS compatibility to begin with.
0724    */
0725   unsigned mem_lower = ((CMOS_READ_BYTE(CMOS_BASE_MEMORY+1) << 8)
0726             | CMOS_READ_BYTE(CMOS_BASE_MEMORY))       << 10;
0727 
0728 #ifdef IMPS_DEBUG
0729   imps_enabled = 0;
0730   imps_num_cpus = 1;
0731 #endif
0732 
0733   /*
0734    *  Sanity check : if this isn't reasonable, it is almost impossibly
0735    *    unlikely to be an MPS compatible machine, so return failure.
0736    */
0737   if (mem_lower < 512*1024 || mem_lower > 640*1024) {
0738     return 0;
0739   }
0740 
0741   if (ebda_addr > mem_lower - 1024
0742    || ebda_addr + *((unsigned char *) PHYS_TO_VIRTUAL(ebda_addr))
0743          * 1024 > mem_lower) {
0744     ebda_addr = 0;
0745   }
0746 
0747   if (((ebda_addr && imps_scan(ebda_addr, 1024))
0748    || (!ebda_addr && imps_scan(mem_lower - 1024, 1024))
0749    || imps_scan(0xF0000, 0x10000)) && imps_enabled) {
0750     return imps_num_cpus;
0751   }
0752 
0753   /*
0754    *  If no BIOS info on MPS hardware is found, then return failure.
0755    */
0756 
0757   return 0;
0758 }
0759 
0760 /*
0761  *  RTEMS SMP BSP Support
0762  */
0763 static void smp_apic_ack(void)
0764 {
0765   (void) IMPS_LAPIC_READ(LAPIC_SPIV);  /* dummy read */
0766   IMPS_LAPIC_WRITE(LAPIC_EOI, 0 );     /* ACK the interrupt */
0767 }
0768 
0769 static void bsp_inter_processor_interrupt(void *arg)
0770 {
0771   (void) arg;
0772 
0773   smp_apic_ack();
0774 
0775   /*
0776    * Disallow nesting.
0777    */
0778    __asm__ __volatile__("cli");
0779 
0780   _SMP_Inter_processor_interrupt_handler(_Per_CPU_Get());
0781 
0782    /*
0783    * Allow nesting.
0784    */
0785    __asm__ __volatile__("sti");
0786 }
0787 
0788 void
0789 ipi_install_irq(void)
0790 {
0791   rtems_status_code status;
0792 
0793   status = rtems_interrupt_handler_install(
0794     16,
0795     "smp-imps",
0796     RTEMS_INTERRUPT_UNIQUE,
0797     bsp_inter_processor_interrupt,
0798     NULL
0799   );
0800   assert(status == RTEMS_SUCCESSFUL);
0801 }
0802 
0803 #ifdef __SSE__
0804 extern void enable_sse(void);
0805 #endif
0806 
0807 /* pc386 specific initialization */
0808 static void secondary_cpu_initialize(void)
0809 {
0810   int apicid;
0811 
0812   asm volatile( "lidt IDT_Descriptor" );
0813 
0814   apicid = IMPS_LAPIC_READ(LAPIC_SPIV);
0815   IMPS_LAPIC_WRITE(LAPIC_SPIV, apicid|LAPIC_SPIV_ENABLE_APIC);
0816 
0817 #ifdef __SSE__
0818   enable_sse();
0819 #endif
0820 
0821   _SMP_Start_multitasking_on_secondary_processor( _Per_CPU_Get() );
0822 }