1282 lines
36 KiB
C
1282 lines
36 KiB
C
/*++
|
||
|
||
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);
|
||
//
|
||
}
|