NT4/private/ntos/nthals/halcbus/i386/cbus.c
2020-09-30 17:12:29 +02:00

1282 lines
36 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1992, 1993, 1994 Corollary Inc.
Module Name:
cbus.c
Abstract:
This module implements the initialization of the system dependent
functions that define the Hardware Architecture Layer (HAL) for the
MP Corollary machines under Windows NT.
This includes the Corollary C-bus II machines which use Corollary's
CBC chips as well as Corollary C-bus I machines which use the Intel APIC.
Hardware dependencies of each C-bus backend are isolated in their
independent hardware modules. This module is completely hardware
independent.
Author:
Landy Wang (landy@corollary.com) 26-Mar-1992
Environment:
Kernel mode only.
Revision History:
--*/
#include "halp.h"
#include "cbus.h" // Cbus1 & Cbus2 max number of elements is here
#include "cbusrrd.h" // HAL <-> RRD interface definitions
#include "cbus_nt.h" // C-bus NT-specific implementation stuff
#include "cbusnls.h"
PVOID
HalpRemapVirtualAddress(IN PVOID, IN PVOID, IN BOOLEAN);
BOOLEAN
CbusMPMachine(VOID);
PUCHAR
CbusFindString (
IN PUCHAR Str,
IN PUCHAR StartAddr,
IN LONG Len
);
ULONG
CbusStringLength (
IN PUCHAR Str
);
ULONG
CbusReadExtIDs(
IN PEXT_ID_INFO From,
IN PEXT_ID_INFO To
);
PVOID
CbusMappings(
IN ULONG Processor,
IN PEXT_ID_INFO Idp
);
VOID
CbusMapMemoryRegisters(
IN PEXT_ID_INFO Idp
);
VOID
CbusEstablishMaps(
IN PEXT_ID_INFO Table,
IN ULONG Count
);
VOID
CbusReadRRD(VOID);
VOID
CbusCheckBusRanges(VOID);
VOID
CbusAddMemoryHoles(VOID);
VOID
CbusInitializeOtherPciBus(VOID);
VOID
HalInitializeProcessor(
IN ULONG Processor
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, CbusStringLength)
#pragma alloc_text(INIT, CbusFindString)
#pragma alloc_text(INIT, CbusReadExtIDs)
#pragma alloc_text(INIT, CbusMappings)
#pragma alloc_text(INIT, CbusMapMemoryRegisters)
#pragma alloc_text(INIT, CbusEstablishMaps)
#pragma alloc_text(INIT, CbusReadRRD)
#pragma alloc_text(PAGE, HalInitializeProcessor)
#endif
#define MIN(a,b) (((a)>(b))?(b):(a))
EXT_CFG_OVERRIDE_T CbusGlobal;
ULONG CbusProcessors;
ULONG CbusProcessorMask;
ULONG CbusBootedProcessors;
ULONG CbusBootedProcessorsMask;
ULONG CbusTemp;
//
// 8254 spinlock. This must be acquired before touching the 8254 chip.
//
ULONG Halp8254Lock;
ULONG HalpDefaultInterruptAffinity;
extern ULONG CbusVectorToIrql[MAXIMUM_IDTVECTOR + 1];
PULONG CbusTimeStamp;
//
// For Cbus1, the CbusCSR[] & CbusBroadcastCSR really point at
// Cbus1 I/O space which can vary from platform to platform.
//
// For Cbus2, the CbusCSR[] & CbusBroadcastCSR really do point at
// the Cbus2 CSR for the particular element.
//
PVOID CbusBroadcastCSR;
//
// Cbus information table for all elements (an element may not
// necessarily contain an x86 processor; ie: it may be a pure
// I/O element).
//
ELEMENT_T CbusCSR[MAX_CBUS_ELEMENTS];
MEMORY_CARD_T CbusMemoryBoards[MAX_ELEMENT_CSRS];
ULONG CbusMemoryBoardIndex;
//
// hardcoded size for now - see cbus.inc for the register definition
// and layout of CbusRebootRegs[].
//
ULONG CbusRebootRegs[8];
RRD_CONFIGURATION_T CbusJumpers;
EXT_ID_INFO_T CbusExtIDTable[MAX_CBUS_ELEMENTS];
ULONG CbusValidIDs;
ULONG CbusVectorToHwmap[MAXIMUM_IDTVECTOR + 1];
//
// Declare the task priority system vectors which vary from APIC to CBC.
// About the only ones that remain constant are high, low and APC & DPC.
// This is primarily due to shortcomings and errata in the APIC.
//
ULONG ProfileVector;
ULONG CbusIpiVector;
ULONG CbusClockVector;
ULONG CbusRedirVector;
ULONG CbusRebootVector;
//
// Declare these two pointers here for speed - it eliminates an
// extra asm instruction each time they are called.
//
VOID (*CbusRequestIPI)(IN ULONG);
VOID (*CbusRequestSoftwareInterrupt) ( IN KIRQL);
LARGE_INTEGER (*CbusQueryPerformanceCounter) ( IN OUT PLARGE_INTEGER);
ADDRESS_USAGE HalpCbusMemoryHole = {
NULL, CmResourceTypeMemory, InternalUsage,
{
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0
}
};
//
// this structure differs from the one above in that it only contains
// memory ranges that we want reserved for the HAL and ensure that
// devices do not get dynamically assigned memory from these ranges.
// specifically, the table above will include more ranges that the
// BIOS E820 function will remove, but that need to remain available
// for resources on the secondary peer PCI bus for C-bus II.
//
ADDRESS_USAGE HalpCbusMemoryResource = {
NULL, CmResourceTypeMemory, InternalUsage,
{
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0,
0, 0
}
};
ULONG CbusMemoryHoleIndex;
ULONG CbusMemoryResourceIndex;
ULONG
CbusStringLength (
IN PUCHAR Str
)
/*++
Routine Description:
Return the length of the input NULL-terminated Ansi string, including
the NULL terminator at the end.
Arguments:
Str - Supplies a pointer to the string
Return Value:
Length of the string in bytes
--*/
{
ULONG n;
for (n = 0; Str[n]; ++n)
;
return ++n;
}
PUCHAR
CbusFindString (
IN PUCHAR Str,
IN PUCHAR StartAddr,
IN LONG Len
)
/*++
Routine Description:
Searches a given virtual address for the specified string
up to the specified length.
Arguments:
Str - Supplies a pointer to the string
StartAddr - Supplies a pointer to memory to be searched
Len - Maximum length for the search
Return Value:
Pointer to the string if found, 0 if not.
--*/
{
LONG Index, n;
for (n = 0; Str[n]; ++n)
;
if (--n < 0) {
return StartAddr;
}
for (Len -= n; Len > 0; --Len, ++StartAddr) {
if ((StartAddr[0] == Str[0]) && (StartAddr[n] == Str[n])) {
for (Index = 1; Index < n; ++Index)
if (StartAddr[Index] != Str[Index])
break;
if (Index >= n) {
return StartAddr;
}
}
}
return (PUCHAR)0;
}
ULONG
CbusReadExtIDs(
IN PEXT_ID_INFO From,
IN PEXT_ID_INFO To
)
/*++
Routine Description:
Read in the C-bus II extended id information table.
Arguments:
From - Supplies a pointer to the RRD source table
To - Supplies a pointer to the destination storage for the table
Return Value:
Number of valid table entries.
--*/
{
ULONG Index = 0;
ULONG ValidEntries = 0;
for ( ; Index < MAX_CBUS_ELEMENTS && From->id != LAST_EXT_ID; Index++) {
//
// we cannot skip blank RRD entries
//
// if (From->pm == 0 && From->io_function == IOF_INVALID_ENTRY)
// continue;
RtlMoveMemory((PVOID)To, (PVOID)From, sizeof(EXT_ID_INFO_T));
From++;
To++;
ValidEntries++;
}
//
// WARNING: this is not necessarily the number of valid CPUs !!!
//
return ValidEntries;
}
PVOID
CbusMappings(
IN ULONG Processor,
IN PEXT_ID_INFO Idp
)
/*++
Routine Description:
Map a given processor's CSR space and save an idp pointer as well.
Arguments:
Processor - Supplies a logical processor number
Idp - Supplies an RRD extended ID pointer for this processor element
Return Value:
Opaque pointer to this processor's CSR space.c
--*/
{
//
// RRD specifies how much to map in per processor - this
// will usually be just 8K of the 64K CSR space for Cbus2.
// For Cbus1, RRD must give us a size which includes any
// processor-specific registers the HAL may access,
// generally indicated via the CbusGlobal structure.
//
CbusCSR[Processor].csr = HalpMapPhysicalMemoryWriteThrough (
(PVOID)Idp->pel_start,
(ULONG)ADDRESS_AND_SIZE_TO_SPAN_PAGES(
Idp->pel_start, Idp->pel_size));
CbusCSR[Processor].idp = (PVOID)Idp;
return CbusCSR[Processor].csr;
}
VOID
CbusMapMemoryRegisters(
IN PEXT_ID_INFO Idp
)
/*++
Routine Description:
Maps a given RRD entry into the HAL's memory board structures.
This is used later to determine ECC error addresses, etc.
Arguments:
Idp - Supplies a pointer to the RRD extended information structure entry
Return Value:
None.
--*/
{
PMEMORY_CARD pm;
pm = &CbusMemoryBoards[CbusMemoryBoardIndex];
pm->physical_start = Idp->pel_start;
pm->physical_size = Idp->pel_size;
pm->io_attr = (ULONG)Idp->io_attr;
//
// map in the csr space for this memory card
//
pm->regmap = HalpMapPhysicalMemoryWriteThrough (
(PVOID)Idp->io_start,
(ULONG)ADDRESS_AND_SIZE_TO_SPAN_PAGES(
Idp->io_start, Idp->io_size));
CbusMemoryBoardIndex++;
}
VOID
CbusEstablishMaps(
IN PEXT_ID_INFO Table,
IN ULONG Count
)
/*++
Routine Description:
Parse the given RRD extended ID configuration table, and construct
various HAL data structures accordingly.
Arguments:
Table - Supplies a pointer to the RRD extended information table
Count - Supplies a count of the maximum number of entries.
Return Value:
None.
--*/
{
ULONG Index, processor = 0;
ULONG HighVector;
ULONG Length;
PEXT_ID_INFO Idp = Table;
PUCHAR csr;
extern VOID CbusMemoryFree(ULONG, ULONG);
extern VOID CbusIOPresent(ULONG, ULONG, ULONG, ULONG, ULONG, PVOID);
extern ULONG IxProfileVector;
for (Index = 0; Index < Count; Index++, Idp++) {
//
// Map in the broadcast CSR. Note this is not a processor.
//
if (Idp->id == CbusGlobal.broadcast_id) {
CbusBroadcastCSR = HalpMapPhysicalMemoryWriteThrough (
(PVOID)Idp->pel_start,
(ULONG)ADDRESS_AND_SIZE_TO_SPAN_PAGES(
Idp->pel_start, Idp->pel_size));
//
// Register the broadcast element's memory
// mapped I/O space
//
continue;
}
//
// Establish virtual maps for each processor
//
if (Idp->pm) {
if ((UCHAR)Idp->id == CbusGlobal.bootid) {
CbusMappings(0, Idp);
}
else {
//
// We have an additional processor - set up
// his maps and put him in reset. We will
// boot him shortly.
//
processor++;
csr = (PUCHAR)CbusMappings(processor, Idp);
csr += CbusGlobal.smp_sreset;
*((PULONG)csr) = CbusGlobal.smp_sreset_val;
}
}
//
// Establish virtual maps for each I/O and/or
// memory board. Note that I/O devices may or may
// not have an attached processor - a CPU is NOT required!
// memory, on the other hand, may NOT have a processor
// on the same board.
//
switch (Idp->io_function) {
case IOF_INVALID_ENTRY:
case IOF_NO_IO:
break;
case IOF_MEMORY:
//
// If not a processor, must be a memory card.
// Add this memory card to our list
// of memory cards in the machine.
//
if (Idp->pm)
break;
CbusMapMemoryRegisters(Idp);
//
// Add this memory range to our list
// of additional memory to free later.
//
CbusMemoryFree(Idp->pel_start, Idp->pel_size);
break;
default:
//
// Add this I/O functionality to our table
// to make available to Cbus hardware drivers.
// Since I/O card interpretation of the RRD
// table is strictly up to the driver, do not
// try to register any of this element's space
// in the HAL's public consumption list, as we
// do for memory and processor cards.
//
if (Idp->pel_features & ELEMENT_HAS_IO) {
//
// If there was an attached processor,
// we already mapped in the CSR. If
// no attached processor, map in the
// CSR now. We'll need it later for
// interrupt vector enabling.
//
if (Idp->pm == 0) {
csr = HalpMapPhysicalMemoryWriteThrough (
(PVOID)Idp->pel_start,
(ULONG)ADDRESS_AND_SIZE_TO_SPAN_PAGES(
Idp->pel_start, Idp->pel_size));
}
CbusIOPresent(
(ULONG)Idp->id,
(ULONG)Idp->io_function,
(ULONG)Idp->io_attr,
Idp->pel_start,
Idp->pel_size,
(PVOID)csr );
}
break;
}
}
//
// Ensure that the memory subsystem does not use areas mapped out by
// e820 in determining the system memory ranges.
//
Length = 0xffffffff - CbusGlobal.cbusio;
AddMemoryHole(CbusGlobal.cbusio, Length + 1);
if (CbusBackend->AddMemoryHoles)
(*CbusBackend->AddMemoryHoles)();
HalpRegisterAddressUsage (&HalpCbusMemoryResource);
//
// Set total number of processors and their global mask
//
CbusProcessors = processor + 1;
CbusProcessorMask = (1 << CbusProcessors) - 1;
//
// Initialize the platform data - done only ONCE.
// Backends are expected to initialize the global Cbus
// spurious interrupt vector and the irqltovec[] table.
// We then pull the dispatch, wake and profile vectors
// from the table.
//
(*CbusBackend->InitializePlatform)();
CbusRequestIPI = CbusBackend->HalRequestInterrupt;
CbusRequestSoftwareInterrupt = CbusBackend->HalRequestSoftwareInterrupt;
CbusQueryPerformanceCounter = CbusBackend->HalQueryPerformanceCounter;
ProfileVector = CbusIrqlToVector[PROFILE_LEVEL];
CbusClockVector = CbusIrqlToVector[CLOCK2_LEVEL];
CbusIpiVector = CbusIrqlToVector[IPI_LEVEL];
HighVector = CbusIrqlToVector[HIGH_LEVEL];
CbusVectorToIrql[HighVector] = HIGH_LEVEL;
//
// Initialize the standard IxProfileVector so that we can
// reuse the standard profile code.
//
IxProfileVector = ProfileVector;
}
VOID
HalpResetAllProcessors(VOID)
/*++
Routine Description:
Called to put all the other processors in reset prior to reboot for
the Corollary architectures. Highly architecture specific.
Arguments:
None.
Return Value:
None.
--*/
{
ULONG Processor;
Processor = KeGetPcr()->HalReserved[PCR_PROCESSOR];
(*CbusBackend->ResetAllOtherProcessors)(Processor);
}
UCHAR ObsoleteMachine[] = MSG_OBSOLETE;
VOID
FatalError(
IN PUCHAR ErrorString
)
/*++
Routine Description:
Called to halt the HAL due to a fatal error, printing out a
string describing the cause of the failure.
Arguments:
ErrorString - Supplies a pointer to failure message
Return Value:
None.
--*/
{
HalDisplayString(ErrorString);
HalDisplayString(MSG_HALT);
while (1)
;
}
static ULONG RRDextsignature[] = { 0xfeedbeef, 0 };
static ULONG RRDsignature[] = { 0xdeadbeef, 0 };
static UCHAR CorollaryOwns[] = "Copyright(C) Corollary, Inc. 1991. All Rights Reserved";
VOID
CbusReadRRD(VOID)
/*++
Routine Description:
For robustness, we check for the following before concluding that
we are indeed a Corollary C-bus I or C-bus II licensee supported
in multiprocessor mode under this HAL:
a) Corollary string in the BIOS ROM 64K area (0x000F0000)
b) Corollary string in the RRD RAM/ROM 64K area (0xFFFE0000)
c) 2 Corollary extended configuration tables
in the RRD RAM/ROM 64K area (0xFFFE0000)
If any of the above checks fail, it is assumed that this machine
is either a non-Corollary machine or an early Corollary machine
not supported by this HAL. Both of these types of machines are,
however, supported (in uniprocessor mode) by the standard
uniprocessor HAL.
If the above checks succeed, then we proceed to fill in various
configuration structures for later use.
Arguments:
None.
Return Value:
None.
--*/
{
ULONG Index;
PEXT_CFG_HEADER p;
PVOID s;
ULONG OverrideLength = 0, EntryLength;
PUCHAR Bios;
//
// Map in the 64K (== 0x10 pages) of BIOS ROM @ 0xF0000
// and scan it for our signature...
//
Bios = (PUCHAR)HalpMapPhysicalMemory ((PVOID)0xF0000, 0x10);
if (!CbusFindString((PUCHAR)"Corollary", Bios, (LONG)0x10000))
KeBugCheck (MISMATCHED_HAL);
//
// Map in the 64K (== 0x10 pages) of RRD @ 0xFFFE0000 and
// scan it for our signature. (Note we are taking advantage
// of the fact that the lengths are the same, thus reusing
// the above PTEs, as opposed to allocating new ones).
//
for (Index = 0; Index < 0x10; Index++) {
HalpRemapVirtualAddress(
Bios + (Index << PAGE_SHIFT),
(PVOID)(0xFFFE0000 + (Index << PAGE_SHIFT)),
FALSE);
}
if (!CbusFindString((PUCHAR)"Corollary", Bios, (LONG)0x10000))
KeBugCheck (MISMATCHED_HAL);
//
// Map in the 32K (== 8 pages) of RRD RAM information @ 0xE0000,
// again reusing previously gained PTEs. Note we are only reusing
// the low half this time.
//
for (Index = 0; Index < 8; Index++) {
HalpRemapVirtualAddress(
Bios + (Index << PAGE_SHIFT),
(PVOID)(RRD_RAM + (Index << PAGE_SHIFT)),
FALSE);
}
if (!CbusFindString((PUCHAR)CorollaryOwns, Bios, (LONG)0x8000))
FatalError(ObsoleteMachine);
//
// At this point, we are assured that it is indeed a
// Corollary architecture machine. Search for our
// extended configuration tables, note we still search for
// the existence of our earliest 'configuration' structure,
// ie: the 0xdeadbeef version. This is not to find out where
// the memory is, but to find out which Cbus1 megabytes have been
// 'jumpered' so that I/O cards can use the RAM address(es) for
// their own dual-ported RAM buffers. This early configuration
// structure will not be present in Cbus2.
//
// If there is no extended configuration structure,
// this must be an old rom. NO SUPPORT FOR THESE.
//
s = (PVOID)CbusFindString((PUCHAR)RRDsignature, Bios,
(LONG)0x8000);
if (s) {
RtlMoveMemory((PVOID)&CbusJumpers, (PVOID)s, JUMPER_SIZE);
}
#if DBG
else {
//
// RRD configuration is not expected on Cbus2, but is for Cbus1
//
HalDisplayString("HAL: No RRD ROM configuration table\n");
}
#endif
//
// Now go for the extended configuration structure which will tell
// us about memory, processors and I/O devices.
//
p = (PEXT_CFG_HEADER)CbusFindString((PUCHAR)RRDextsignature,
Bios, (LONG)0x8000);
if (!p) {
#if DBG
HalDisplayString("HAL: No extended configuration table\n");
#endif
FatalError(ObsoleteMachine);
}
//
// Read in the 'extended ID information' table which,
// among other things, will give us the processor
// configuration.
//
// Multiple structures are strung together with a "checkword",
// "length", and "data" structure. The first null "checkword"
// entry marks the end of the extended configuration
// structure.
//
// We are only actively reading two types of structures, and
// they MUST be in the following order, although not necessarily
// consecutive:
//
// - ext_id_info
//
// - ext_cfg_override
//
// We ignore all other extended configuration entries built
// by RRD - they are mainly for early UNIX kernels.
//
do {
EntryLength = p->ext_cfg_length;
switch (p->ext_cfg_checkword) {
case EXT_ID_INFO:
CbusValidIDs = CbusReadExtIDs((PEXT_ID_INFO)(p+1),
(PEXT_ID_INFO)CbusExtIDTable);
break;
case EXT_CFG_OVERRIDE:
//
// We just copy the size of the structures
// we know about. If an rrd tries to pass us
// more than we know about, we ignore the
// overflow. Underflow is interpreted as
// "this must be a pre-XM machine", and such
// machines must default to the standard Windows NT
// uniprocessor HAL.
//
if (EntryLength < sizeof(EXT_CFG_OVERRIDE_T)) {
FatalError(MSG_RRD_ERROR);
}
OverrideLength = MIN(sizeof(EXT_CFG_OVERRIDE_T),
EntryLength);
RtlMoveMemory((PVOID)&CbusGlobal,
(PVOID)(p + 1), OverrideLength);
break;
case EXT_CFG_END:
//
// If ancient C-bus box, it's not supported in MP mode
//
if (CbusValidIDs == 0 || OverrideLength == 0) {
#if DBG
HalDisplayString("HAL: Missing RRD tables\n");
#endif
FatalError(ObsoleteMachine);
}
if (CbusMPMachine() == FALSE) {
#if DBG
HalDisplayString("HAL: This Corollary machine is not supported under this HAL\n");
#endif
FatalError(ObsoleteMachine);
}
(*CbusBackend->ParseRRD)((PVOID)CbusExtIDTable,
&CbusValidIDs);
CbusEstablishMaps(CbusExtIDTable, CbusValidIDs);
return;
default:
//
// Skip unused or unrecognized configuration entries
//
break;
}
//
// Get past the header, add in the length and then
// we're at the next entry.
//
p = (PEXT_CFG_HEADER) ((PUCHAR)(p + 1) + EntryLength);
} while (1);
// never reached
}
VOID
CbusCheckBusRanges(VOID)
/*++
Routine Description:
Check all buses and determine SystemBase
for all ranges within all buses.
Arguments:
None.
Return Value:
None.
--*/
{
if (CbusBackend->CheckBusRanges) {
(*CbusBackend->CheckBusRanges)();
}
}
VOID
CbusAddMemoryHoles(VOID)
/*++
Routine Description:
Find the holes in the C-bus memory space that is not
useable for device allocation.
Arguments:
None.
Return Value:
None.
--*/
{
if (CbusBackend->AddMemoryHoles) {
(*CbusBackend->AddMemoryHoles)();
}
}
VOID
CbusInitializeOtherPciBus(VOID)
/*++
Routine Description:
Find and initialize other PCI system buses.
Arguments:
None.
Return Value:
None.
--*/
{
if (CbusBackend->InitializeOtherPciBus) {
(*CbusBackend->InitializeOtherPciBus)();
}
}
VOID
HalpDisableAllInterrupts (VOID)
/*++
Routine Description:
This routine is called during a system crash. The Hal needs all
interrupts disabled. Interrupts will NOT be enabled upon leaving
this routine, nor is it allowed to turn them back on later. this
is a one-time thing, done as the system is coming down.
Disables all incoming interrupts for the calling processor.
Arguments:
None.
Return Value:
None - all interrupts are masked off
--*/
{
KfRaiseIrql(HIGH_LEVEL);
}
/*++
Routine Description:
Called by each processor in turn, to initialize himself.
- This is the earliest point at which the HAL gets control of
the system on each processor.
- The boot CPU runs it early on in kernel startup. Much later,
each additional CPU will also run it shortly after they are
brought out of reset during Phase 1.
- When called by the boot processor, this routine also reads in
the global RRD information which pertains to the entire system,
including all the processors.
- Later, the boot cpu will run HalInitSystem --> HalpInitMP.
This all occurs at Phase 0.
Much later, the Phase1 thread runs on the boot cpu, and calls
HalInitSystem --> HalpInitMP again, this time at Phase 1.
Then the boot cpu runs KeStartAllProcessors. this serially
invokes HalStartNextProcessor for each of our additional cpus
to start them running KiSystemStartup.
As each additional processor runs KiSystemStartup, he then
runs HalInitializeProcessor. This is when we will enable the
additional processor's incoming IPIs. After that, he will proceed
to KiInitializeKernel & ExpInitializeExecutive, who then calls
HalInitSystem. Each additional processor is always running
at Phase 1, never Phase 0.
The above dictates the actions of these routines:
- HalInitializeProcessor will read in (ONCE only, ie: only the
boot cpu will do this) all of the element space and set up
_global_ maps for each processor's CSR. each CPU will map
his own _local_ CSR into his HAL pcr. each CPU will
enable his incoming IPI here, and disable all other interrupts,
including all of this CPU's half-card CBC I/O interrupts.
- It would be nice to move some of the HalInitializeProcessor Phase0
code to HalInitSystem Phase0, but we need full mappings, etc,
for entering the debugger from KiSystemStartup early on.
- KeReadir/LowerIrq's must be available once this function
returns. (IPI's are only used once two or more processors are
available)
Arguments:
Processor - Supplies a logical processor number
Return Value:
None.
--*/
VOID
HalInitializeProcessor(
IN ULONG Processor
)
{
extern VOID i486cacheon(VOID);
extern VOID HalpInitializeCoreIntrs(VOID);
extern VOID CbusDefaultStall(VOID);
extern VOID HalpIpiHandler( VOID );
extern PULONG CbusVectorToEoi[MAXIMUM_IDTVECTOR + 1];
extern KSPIN_LOCK CbusVectorLock;
ULONG i;
ELEMENT_T csr;
PEXT_ID_INFO Idp;
extern KAFFINITY HalpActiveProcessors;
if (Processor == 0) {
//
// Find our signature and configuration information
//
CbusReadRRD();
//
// CbusVectorLock needs to be initialized before
// any CBC interrupt vectors are given out via
// HalGetInterruptVector().
//
KeInitializeSpinLock(&CbusVectorLock);
//
// Initialize the Eoi addresses to point at a known don't-care.
// Any interrupt that gets enabled later will get the correct
// EOI address filled in by the hardware backend interrupt
// enabling code.
//
for (i = 0 ; i <= MAXIMUM_IDTVECTOR; i++) {
CbusVectorToEoi[i] = &CbusTemp;
}
CbusTimeStamp = &CbusTemp;
}
//
// Default stall execution to something reasonable
// until we initialize it later in HalInitSystem.
//
CbusDefaultStall();
//
// Enable this processor's internal cache - do this
// before stall execution is initialized in HalInitSystem.
//
csr = CbusCSR[Processor];
Idp = (PEXT_ID_INFO)csr.idp;
//
// Enable the processor internal cache here...
//
if (Idp->proc_attr == PA_CACHE_ON) {
i486cacheon();
}
//
// Map this CPU's CSR stuff into his local address space for
// fast access. Also his logical # and bit position.
//
(PVOID) KeGetPcr()->HalReserved[PCR_CSR] = CbusCSR[Processor].csr;
(ULONG) KeGetPcr()->HalReserved[PCR_PROCESSOR] = Processor;
(ULONG) KeGetPcr()->HalReserved[PCR_BIT] = (1 << Processor);
(ULONG) KeGetPcr()->HalReserved[PCR_ALL_OTHERS] =
(CbusProcessorMask & ~(1 << Processor));
(PVOID) KeGetPcr()->HalReserved[PCR_LED_ON] = (PVOID)
((PUCHAR)CbusCSR[Processor].csr + CbusGlobal.smp_sled);
(PVOID) KeGetPcr()->HalReserved[PCR_LED_OFF] = (PVOID)
((PUCHAR)CbusCSR[Processor].csr + CbusGlobal.smp_cled);
//
// Since our architecture is completely symmetric,
// update affinity to contain each booted processor.
//
HalpDefaultInterruptAffinity |= (1 << Processor);
//
// This parameter is returned by the HAL when the system asks
// for the HAL's configured resources list.
//
HalpActiveProcessors = HalpDefaultInterruptAffinity;
CbusBootedProcessors += 1;
CbusBootedProcessorsMask = (1 << CbusBootedProcessors) - 1;
//
// Initialize this processor's data - done ONCE by each processor.
//
// Typically, this processor's interrupt controller is initialized
// here. Also, if it's the first processor, any I/O interrupt
// controllers will also be initialized here, ie: EISA bridges or
// I/O APICs.
//
(*CbusBackend->InitializeCPU)(Processor);
//
// This is where we actually enable IPI, APC, DPC and
// SPURIOUS interrupts. Device interrupts (like clock and
// profile) will not be enabled until HalInitSystem calls
// HalpInitializePICs later. Since we are still cli'd, no
// interrupt could actually bop us until KiSystemStartup
// calls KiInitializeKernel who drops IRQL.
//
HalpInitializeCoreIntrs();
}
#define CBUS1_NMI_MASK (PUCHAR)0x70
#define CBUS1_IO_CHANNEL_CHECK (PUCHAR)0x61
VOID
CbusClearEISANMI(VOID)
/*++
Routine Description:
This function clears the NMI on the EISA bus. Typically this
was generated by one of Corollary's "NMI cards", used for
debugging purposes. Our caller will have pointed us at the
correct bus bridge prior to calling us. note therefore we cannot
display anything because we may not be pointing at the default
display - if we want to display, we must map the bridge containing
the default video adapter!
Arguments:
None.
Return Value:
None.
--*/
{
UCHAR IoChannelCheck;
WRITE_PORT_UCHAR(CBUS1_NMI_MASK, (UCHAR)0);
IoChannelCheck = READ_PORT_UCHAR(CBUS1_IO_CHANNEL_CHECK);
WRITE_PORT_UCHAR(CBUS1_IO_CHANNEL_CHECK,
(UCHAR)((IoChannelCheck & 0xF) | 0x08));
IoChannelCheck = READ_PORT_UCHAR(CBUS1_IO_CHANNEL_CHECK);
WRITE_PORT_UCHAR(CBUS1_IO_CHANNEL_CHECK,
(UCHAR)(IoChannelCheck & 0x7));
//
// Since the NMI we are clearing was caused by pressing the button,
// which generated an EISA NMI (not a Cbus NMI), don't clear the
// NMI in Cbus space.
//
// COUTB(CbusCSR[Processor].csr, CbusGlobal.smp_cnmi,
// CbusGlobal.smp_cnmi_val);
//
}