|  | /* | 
|  | *  GRUB  --  GRand Unified Bootloader | 
|  | *  Copyright (C) 1999  Free Software Foundation, Inc. | 
|  | * | 
|  | *  This program is free software; you can redistribute it and/or modify | 
|  | *  it under the terms of the GNU General Public License as published by | 
|  | *  the Free Software Foundation; either version 2 of the License, or | 
|  | *  (at your option) any later version. | 
|  | * | 
|  | *  This program is distributed in the hope that it will be useful, | 
|  | *  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | *  GNU General Public License for more details. | 
|  | * | 
|  | *  You should have received a copy of the GNU General Public License | 
|  | *  along with this program; if not, write to the Free Software | 
|  | *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 
|  | */ | 
|  |  | 
|  | /* | 
|  | *  <Insert copyright here : it must be BSD-like so anyone can use it> | 
|  | * | 
|  | *  Author:  Erich Boleyn  <erich@uruk.org>   http://www.uruk.org/~erich/ | 
|  | * | 
|  | *  Source file implementing Intel MultiProcessor Specification (MPS) | 
|  | *  version 1.1 and 1.4 SMP hardware control for Intel Architecture CPUs, | 
|  | *  with hooks for running correctly on a standard PC without the hardware. | 
|  | * | 
|  | *  This file was created from information in the Intel MPS version 1.4 | 
|  | *  document, order number 242016-004, which can be ordered from the | 
|  | *  Intel literature center. | 
|  | * | 
|  | *  General limitations of this code: | 
|  | * | 
|  | *   (1) : This code has never been tested on an MPS-compatible system with | 
|  | *           486 CPUs, but is expected to work. | 
|  | *   (2) : Presumes "int", "long", and "unsigned" are 32 bits in size, and | 
|  | *	     that 32-bit pointers and memory addressing is used uniformly. | 
|  | */ | 
|  |  | 
|  | #define _SMP_IMPS_C | 
|  |  | 
|  |  | 
|  | /* | 
|  | *  XXXXX  The following absolutely must be defined!!! | 
|  | * | 
|  | *  The "KERNEL_PRINT" could be made a null macro with no danger, of | 
|  | *  course, but pretty much nothing would work without the other | 
|  | *  ones defined. | 
|  | */ | 
|  |  | 
|  | #if 0 | 
|  | #define KERNEL_PRINT(x)		/* some kind of print function */ | 
|  | #define CMOS_WRITE_BYTE(x,y)	/* write unsigned char "y" at CMOS loc "x" */ | 
|  | #define CMOS_READ_BYTE(x)	/* read unsigned char at CMOS loc "x" */ | 
|  | #define PHYS_TO_VIRTUAL(x)	/* convert physical address "x" to virtual */ | 
|  | #define VIRTUAL_TO_PHYS(x)	/* convert virtual address "x" to physical */ | 
|  | #endif | 
|  |  | 
|  |  | 
|  | /* | 
|  | *  This is the Intel MultiProcessor Spec debugging/display code. | 
|  | */ | 
|  |  | 
|  | #define IMPS_DEBUG | 
|  | #define KERNEL_PRINT(x)         printf x | 
|  | #define CMOS_WRITE_BYTE(x, y)	cmos_write_byte(x, y) | 
|  | #define CMOS_READ_BYTE(x)	cmos_read_byte(x) | 
|  | #define PHYS_TO_VIRTUAL(x)	(x) | 
|  | #define VIRTUAL_TO_PHYS(x)	(x) | 
|  |  | 
|  | static inline unsigned char | 
|  | inb (unsigned short port) | 
|  | { | 
|  | unsigned char data; | 
|  |  | 
|  | __asm __volatile ("inb %1,%0" :"=a" (data):"d" (port)); | 
|  | return data; | 
|  | } | 
|  |  | 
|  | static inline void | 
|  | outb (unsigned short port, unsigned char val) | 
|  | { | 
|  | __asm __volatile ("outb %0,%1"::"a" (val), "d" (port)); | 
|  | } | 
|  |  | 
|  |  | 
|  | static inline void | 
|  | cmos_write_byte (int loc, int val) | 
|  | { | 
|  | outb (0x70, loc); | 
|  | outb (0x71, val); | 
|  | } | 
|  |  | 
|  | static inline unsigned | 
|  | cmos_read_byte (int loc) | 
|  | { | 
|  | outb (0x70, loc); | 
|  | return inb (0x71); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | *  Includes here | 
|  | */ | 
|  |  | 
|  | #include "shared.h" | 
|  | #include "apic.h" | 
|  | #include "smp-imps.h" | 
|  |  | 
|  |  | 
|  | /* | 
|  | *  Defines that are here so as not to be in the global header file. | 
|  | */ | 
|  | #define EBDA_SEG_ADDR			0x40E | 
|  | #define BIOS_RESET_VECTOR		0x467 | 
|  | #define LAPIC_ADDR_DEFAULT		0xFEE00000uL | 
|  | #define IOAPIC_ADDR_DEFAULT		0xFEC00000uL | 
|  | #define CMOS_RESET_CODE			0xF | 
|  | #define		CMOS_RESET_JUMP		0xa | 
|  | #define CMOS_BASE_MEMORY		0x15 | 
|  |  | 
|  |  | 
|  | /* | 
|  | *  Static defines here for SMP use. | 
|  | */ | 
|  |  | 
|  | #define DEF_ENTRIES	23 | 
|  |  | 
|  | static int lapic_dummy = 0; | 
|  | static struct | 
|  | { | 
|  | imps_processor proc[2]; | 
|  | imps_bus bus[2]; | 
|  | imps_ioapic ioapic; | 
|  | imps_interrupt intin[16]; | 
|  | imps_interrupt lintin[2]; | 
|  | } | 
|  | defconfig = | 
|  | { | 
|  | { | 
|  | { | 
|  | IMPS_BCT_PROCESSOR, 0, 0, 0, 0, 0 | 
|  | } | 
|  | , | 
|  | { | 
|  | IMPS_BCT_PROCESSOR, 1, 0, 0, 0, 0 | 
|  | } | 
|  | } | 
|  | , | 
|  | { | 
|  | { | 
|  | IMPS_BCT_BUS, 0, | 
|  | { | 
|  | 'E', 'I', 'S', 'A', ' ', ' ' | 
|  | } | 
|  | } | 
|  | , | 
|  | { | 
|  | 255, 1, | 
|  | { | 
|  | 'P', 'C', 'I', ' ', ' ', ' ' | 
|  | } | 
|  | } | 
|  | } | 
|  | , | 
|  | { | 
|  | IMPS_BCT_IOAPIC, 0, 0, IMPS_FLAG_ENABLED, IOAPIC_ADDR_DEFAULT | 
|  | } | 
|  | , | 
|  | { | 
|  | { | 
|  | IMPS_BCT_IO_INTERRUPT, IMPS_INT_EXTINT, 0, 0, 0, 0xFF, 0 | 
|  | } | 
|  | , | 
|  | { | 
|  | IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 1, 0xFF, 1 | 
|  | } | 
|  | , | 
|  | { | 
|  | IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 0, 0xFF, 2 | 
|  | } | 
|  | , | 
|  | { | 
|  | IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 3, 0xFF, 3 | 
|  | } | 
|  | , | 
|  | { | 
|  | IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 4, 0xFF, 4 | 
|  | } | 
|  | , | 
|  | { | 
|  | IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 5, 0xFF, 5 | 
|  | } | 
|  | , | 
|  | { | 
|  | IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 6, 0xFF, 6 | 
|  | } | 
|  | , | 
|  | { | 
|  | IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 7, 0xFF, 7 | 
|  | } | 
|  | , | 
|  | { | 
|  | IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 8, 0xFF, 8 | 
|  | } | 
|  | , | 
|  | { | 
|  | IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 9, 0xFF, 9 | 
|  | } | 
|  | , | 
|  | { | 
|  | IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 10, 0xFF, 10 | 
|  | } | 
|  | , | 
|  | { | 
|  | IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 11, 0xFF, 11 | 
|  | } | 
|  | , | 
|  | { | 
|  | IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 12, 0xFF, 12 | 
|  | } | 
|  | , | 
|  | { | 
|  | IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 13, 0xFF, 13 | 
|  | } | 
|  | , | 
|  | { | 
|  | IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 14, 0xFF, 14 | 
|  | } | 
|  | , | 
|  | { | 
|  | IMPS_BCT_IO_INTERRUPT, IMPS_INT_INT, 0, 0, 15, 0xFF, 15 | 
|  | } | 
|  | } | 
|  | , | 
|  | { | 
|  | { | 
|  | IMPS_BCT_LOCAL_INTERRUPT, IMPS_INT_EXTINT, 0, 0, 15, 0xFF, 0 | 
|  | } | 
|  | , | 
|  | { | 
|  | IMPS_BCT_LOCAL_INTERRUPT, IMPS_INT_NMI, 0, 0, 15, 0xFF, 1 | 
|  | } | 
|  | } | 
|  | }; | 
|  |  | 
|  | /* | 
|  | *  Exported globals here. | 
|  | */ | 
|  |  | 
|  | static int imps_any_new_apics = 0; | 
|  | #if 0 | 
|  | volatile int imps_release_cpus = 0; | 
|  | #endif | 
|  | static int imps_enabled = 0; | 
|  | static int imps_num_cpus = 1; | 
|  | static unsigned imps_lapic_addr = ((unsigned) (&lapic_dummy)) - LAPIC_ID; | 
|  | static unsigned char imps_cpu_apic_map[IMPS_MAX_CPUS]; | 
|  | static unsigned char imps_apic_cpu_map[IMPS_MAX_CPUS]; | 
|  |  | 
|  |  | 
|  | /* | 
|  | *  MPS checksum function | 
|  | * | 
|  | *  Function finished. | 
|  | */ | 
|  |  | 
|  | static int | 
|  | get_checksum (unsigned start, int length) | 
|  | { | 
|  | unsigned sum = 0; | 
|  |  | 
|  | while (length-- > 0) | 
|  | { | 
|  | sum += *((unsigned char *) (start++)); | 
|  | } | 
|  |  | 
|  | return (sum & 0xFF); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | *  Primary function for booting individual CPUs. | 
|  | * | 
|  | *  This must be modified to perform whatever OS-specific initialization | 
|  | *  that is required. | 
|  | */ | 
|  |  | 
|  | static int | 
|  | boot_cpu (imps_processor * proc) | 
|  | { | 
|  | unsigned bootaddr, accept_status; | 
|  | unsigned bios_reset_vector = PHYS_TO_VIRTUAL (BIOS_RESET_VECTOR); | 
|  |  | 
|  | /* %%%%% ESB */ | 
|  | extern char patch_code[]; | 
|  | bootaddr = 256 * 1024; | 
|  | memmove ((char *) bootaddr, patch_code, 32); | 
|  |  | 
|  | /* | 
|  | *  Generic CPU startup sequence starts here. | 
|  | */ | 
|  |  | 
|  | /* set BIOS reset vector */ | 
|  | CMOS_WRITE_BYTE (CMOS_RESET_CODE, CMOS_RESET_JUMP); | 
|  | *((volatile unsigned *) bios_reset_vector) = bootaddr << 12; | 
|  |  | 
|  | /* clear the error register */ | 
|  | if (proc->apic_ver & 0x10) | 
|  | { | 
|  | IMPS_LAPIC_WRITE (LAPIC_ESR, 0); | 
|  | accept_status = IMPS_LAPIC_READ (LAPIC_ESR); | 
|  | } | 
|  |  | 
|  | #if 0 | 
|  | /* assert INIT IPI */ | 
|  | cfg = IMPS_LAPIC_READ (LAPIC_ICR + 1); | 
|  | cfg &= LAPIC_DEST_MASK; | 
|  | IMPS_LAPIC_WRITE (LAPIC_ICR + 1, cfg); | 
|  | cfg = IMPS_LAPIC_READ (LAPIC_ACR); | 
|  | cfg &=; | 
|  |  | 
|  | /* %%%%% ESB finish adding startup sequence */ | 
|  | #endif | 
|  |  | 
|  | /* clean up BIOS reset vector */ | 
|  | CMOS_WRITE_BYTE (CMOS_RESET_CODE, 0); | 
|  | *((volatile unsigned *) bios_reset_vector) = 0; | 
|  |  | 
|  | /* | 
|  | *  Generic CPU startup sequence ends here. | 
|  | */ | 
|  |  | 
|  | KERNEL_PRINT (("\n")); | 
|  |  | 
|  | return 1; | 
|  |  | 
|  | /* XXXXX add OS-specific initialization here! */ | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | *  read bios stuff and fill tables | 
|  | */ | 
|  |  | 
|  | static void | 
|  | add_processor (imps_processor * proc) | 
|  | { | 
|  | int apicid = proc->apic_id; | 
|  |  | 
|  | KERNEL_PRINT (("  Processor [APIC id %d ver %d]:  ", | 
|  | apicid, proc->apic_ver)); | 
|  | if (!(proc->flags & IMPS_FLAG_ENABLED)) | 
|  | { | 
|  | KERNEL_PRINT (("DISABLED\n")); | 
|  | return; | 
|  | } | 
|  | if (proc->apic_ver > 0xF) | 
|  | { | 
|  | imps_any_new_apics = 1; | 
|  | } | 
|  | if (proc->flags & (IMPS_CPUFLAG_BOOT)) | 
|  | { | 
|  | KERNEL_PRINT (("#0  Bootstrap Processor (BSP)\n")); | 
|  | return; | 
|  | } | 
|  | imps_cpu_apic_map[imps_num_cpus] = apicid; | 
|  | imps_apic_cpu_map[apicid] = imps_num_cpus; | 
|  | if (boot_cpu (proc)) | 
|  | { | 
|  |  | 
|  | /*  XXXXX  add OS-specific setup for secondary CPUs here */ | 
|  |  | 
|  | imps_num_cpus++; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | add_bus (imps_bus * bus) | 
|  | { | 
|  | char str[8]; | 
|  |  | 
|  | memmove (str, bus->bus_type, 6); | 
|  | str[6] = 0; | 
|  | KERNEL_PRINT (("  Bus id %d is %s\n", bus->id, str)); | 
|  |  | 
|  | /*  XXXXX  add OS-specific code here */ | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | add_ioapic (imps_ioapic * ioapic) | 
|  | { | 
|  | KERNEL_PRINT (("  I/O APIC id %d ver %d, address: 0x%x  ", | 
|  | ioapic->id, ioapic->ver, ioapic->addr)); | 
|  | if (!(ioapic->flags & IMPS_FLAG_ENABLED)) | 
|  | { | 
|  | KERNEL_PRINT (("DISABLED\n")); | 
|  | return; | 
|  | } | 
|  | KERNEL_PRINT (("\n")); | 
|  |  | 
|  | /*  XXXXX  add OS-specific code here */ | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | imps_read_config_table (unsigned start, int count) | 
|  | { | 
|  | while (count-- > 0) | 
|  | { | 
|  | switch (*((unsigned char *) start)) | 
|  | { | 
|  | case IMPS_BCT_PROCESSOR: | 
|  | add_processor ((imps_processor *) start); | 
|  | start += 12;		/* 20 total */ | 
|  | break; | 
|  | case IMPS_BCT_BUS: | 
|  | add_bus ((imps_bus *) start); | 
|  | break; | 
|  | case IMPS_BCT_IOAPIC: | 
|  | add_ioapic ((imps_ioapic *) start); | 
|  | break; | 
|  | #if 0				/*  XXXXX  uncomment this if "add_io_interrupt" is implemented */ | 
|  | case IMPS_BCT_IO_INTERRUPT: | 
|  | add_io_interrupt ((imps_interrupt *) start); | 
|  | break; | 
|  | #endif | 
|  | #if 0				/*  XXXXX  uncomment this if "add_local_interrupt" is implemented */ | 
|  | case IMPS_BCT_LOCAL_INTERRUPT: | 
|  | add_local_interupt ((imps_interrupt *) start); | 
|  | break; | 
|  | #endif | 
|  | default: | 
|  | break; | 
|  | } | 
|  | start += 8; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | imps_bad_bios (imps_fps * fps_ptr) | 
|  | { | 
|  | int sum; | 
|  | imps_cth *local_cth_ptr | 
|  | = (imps_cth *) PHYS_TO_VIRTUAL (fps_ptr->cth_ptr); | 
|  |  | 
|  | if (fps_ptr->feature_info[0] > IMPS_FPS_DEFAULT_MAX) | 
|  | { | 
|  | KERNEL_PRINT (("    Invalid MP System Configuration type %d\n", | 
|  | fps_ptr->feature_info[0])); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | if (fps_ptr->cth_ptr) | 
|  | { | 
|  | sum = get_checksum ((unsigned) local_cth_ptr, | 
|  | local_cth_ptr->base_length); | 
|  | if (local_cth_ptr->sig != IMPS_CTH_SIGNATURE || sum) | 
|  | { | 
|  | KERNEL_PRINT | 
|  | (("    Bad MP Config Table sig 0x%x and/or checksum 0x%x\n", | 
|  | (unsigned) (fps_ptr->cth_ptr), sum)); | 
|  | return 1; | 
|  | } | 
|  | if (local_cth_ptr->spec_rev != fps_ptr->spec_rev) | 
|  | { | 
|  | KERNEL_PRINT (("    Bad MP Config Table sub-revision # %d\n", local_cth_ptr->spec_rev)); | 
|  | return 1; | 
|  | } | 
|  | if (local_cth_ptr->extended_length) | 
|  | { | 
|  | sum = (get_checksum (((unsigned) local_cth_ptr) | 
|  | + local_cth_ptr->base_length, | 
|  | local_cth_ptr->extended_length) | 
|  | + local_cth_ptr->extended_checksum) & 0xFF; | 
|  | if (sum) | 
|  | { | 
|  | KERNEL_PRINT | 
|  | (("    Bad Extended MP Config Table checksum 0x%x\n", sum)); | 
|  | return 1; | 
|  | } | 
|  | } | 
|  | } | 
|  | else if (!fps_ptr->feature_info[0]) | 
|  | { | 
|  | KERNEL_PRINT (("    Missing configuration information\n")); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | imps_read_bios (imps_fps * fps_ptr) | 
|  | { | 
|  | int apicid; | 
|  | unsigned cth_start, cth_count; | 
|  | imps_cth *local_cth_ptr | 
|  | = (imps_cth *) PHYS_TO_VIRTUAL (fps_ptr->cth_ptr); | 
|  | char *str_ptr; | 
|  |  | 
|  | KERNEL_PRINT (("Intel MultiProcessor Spec 1.%d BIOS support detected\n", | 
|  | fps_ptr->spec_rev)); | 
|  |  | 
|  | /* | 
|  | *  Do all checking of errors which would definitely | 
|  | *  lead to failure of the SMP boot here. | 
|  | */ | 
|  |  | 
|  | if (imps_bad_bios (fps_ptr)) | 
|  | { | 
|  | KERNEL_PRINT (("    Disabling MPS support\n")); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (fps_ptr->feature_info[1] & IMPS_FPS_IMCRP_BIT) | 
|  | { | 
|  | str_ptr = "IMCR and PIC"; | 
|  | } | 
|  | else | 
|  | { | 
|  | str_ptr = "Virtual Wire"; | 
|  | } | 
|  | if (fps_ptr->cth_ptr) | 
|  | { | 
|  | imps_lapic_addr = local_cth_ptr->lapic_addr; | 
|  | } | 
|  | else | 
|  | { | 
|  | imps_lapic_addr = LAPIC_ADDR_DEFAULT; | 
|  | } | 
|  | KERNEL_PRINT | 
|  | (("    APIC config: \"%s mode\"    Local APIC address: 0x%x\n", | 
|  | str_ptr, imps_lapic_addr)); | 
|  | imps_lapic_addr = PHYS_TO_VIRTUAL (imps_lapic_addr); | 
|  |  | 
|  | /* | 
|  | *  Setup primary CPU. | 
|  | */ | 
|  | apicid = IMPS_LAPIC_READ (LAPIC_SPIV); | 
|  | IMPS_LAPIC_WRITE (LAPIC_SPIV, apicid | LAPIC_SPIV_ENABLE_APIC); | 
|  | imps_any_new_apics = IMPS_LAPIC_READ (LAPIC_VER) & 0xF0; | 
|  | apicid = IMPS_APIC_ID (IMPS_LAPIC_READ (LAPIC_ID)); | 
|  | imps_cpu_apic_map[0] = apicid; | 
|  | imps_apic_cpu_map[apicid] = 0; | 
|  |  | 
|  | if (fps_ptr->cth_ptr) | 
|  | { | 
|  | char str1[16], str2[16]; | 
|  | memcpy (str1, local_cth_ptr->oem_id, 8); | 
|  | str1[8] = 0; | 
|  | memcpy (str2, local_cth_ptr->prod_id, 12); | 
|  | str2[12] = 0; | 
|  | KERNEL_PRINT (("  OEM id: %s  Product id: %s\n", str1, str2)); | 
|  | cth_start = ((unsigned) local_cth_ptr) + sizeof (imps_cth); | 
|  | cth_count = local_cth_ptr->entry_count; | 
|  | } | 
|  | else | 
|  | { | 
|  | *((volatile unsigned *) IOAPIC_ADDR_DEFAULT) = IOAPIC_ID; | 
|  | defconfig.ioapic.id | 
|  | = IMPS_APIC_ID (*((volatile unsigned *) | 
|  | (IOAPIC_ADDR_DEFAULT + IOAPIC_RW))); | 
|  | *((volatile unsigned *) IOAPIC_ADDR_DEFAULT) = IOAPIC_VER; | 
|  | defconfig.ioapic.ver | 
|  | = APIC_VERSION (*((volatile unsigned *) | 
|  | (IOAPIC_ADDR_DEFAULT + IOAPIC_RW))); | 
|  | defconfig.proc[apicid].flags | 
|  | = IMPS_FLAG_ENABLED | IMPS_CPUFLAG_BOOT; | 
|  | defconfig.proc[!apicid].flags = IMPS_FLAG_ENABLED; | 
|  | imps_num_cpus = 2; | 
|  | if (fps_ptr->feature_info[0] == 1 | 
|  | || fps_ptr->feature_info[0] == 5) | 
|  | { | 
|  | memcpy (defconfig.bus[0].bus_type, "ISA   ", 6); | 
|  | } | 
|  | if (fps_ptr->feature_info[0] == 4 | 
|  | || fps_ptr->feature_info[0] == 7) | 
|  | { | 
|  | memcpy (defconfig.bus[0].bus_type, "MCA   ", 6); | 
|  | } | 
|  | if (fps_ptr->feature_info[0] > 4) | 
|  | { | 
|  | defconfig.proc[0].apic_ver = 0x10; | 
|  | defconfig.proc[1].apic_ver = 0x10; | 
|  | defconfig.bus[1].type = IMPS_BCT_BUS; | 
|  | } | 
|  | if (fps_ptr->feature_info[0] == 2) | 
|  | { | 
|  | defconfig.intin[2].type = 255; | 
|  | defconfig.intin[13].type = 255; | 
|  | } | 
|  | if (fps_ptr->feature_info[0] == 7) | 
|  | { | 
|  | defconfig.intin[0].type = 255; | 
|  | } | 
|  | cth_start = (unsigned) &defconfig; | 
|  | cth_count = DEF_ENTRIES; | 
|  | } | 
|  | imps_read_config_table (cth_start, cth_count); | 
|  |  | 
|  | /* %%%%% ESB read extended entries here */ | 
|  |  | 
|  | imps_enabled = 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | *  Given a region to check, this actually looks for the "MP Floating | 
|  | *  Pointer Structure".  The return value indicates if the correct | 
|  | *  signature and checksum for a floating pointer structure of the | 
|  | *  appropriate spec revision was found.  If so, then do not search | 
|  | *  further. | 
|  | * | 
|  | *  NOTE:  The memory scan will always be in the bottom 1 MB. | 
|  | * | 
|  | *  This function presumes that "start" will always be aligned to a 16-bit | 
|  | *  boundary. | 
|  | * | 
|  | *  Function finished. | 
|  | */ | 
|  |  | 
|  | static int | 
|  | imps_scan (unsigned start, unsigned length) | 
|  | { | 
|  | IMPS_DEBUG_PRINT (("Scanning from 0x%x for %d bytes\n", | 
|  | start, length)); | 
|  |  | 
|  | while (length > 0) | 
|  | { | 
|  | imps_fps *fps_ptr = (imps_fps *) PHYS_TO_VIRTUAL (start); | 
|  |  | 
|  | if (fps_ptr->sig == IMPS_FPS_SIGNATURE | 
|  | && fps_ptr->length == 1 | 
|  | && (fps_ptr->spec_rev == 1 || fps_ptr->spec_rev == 4) | 
|  | && !get_checksum (start, 16)) | 
|  | { | 
|  | IMPS_DEBUG_PRINT (("Found MP Floating Structure Pointer at %x\n", start)); | 
|  | imps_read_bios (fps_ptr); | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | length -= 16; | 
|  | start += 16; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /* | 
|  | *  This is the primary function for probing for MPS compatible hardware | 
|  | *  and BIOS information.  Call this during the early stages of OS startup, | 
|  | *  before memory can be messed up. | 
|  | * | 
|  | *  The probe looks for the "MP Floating Pointer Structure" at locations | 
|  | *  listed at the top of page 4-2 of the spec. | 
|  | * | 
|  | *  Environment requirements from the OS to run: | 
|  | * | 
|  | *   (1) : A non-linear virtual to physical memory mapping is probably OK, | 
|  | *	     as (I think) the structures all fall within page boundaries, | 
|  | *	     but a linear mapping is recommended.  Currently assumes that | 
|  | *	     the mapping will remain identical over time (which should be | 
|  | *	     OK since it only accesses memory which shouldn't be munged | 
|  | *	     by the OS anyway). | 
|  | *   (2) : The OS only consumes memory which the BIOS says is OK to use, | 
|  | *	     and not any of the BIOS standard areas (the areas 0x400 to | 
|  | *	     0x600, the EBDA, 0xE0000 to 0xFFFFF, and unreported physical | 
|  | *	     RAM).  Sometimes a small amount of physical RAM is not | 
|  | *	     reported by the BIOS, to be used to store MPS and other | 
|  | *	     information. | 
|  | *   (3) : It must be possible to read the CMOS. | 
|  | *   (4) : There must be between 512K and 640K of lower memory (this is a | 
|  | *	     sanity check). | 
|  | * | 
|  | *  Function finished. | 
|  | */ | 
|  |  | 
|  | int | 
|  | imps_probe (void) | 
|  | { | 
|  | /* | 
|  | *  Determine possible address of the EBDA | 
|  | */ | 
|  | unsigned ebda_addr = *((unsigned short *) | 
|  | PHYS_TO_VIRTUAL (EBDA_SEG_ADDR)) << 4; | 
|  |  | 
|  | /* | 
|  | *  Determine amount of installed lower memory (not *available* | 
|  | *  lower memory). | 
|  | * | 
|  | *  NOTE:  This should work reliably as long as we verify the | 
|  | *         machine is at least a system that could possibly have | 
|  | *         MPS compatibility to begin with. | 
|  | */ | 
|  | unsigned mem_lower = ((CMOS_READ_BYTE (CMOS_BASE_MEMORY + 1) << 8) | 
|  | | CMOS_READ_BYTE (CMOS_BASE_MEMORY)) << 10; | 
|  |  | 
|  | #ifdef IMPS_DEBUG | 
|  | imps_enabled = 0; | 
|  | imps_num_cpus = 1; | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | *  Sanity check : if this isn't reasonable, it is almost impossibly | 
|  | *    unlikely to be an MPS compatible machine, so return failure. | 
|  | */ | 
|  | if (mem_lower < 512 * 1024 || mem_lower > 640 * 1024) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (ebda_addr > mem_lower - 1024 | 
|  | || ebda_addr + *((unsigned char *) PHYS_TO_VIRTUAL (ebda_addr)) | 
|  | * 1024 > mem_lower) | 
|  | { | 
|  | ebda_addr = 0; | 
|  | } | 
|  |  | 
|  | if (((ebda_addr && imps_scan (ebda_addr, 1024)) | 
|  | || (!ebda_addr && imps_scan (mem_lower - 1024, 1024)) | 
|  | || imps_scan (0xF0000, 0x10000)) && imps_enabled) | 
|  | { | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | /* | 
|  | *  If no BIOS info on MPS hardware is found, then return failure. | 
|  | */ | 
|  |  | 
|  | return 0; | 
|  | } |