xref: /titanic_51/usr/src/grub/grub-0.97/stage2/smp-imps.c (revision 1b8adde7ba7d5e04395c141c5400dc2cffd7d809)
1 /*
2  *  GRUB  --  GRand Unified Bootloader
3  *  Copyright (C) 1999,2005,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@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