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