Home | History | Annotate | Download | only in stage2
      1 /*
      2  *  GRUB  --  GRand Unified Bootloader
      3  *  Copyright (C) 1999,2005  Free Software Foundation, Inc.
      4  *
      5  *  This program is free software; you can redistribute it and/or modify
      6  *  it under the terms of the GNU General Public License as published by
      7  *  the Free Software Foundation; either version 2 of the License, or
      8  *  (at your option) any later version.
      9  *
     10  *  This program is distributed in the hope that it will be useful,
     11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  *  GNU General Public License for more details.
     14  *
     15  *  You should have received a copy of the GNU General Public License
     16  *  along with this program; if not, write to the Free Software
     17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     18  */
     19 
     20 /*
     21  *  <Insert copyright here : it must be BSD-like so anyone can use it>
     22  *
     23  *  Author:  Erich Boleyn  <erich (at) uruk.org>   http://www.uruk.org/~erich/
     24  *
     25  *  Source file implementing Intel MultiProcessor Specification (MPS)
     26  *  version 1.1 and 1.4 SMP hardware control for Intel Architecture CPUs,
     27  *  with hooks for running correctly on a standard PC without the hardware.
     28  *
     29  *  This file was created from information in the Intel MPS version 1.4
     30  *  document, order number 242016-004, which can be ordered from the
     31  *  Intel literature center.
     32  *
     33  *  General limitations of this code:
     34  *
     35  *   (1) : This code has never been tested on an MPS-compatible system with
     36  *           486 CPUs, but is expected to work.
     37  *   (2) : Presumes "int", "long", and "unsigned" are 32 bits in size, and
     38  *	     that 32-bit pointers and memory addressing is used uniformly.
     39  */
     40 
     41 #define _SMP_IMPS_C
     42 
     43 
     44 /*
     45  *  XXXXX  The following absolutely must be defined!!!
     46  *
     47  *  The "KERNEL_PRINT" could be made a null macro with no danger, of
     48  *  course, but pretty much nothing would work without the other
     49  *  ones defined.
     50  */
     51 
     52 #if 0
     53 #define KERNEL_PRINT(x)		/* some kind of print function */
     54 #define CMOS_WRITE_BYTE(x,y)	/* write unsigned char "y" at CMOS loc "x" */
     55 #define CMOS_READ_BYTE(x)	/* read unsigned char at CMOS loc "x" */
     56 #define PHYS_TO_VIRTUAL(x)	/* convert physical address "x" to virtual */
     57 #define VIRTUAL_TO_PHYS(x)	/* convert virtual address "x" to physical */
     58 #endif
     59 
     60 
     61 /*
     62  *  This is the Intel MultiProcessor Spec debugging/display code.
     63  */
     64 
     65 #define IMPS_DEBUG
     66 #define KERNEL_PRINT(x)         printf x
     67 #define CMOS_WRITE_BYTE(x, y)	cmos_write_byte(x, y)
     68 #define CMOS_READ_BYTE(x)	cmos_read_byte(x)
     69 #define PHYS_TO_VIRTUAL(x)	(x)
     70 #define VIRTUAL_TO_PHYS(x)	(x)
     71 
     72 static inline unsigned char
     73 inb (unsigned short port)
     74 {
     75   unsigned char data;
     76 
     77   __asm __volatile ("inb %1,%0" :"=a" (data):"d" (port));
     78   return data;
     79 }
     80 
     81 static inline void
     82 outb (unsigned short port, unsigned char val)
     83 {
     84   __asm __volatile ("outb %0,%1"::"a" (val), "d" (port));
     85 }
     86 
     87 
     88 static inline void
     89 cmos_write_byte (int loc, int val)
     90 {
     91   outb (0x70, loc);
     92   outb (0x71, val);
     93 }
     94 
     95 static inline unsigned
     96 cmos_read_byte (int loc)
     97 {
     98   outb (0x70, loc);
     99   return inb (0x71);
    100 }
    101 
    102 
    103 /*
    104  *  Includes here
    105  */
    106 
    107 #include "shared.h"
    108 #include "apic.h"
    109 #include "smp-imps.h"
    110 
    111 
    112 /*
    113  *  Defines that are here so as not to be in the global header file.
    114  */
    115 #define EBDA_SEG_ADDR			0x40E
    116 #define BIOS_RESET_VECTOR		0x467
    117 #define LAPIC_ADDR_DEFAULT		0xFEE00000uL
    118 #define IOAPIC_ADDR_DEFAULT		0xFEC00000uL
    119 #define CMOS_RESET_CODE			0xF
    120 #define		CMOS_RESET_JUMP		0xa
    121 #define CMOS_BASE_MEMORY		0x15
    122 
    123 
    124 /*
    125  *  Static defines here for SMP use.
    126  */
    127 
    128 #define DEF_ENTRIES	23
    129 
    130 static int lapic_dummy = 0;
    131 static struct
    132   {
    133     imps_processor proc[2];
    134     imps_bus bus[2];
    135     imps_ioapic ioapic;
    136     imps_interrupt intin[16];
    137     imps_interrupt lintin[2];
    138   }
    139 defconfig =
    140 {
    141   {
    142     {
    143       IMPS_BCT_PROCESSOR, 0, 0, 0, 0, 0
    144     }
    145     ,
    146     {
    147       IMPS_BCT_PROCESSOR, 1, 0, 0, 0, 0
    148     }
    149   }
    150   ,
    151   {
    152     {
    153       IMPS_BCT_BUS, 0,
    154       {
    155 	'E', 'I', 'S', 'A', ' ', ' '
    156       }
    157     }
    158     ,
    159     {
    160       255, 1,
    161       {
    162 	'P', 'C', 'I', ' ', ' ', ' '
    163       }
    164     }
    165   }
    166   ,
    167   {
    168     IMPS_BCT_IOAPIC, 0, 0, IMPS_FLAG_ENABLED, IOAPIC_ADDR_DEFAULT
    169   }
    170   ,
    171   {
    172     {
    173       IMPS_BCT_IO_INTERRUPT, IMPS_INT_EXTINT, 0, 0, 0, 0xFF, 0
    174     }
    175     ,
    176     {
    177       IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 1, 0xFF, 1
    178     }
    179     ,
    180     {
    181       IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 0, 0xFF, 2
    182     }
    183     ,
    184     {
    185       IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 3, 0xFF, 3
    186     }
    187     ,
    188     {
    189       IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 4, 0xFF, 4
    190     }
    191     ,
    192     {
    193       IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 5, 0xFF, 5
    194     }
    195     ,
    196     {
    197       IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 6, 0xFF, 6
    198     }
    199     ,
    200     {
    201       IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 7, 0xFF, 7
    202     }
    203     ,
    204     {
    205       IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 8, 0xFF, 8
    206     }
    207     ,
    208     {
    209       IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 9, 0xFF, 9
    210     }
    211     ,
    212     {
    213       IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 10, 0xFF, 10
    214     }
    215     ,
    216     {
    217       IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 11, 0xFF, 11
    218     }
    219     ,
    220     {
    221       IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 12, 0xFF, 12
    222     }
    223     ,
    224     {
    225       IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 13, 0xFF, 13
    226     }
    227     ,
    228     {
    229       IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 14, 0xFF, 14
    230     }
    231     ,
    232     {
    233       IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 15, 0xFF, 15
    234     }
    235   }
    236   ,
    237   {
    238     {
    239       IMPS_BCT_LOCAL_INTERRUPT, IMPS_INT_EXTINT, 0, 0, 15, 0xFF, 0
    240     }
    241     ,
    242     {
    243       IMPS_BCT_LOCAL_INTERRUPT, IMPS_INT_NMI, 0, 0, 15, 0xFF, 1
    244     }
    245   }
    246 };
    247 
    248 /*
    249  *  Exported globals here.
    250  */
    251 
    252 /*
    253  *  "imps_any_new_apics" is non-zero if any of the APICS (local or I/O)
    254  *  are *not* an 82489DX.  This is useful to determine if more than 15
    255  *  CPUs can be supported (true if zero).
    256  */
    257 static int imps_any_new_apics = 0;
    258 #if 0
    259 volatile int imps_release_cpus = 0;
    260 #endif
    261 /*
    262  *  "imps_enabled" is non-zero if the probe sequence found IMPS
    263  *  information and was successful.
    264  */
    265 static int imps_enabled = 0;
    266 /*
    267  *  This represents the number of CPUs found.
    268  */
    269 static int imps_num_cpus = 1;
    270 /*
    271  *  This contains the local APIC hardware address.
    272  */
    273 static unsigned imps_lapic_addr = ((unsigned) (&lapic_dummy)) - LAPIC_ID;
    274 /*
    275  *  These map from virtual cpu numbers to APIC id's and back.
    276  */
    277 static unsigned char imps_cpu_apic_map[IMPS_MAX_CPUS];
    278 static unsigned char imps_apic_cpu_map[IMPS_MAX_CPUS];
    279 
    280 
    281 /*
    282  *  MPS checksum function
    283  *
    284  *  Function finished.
    285  */
    286 
    287 static int
    288 get_checksum (unsigned start, int length)
    289 {
    290   unsigned sum = 0;
    291 
    292   while (length-- > 0)
    293     {
    294       sum += *((unsigned char *) (start++));
    295     }
    296 
    297   return (sum & 0xFF);
    298 }
    299 
    300 
    301 /*
    302  *  Primary function for booting individual CPUs.
    303  *
    304  *  This must be modified to perform whatever OS-specific initialization
    305  *  that is required.
    306  */
    307 
    308 static int
    309 boot_cpu (imps_processor * proc)
    310 {
    311   unsigned bootaddr, accept_status;
    312   unsigned bios_reset_vector = PHYS_TO_VIRTUAL (BIOS_RESET_VECTOR);
    313 
    314   /* %%%%% ESB */
    315   extern char patch_code[];
    316   bootaddr = 256 * 1024;
    317   memmove ((char *) bootaddr, patch_code, 32);
    318 
    319   /*
    320    *  Generic CPU startup sequence starts here.
    321    */
    322 
    323   /* set BIOS reset vector */
    324   CMOS_WRITE_BYTE (CMOS_RESET_CODE, CMOS_RESET_JUMP);
    325   *((volatile unsigned *) bios_reset_vector) = bootaddr << 12;
    326 
    327   /* clear the error register */
    328   if (proc->apic_ver & 0x10)
    329     {
    330       IMPS_LAPIC_WRITE (LAPIC_ESR, 0);
    331       accept_status = IMPS_LAPIC_READ (LAPIC_ESR);
    332     }
    333 
    334 #if 0
    335   /* assert INIT IPI */
    336   cfg = IMPS_LAPIC_READ (LAPIC_ICR + 1);
    337   cfg &= LAPIC_DEST_MASK;
    338   IMPS_LAPIC_WRITE (LAPIC_ICR + 1, cfg);
    339   cfg = IMPS_LAPIC_READ (LAPIC_ACR);
    340   cfg &=;
    341 
    342   /* %%%%% ESB finish adding startup sequence */
    343 #endif
    344 
    345   /* clean up BIOS reset vector */
    346   CMOS_WRITE_BYTE (CMOS_RESET_CODE, 0);
    347   *((volatile unsigned *) bios_reset_vector) = 0;
    348 
    349   /*
    350    *  Generic CPU startup sequence ends here.
    351    */
    352 
    353   KERNEL_PRINT (("\n"));
    354 
    355   return 1;
    356 
    357   /* XXXXX add OS-specific initialization here! */
    358 }
    359 
    360 
    361 /*
    362  *  read bios stuff and fill tables
    363  */
    364 
    365 static void
    366 add_processor (imps_processor * proc)
    367 {
    368   int apicid = proc->apic_id;
    369 
    370   KERNEL_PRINT (("  Processor [APIC id %d ver %d]:  ",
    371 		 apicid, proc->apic_ver));
    372   if (!(proc->flags & IMPS_FLAG_ENABLED))
    373     {
    374       KERNEL_PRINT (("DISABLED\n"));
    375       return;
    376     }
    377   if (proc->apic_ver > 0xF)
    378     {
    379       imps_any_new_apics = 1;
    380     }
    381   if (proc->flags & (IMPS_CPUFLAG_BOOT))
    382     {
    383       KERNEL_PRINT (("#0  Bootstrap Processor (BSP)\n"));
    384       return;
    385     }
    386   imps_cpu_apic_map[imps_num_cpus] = apicid;
    387   imps_apic_cpu_map[apicid] = imps_num_cpus;
    388   if (boot_cpu (proc))
    389     {
    390 
    391       /*  XXXXX  add OS-specific setup for secondary CPUs here */
    392 
    393       imps_num_cpus++;
    394     }
    395 }
    396 
    397 
    398 static void
    399 add_bus (imps_bus * bus)
    400 {
    401   char str[8];
    402 
    403   memmove (str, bus->bus_type, 6);
    404   str[6] = 0;
    405   KERNEL_PRINT (("  Bus id %d is %s\n", bus->id, str));
    406 
    407   /*  XXXXX  add OS-specific code here */
    408 }
    409 
    410 
    411 static void
    412 add_ioapic (imps_ioapic * ioapic)
    413 {
    414   KERNEL_PRINT (("  I/O APIC id %d ver %d, address: 0x%x  ",
    415 		 ioapic->id, ioapic->ver, ioapic->addr));
    416   if (!(ioapic->flags & IMPS_FLAG_ENABLED))
    417     {
    418       KERNEL_PRINT (("DISABLED\n"));
    419       return;
    420     }
    421   KERNEL_PRINT (("\n"));
    422 
    423   /*  XXXXX  add OS-specific code here */
    424 }
    425 
    426 
    427 static void
    428 imps_read_config_table (unsigned start, int count)
    429 {
    430   while (count-- > 0)
    431     {
    432       switch (*((unsigned char *) start))
    433 	{
    434 	case IMPS_BCT_PROCESSOR:
    435 	  add_processor ((imps_processor *) start);
    436 	  start += 12;		/* 20 total */
    437 	  break;
    438 	case IMPS_BCT_BUS:
    439 	  add_bus ((imps_bus *) start);
    440 	  break;
    441 	case IMPS_BCT_IOAPIC:
    442 	  add_ioapic ((imps_ioapic *) start);
    443 	  break;
    444 #if 0				/*  XXXXX  uncomment this if "add_io_interrupt" is implemented */
    445 	case IMPS_BCT_IO_INTERRUPT:
    446 	  add_io_interrupt ((imps_interrupt *) start);
    447 	  break;
    448 #endif
    449 #if 0				/*  XXXXX  uncomment this if "add_local_interrupt" is implemented */
    450 	case IMPS_BCT_LOCAL_INTERRUPT:
    451 	  add_local_interupt ((imps_interrupt *) start);
    452 	  break;
    453 #endif
    454 	default:
    455 	  break;
    456 	}
    457       start += 8;
    458     }
    459 }
    460 
    461 
    462 static int
    463 imps_bad_bios (imps_fps * fps_ptr)
    464 {
    465   int sum;
    466   imps_cth *local_cth_ptr
    467   = (imps_cth *) PHYS_TO_VIRTUAL (fps_ptr->cth_ptr);
    468 
    469   if (fps_ptr->feature_info[0] > IMPS_FPS_DEFAULT_MAX)
    470     {
    471       KERNEL_PRINT (("    Invalid MP System Configuration type %d\n",
    472 		     fps_ptr->feature_info[0]));
    473       return 1;
    474     }
    475 
    476   if (fps_ptr->cth_ptr)
    477     {
    478       sum = get_checksum ((unsigned) local_cth_ptr,
    479 			  local_cth_ptr->base_length);
    480       if (local_cth_ptr->sig != IMPS_CTH_SIGNATURE || sum)
    481 	{
    482 	  KERNEL_PRINT
    483 			(("    Bad MP Config Table sig 0x%x and/or checksum 0x%x\n",
    484 			(unsigned) (fps_ptr->cth_ptr), sum));
    485 	  return 1;
    486 	}
    487       if (local_cth_ptr->spec_rev != fps_ptr->spec_rev)
    488 	{
    489 	  KERNEL_PRINT (("    Bad MP Config Table sub-revision # %d\n", local_cth_ptr->spec_rev));
    490 	  return 1;
    491 	}
    492       if (local_cth_ptr->extended_length)
    493 	{
    494 	  sum = (get_checksum (((unsigned) local_cth_ptr)
    495 			       + local_cth_ptr->base_length,
    496 			       local_cth_ptr->extended_length)
    497 		 + local_cth_ptr->extended_checksum) & 0xFF;
    498 	  if (sum)
    499 	    {
    500 	      KERNEL_PRINT
    501 			    (("    Bad Extended MP Config Table checksum 0x%x\n", sum));
    502 	      return 1;
    503 	    }
    504 	}
    505     }
    506   else if (!fps_ptr->feature_info[0])
    507     {
    508       KERNEL_PRINT (("    Missing configuration information\n"));
    509       return 1;
    510     }
    511 
    512   return 0;
    513 }
    514 
    515 
    516 static void
    517 imps_read_bios (imps_fps * fps_ptr)
    518 {
    519   int apicid;
    520   unsigned cth_start, cth_count;
    521   imps_cth *local_cth_ptr
    522   = (imps_cth *) PHYS_TO_VIRTUAL (fps_ptr->cth_ptr);
    523   char *str_ptr;
    524 
    525   KERNEL_PRINT (("Intel MultiProcessor Spec 1.%d BIOS support detected\n",
    526 		 fps_ptr->spec_rev));
    527 
    528   /*
    529    *  Do all checking of errors which would definitely
    530    *  lead to failure of the SMP boot here.
    531    */
    532 
    533   if (imps_bad_bios (fps_ptr))
    534     {
    535       KERNEL_PRINT (("    Disabling MPS support\n"));
    536       return;
    537     }
    538 
    539   if (fps_ptr->feature_info[1] & IMPS_FPS_IMCRP_BIT)
    540     {
    541       str_ptr = "IMCR and PIC";
    542     }
    543   else
    544     {
    545       str_ptr = "Virtual Wire";
    546     }
    547   if (fps_ptr->cth_ptr)
    548     {
    549       imps_lapic_addr = local_cth_ptr->lapic_addr;
    550     }
    551   else
    552     {
    553       imps_lapic_addr = LAPIC_ADDR_DEFAULT;
    554     }
    555   KERNEL_PRINT
    556 		(("    APIC config: \"%s mode\"    Local APIC address: 0x%x\n",
    557 		 str_ptr, imps_lapic_addr));
    558   imps_lapic_addr = PHYS_TO_VIRTUAL (imps_lapic_addr);
    559 
    560   /*
    561    *  Setup primary CPU.
    562    */
    563   apicid = IMPS_LAPIC_READ (LAPIC_SPIV);
    564   IMPS_LAPIC_WRITE (LAPIC_SPIV, apicid | LAPIC_SPIV_ENABLE_APIC);
    565   imps_any_new_apics = IMPS_LAPIC_READ (LAPIC_VER) & 0xF0;
    566   apicid = IMPS_APIC_ID (IMPS_LAPIC_READ (LAPIC_ID));
    567   imps_cpu_apic_map[0] = apicid;
    568   imps_apic_cpu_map[apicid] = 0;
    569 
    570   if (fps_ptr->cth_ptr)
    571     {
    572       char str1[16], str2[16];
    573       memcpy (str1, local_cth_ptr->oem_id, 8);
    574       str1[8] = 0;
    575       memcpy (str2, local_cth_ptr->prod_id, 12);
    576       str2[12] = 0;
    577       KERNEL_PRINT (("  OEM id: %s  Product id: %s\n", str1, str2));
    578       cth_start = ((unsigned) local_cth_ptr) + sizeof (imps_cth);
    579       cth_count = local_cth_ptr->entry_count;
    580     }
    581   else
    582     {
    583       *((volatile unsigned *) IOAPIC_ADDR_DEFAULT) = IOAPIC_ID;
    584       defconfig.ioapic.id
    585 	= IMPS_APIC_ID (*((volatile unsigned *)
    586 			  (IOAPIC_ADDR_DEFAULT + IOAPIC_RW)));
    587       *((volatile unsigned *) IOAPIC_ADDR_DEFAULT) = IOAPIC_VER;
    588       defconfig.ioapic.ver
    589 	= APIC_VERSION (*((volatile unsigned *)
    590 			  (IOAPIC_ADDR_DEFAULT + IOAPIC_RW)));
    591       defconfig.proc[apicid].flags
    592 	= IMPS_FLAG_ENABLED | IMPS_CPUFLAG_BOOT;
    593       defconfig.proc[!apicid].flags = IMPS_FLAG_ENABLED;
    594       imps_num_cpus = 2;
    595       if (fps_ptr->feature_info[0] == 1
    596 	  || fps_ptr->feature_info[0] == 5)
    597 	{
    598 	  memcpy (defconfig.bus[0].bus_type, "ISA   ", 6);
    599 	}
    600       if (fps_ptr->feature_info[0] == 4
    601 	  || fps_ptr->feature_info[0] == 7)
    602 	{
    603 	  memcpy (defconfig.bus[0].bus_type, "MCA   ", 6);
    604 	}
    605       if (fps_ptr->feature_info[0] > 4)
    606 	{
    607 	  defconfig.proc[0].apic_ver = 0x10;
    608 	  defconfig.proc[1].apic_ver = 0x10;
    609 	  defconfig.bus[1].type = IMPS_BCT_BUS;
    610 	}
    611       if (fps_ptr->feature_info[0] == 2)
    612 	{
    613 	  defconfig.intin[2].type = 255;
    614 	  defconfig.intin[13].type = 255;
    615 	}
    616       if (fps_ptr->feature_info[0] == 7)
    617 	{
    618 	  defconfig.intin[0].type = 255;
    619 	}
    620       cth_start = (unsigned) &defconfig;
    621       cth_count = DEF_ENTRIES;
    622     }
    623   imps_read_config_table (cth_start, cth_count);
    624 
    625   /* %%%%% ESB read extended entries here */
    626 
    627   imps_enabled = 1;
    628 }
    629 
    630 
    631 /*
    632  *  Given a region to check, this actually looks for the "MP Floating
    633  *  Pointer Structure".  The return value indicates if the correct
    634  *  signature and checksum for a floating pointer structure of the
    635  *  appropriate spec revision was found.  If so, then do not search
    636  *  further.
    637  *
    638  *  NOTE:  The memory scan will always be in the bottom 1 MB.
    639  *
    640  *  This function presumes that "start" will always be aligned to a 16-bit
    641  *  boundary.
    642  *
    643  *  Function finished.
    644  */
    645 
    646 static int
    647 imps_scan (unsigned start, unsigned length)
    648 {
    649   IMPS_DEBUG_PRINT (("Scanning from 0x%x for %d bytes\n",
    650 		     start, length));
    651 
    652   while (length > 0)
    653     {
    654       imps_fps *fps_ptr = (imps_fps *) PHYS_TO_VIRTUAL (start);
    655 
    656       if (fps_ptr->sig == IMPS_FPS_SIGNATURE
    657 	  && fps_ptr->length == 1
    658 	  && (fps_ptr->spec_rev == 1 || fps_ptr->spec_rev == 4)
    659 	  && !get_checksum (start, 16))
    660 	{
    661 	  IMPS_DEBUG_PRINT (("Found MP Floating Structure Pointer at %x\n", start));
    662 	  imps_read_bios (fps_ptr);
    663 	  return 1;
    664 	}
    665 
    666       length -= 16;
    667       start += 16;
    668     }
    669 
    670   return 0;
    671 }
    672 
    673 
    674 /*
    675  *  This is the primary function for probing for MPS compatible hardware
    676  *  and BIOS information.  Call this during the early stages of OS startup,
    677  *  before memory can be messed up.
    678  *
    679  *  The probe looks for the "MP Floating Pointer Structure" at locations
    680  *  listed at the top of page 4-2 of the spec.
    681  *
    682  *  Environment requirements from the OS to run:
    683  *
    684  *   (1) : A non-linear virtual to physical memory mapping is probably OK,
    685  *	     as (I think) the structures all fall within page boundaries,
    686  *	     but a linear mapping is recommended.  Currently assumes that
    687  *	     the mapping will remain identical over time (which should be
    688  *	     OK since it only accesses memory which shouldn't be munged
    689  *	     by the OS anyway).
    690  *   (2) : The OS only consumes memory which the BIOS says is OK to use,
    691  *	     and not any of the BIOS standard areas (the areas 0x400 to
    692  *	     0x600, the EBDA, 0xE0000 to 0xFFFFF, and unreported physical
    693  *	     RAM).  Sometimes a small amount of physical RAM is not
    694  *	     reported by the BIOS, to be used to store MPS and other
    695  *	     information.
    696  *   (3) : It must be possible to read the CMOS.
    697  *   (4) : There must be between 512K and 640K of lower memory (this is a
    698  *	     sanity check).
    699  *
    700  *  Function finished.
    701  */
    702 
    703 int
    704 imps_probe (void)
    705 {
    706   /*
    707    *  Determine possible address of the EBDA
    708    */
    709   unsigned ebda_addr = *((unsigned short *)
    710 			 PHYS_TO_VIRTUAL (EBDA_SEG_ADDR)) << 4;
    711 
    712   /*
    713    *  Determine amount of installed lower memory (not *available*
    714    *  lower memory).
    715    *
    716    *  NOTE:  This should work reliably as long as we verify the
    717    *         machine is at least a system that could possibly have
    718    *         MPS compatibility to begin with.
    719    */
    720   unsigned mem_lower = ((CMOS_READ_BYTE (CMOS_BASE_MEMORY + 1) << 8)
    721 			| CMOS_READ_BYTE (CMOS_BASE_MEMORY)) << 10;
    722 
    723 #ifdef IMPS_DEBUG
    724   imps_enabled = 0;
    725   imps_num_cpus = 1;
    726 #endif
    727 
    728   /*
    729    *  Sanity check : if this isn't reasonable, it is almost impossibly
    730    *    unlikely to be an MPS compatible machine, so return failure.
    731    */
    732   if (mem_lower < 512 * 1024 || mem_lower > 640 * 1024)
    733     {
    734       return 0;
    735     }
    736 
    737   if (ebda_addr > mem_lower - 1024
    738       || ebda_addr + *((unsigned char *) PHYS_TO_VIRTUAL (ebda_addr))
    739       * 1024 > mem_lower)
    740     {
    741       ebda_addr = 0;
    742     }
    743 
    744   if (((ebda_addr && imps_scan (ebda_addr, 1024))
    745        || (!ebda_addr && imps_scan (mem_lower - 1024, 1024))
    746        || imps_scan (0xF0000, 0x10000)) && imps_enabled)
    747     {
    748       return 1;
    749     }
    750 
    751   /*
    752    *  If no BIOS info on MPS hardware is found, then return failure.
    753    */
    754 
    755   return 0;
    756 }
    757