smp-imps.c revision 1b8adde7ba7d5e04395c141c5400dc2cffd7d809
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 1999,2005,2005 Free Software Foundation, Inc.
*
* 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
/*
*/
#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
{
unsigned char data;
return data;
}
static inline void
{
}
static inline void
{
}
static inline unsigned
cmos_read_byte (int 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_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', ' ', ' ', ' '
}
}
}
,
{
}
,
{
{
}
,
{
}
,
{
}
,
{
}
,
{
}
,
{
}
,
{
}
,
{
}
,
{
}
,
{
}
,
{
}
,
{
}
,
{
}
,
{
}
,
{
}
,
{
}
}
,
{
{
}
,
{
}
}
};
/*
* Exported globals here.
*/
/*
* "imps_any_new_apics" is non-zero if any of the APICS (local or I/O)
* are *not* an 82489DX. This is useful to determine if more than 15
* CPUs can be supported (true if zero).
*/
static int imps_any_new_apics = 0;
#if 0
volatile int imps_release_cpus = 0;
#endif
/*
* "imps_enabled" is non-zero if the probe sequence found IMPS
* information and was successful.
*/
static int imps_enabled = 0;
/*
* This represents the number of CPUs found.
*/
static int imps_num_cpus = 1;
/*
* This contains the local APIC hardware address.
*/
/*
* These map from virtual cpu numbers to APIC id's and back.
*/
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
{
unsigned sum = 0;
while (length-- > 0)
{
}
return (sum & 0xFF);
}
/*
* Primary function for booting individual CPUs.
*
* This must be modified to perform whatever OS-specific initialization
* that is required.
*/
static int
{
unsigned bootaddr, accept_status;
/* %%%%% ESB */
extern char patch_code[];
/*
* Generic CPU startup sequence starts here.
*/
/* set BIOS reset vector */
/* clear the error register */
{
IMPS_LAPIC_WRITE (LAPIC_ESR, 0);
}
#if 0
/* assert INIT IPI */
cfg &= LAPIC_DEST_MASK;
cfg &=;
/* %%%%% ESB finish adding startup sequence */
#endif
/* clean up BIOS reset vector */
*((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
{
KERNEL_PRINT ((" Processor [APIC id %d ver %d]: ",
{
KERNEL_PRINT (("DISABLED\n"));
return;
}
{
imps_any_new_apics = 1;
}
{
KERNEL_PRINT (("#0 Bootstrap Processor (BSP)\n"));
return;
}
{
/* XXXXX add OS-specific setup for secondary CPUs here */
}
}
static void
{
char str[8];
str[6] = 0;
/* XXXXX add OS-specific code here */
}
static void
{
KERNEL_PRINT ((" I/O APIC id %d ver %d, address: 0x%x ",
{
KERNEL_PRINT (("DISABLED\n"));
return;
}
KERNEL_PRINT (("\n"));
/* XXXXX add OS-specific code here */
}
static void
{
while (count-- > 0)
{
switch (*((unsigned char *) start))
{
case IMPS_BCT_PROCESSOR:
break;
case IMPS_BCT_BUS:
break;
case IMPS_BCT_IOAPIC:
break;
#if 0 /* XXXXX uncomment this if "add_io_interrupt" is implemented */
case IMPS_BCT_IO_INTERRUPT:
break;
#endif
#if 0 /* XXXXX uncomment this if "add_local_interrupt" is implemented */
case IMPS_BCT_LOCAL_INTERRUPT:
break;
#endif
default:
break;
}
start += 8;
}
}
static int
{
int sum;
{
KERNEL_PRINT ((" Invalid MP System Configuration type %d\n",
fps_ptr->feature_info[0]));
return 1;
}
{
{
return 1;
}
{
return 1;
}
if (local_cth_ptr->extended_length)
{
if (sum)
{
((" 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
{
int apicid;
char *str_ptr;
KERNEL_PRINT (("Intel MultiProcessor Spec 1.%d BIOS support detected\n",
/*
* 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;
}
{
str_ptr = "IMCR and PIC";
}
else
{
str_ptr = "Virtual Wire";
}
{
}
else
{
}
((" APIC config: \"%s mode\" Local APIC address: 0x%x\n",
/*
* Setup primary CPU.
*/
imps_cpu_apic_map[0] = apicid;
imps_apic_cpu_map[apicid] = 0;
{
str1[8] = 0;
str2[12] = 0;
}
else
{
*((volatile unsigned *) IOAPIC_ADDR_DEFAULT) = IOAPIC_ID;
= IMPS_APIC_ID (*((volatile unsigned *)
(IOAPIC_ADDR_DEFAULT + IOAPIC_RW)));
*((volatile unsigned *) IOAPIC_ADDR_DEFAULT) = IOAPIC_VER;
= APIC_VERSION (*((volatile unsigned *)
(IOAPIC_ADDR_DEFAULT + IOAPIC_RW)));
imps_num_cpus = 2;
{
}
{
}
{
}
{
}
{
}
}
/* %%%%% 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_DEBUG_PRINT (("Scanning from 0x%x for %d bytes\n",
while (length > 0)
{
{
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 *)
/*
* 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.
*/
#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.
*/
{
return 0;
}
* 1024 > mem_lower)
{
ebda_addr = 0;
}
{
return 1;
}
/*
* If no BIOS info on MPS hardware is found, then return failure.
*/
return 0;
}