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
inb(unsigned short port)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
outb(unsigned short port,unsigned char val)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
cmos_write_byte(int loc,int val)89 cmos_write_byte (int loc, int val)
90 {
91 outb (0x70, loc);
92 outb (0x71, val);
93 }
94
95 static inline unsigned
cmos_read_byte(int loc)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
get_checksum(unsigned start,int length)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
boot_cpu(imps_processor * proc)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
add_processor(imps_processor * proc)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
add_bus(imps_bus * bus)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
add_ioapic(imps_ioapic * ioapic)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
imps_read_config_table(unsigned start,int count)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
imps_bad_bios(imps_fps * fps_ptr)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
imps_read_bios(imps_fps * fps_ptr)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
imps_scan(unsigned start,unsigned length)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
imps_probe(void)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