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@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
72static inline unsigned char
73inb (unsigned short port)
74{
75  unsigned char data;
76
77  __asm __volatile ("inb %1,%0" :"=a" (data):"d" (port));
78  return data;
79}
80
81static inline void
82outb (unsigned short port, unsigned char val)
83{
84  __asm __volatile ("outb %0,%1"::"a" (val), "d" (port));
85}
86
87
88static inline void
89cmos_write_byte (int loc, int val)
90{
91  outb (0x70, loc);
92  outb (0x71, val);
93}
94
95static inline unsigned
96cmos_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
130static int lapic_dummy = 0;
131static 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  }
139defconfig =
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 */
257static int imps_any_new_apics = 0;
258#if 0
259volatile 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 */
265static int imps_enabled = 0;
266/*
267 *  This represents the number of CPUs found.
268 */
269static int imps_num_cpus = 1;
270/*
271 *  This contains the local APIC hardware address.
272 */
273static unsigned imps_lapic_addr = ((unsigned) (&lapic_dummy)) - LAPIC_ID;
274/*
275 *  These map from virtual cpu numbers to APIC id's and back.
276 */
277static unsigned char imps_cpu_apic_map[IMPS_MAX_CPUS];
278static unsigned char imps_apic_cpu_map[IMPS_MAX_CPUS];
279
280
281/*
282 *  MPS checksum function
283 *
284 *  Function finished.
285 */
286
287static int
288get_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
308static int
309boot_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
365static void
366add_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
398static void
399add_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
411static void
412add_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
427static void
428imps_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
462static int
463imps_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
516static void
517imps_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
646static int
647imps_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
703int
704imps_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