607 lines
14 KiB
C
607 lines
14 KiB
C
/*++
|
||
|
||
Copyright (c) 1991 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
xxinithl.c
|
||
|
||
Abstract:
|
||
|
||
|
||
This module implements the initialization of the system dependent
|
||
functions that define the Hardware Architecture Layer (HAL) for an
|
||
Alpha machine
|
||
|
||
Author:
|
||
|
||
David N. Cutler (davec) 25-Apr-1991
|
||
Miche Baker-Harvey (miche) 18-May-1992
|
||
|
||
Environment:
|
||
|
||
Kernel mode only.
|
||
|
||
Revision History:
|
||
|
||
28-Jul-1992 Jeff McLeman (mcleman)
|
||
Add code to allocate a mapping buffer for buffered DMA
|
||
|
||
14-Jul-1992 Jeff McLeman (mcleman)
|
||
Add call to HalpCachePcrValues, which will call the PALcode to
|
||
cache values of the PCR that need fast access.
|
||
|
||
10-Jul-1992 Jeff McLeman (mcleman)
|
||
Remove reference to initializing the fixed TB entries, since Alpha
|
||
does not have fixed TB entries.
|
||
|
||
--*/
|
||
|
||
#include "halp.h"
|
||
#include "eisa.h"
|
||
#include "jxisa.h"
|
||
#include "jnsnrtc.h"
|
||
|
||
ULONG HalpBusType = MACHINE_TYPE_EISA;
|
||
ULONG HalpMapBufferSize;
|
||
PHYSICAL_ADDRESS HalpMapBufferPhysicalAddress;
|
||
|
||
typedef
|
||
BOOLEAN
|
||
KBUS_ERROR_ROUTINE (
|
||
IN struct _EXCEPTION_RECORD *ExceptionRecord,
|
||
IN struct _KEXCEPTION_FRAME *ExceptionFrame,
|
||
IN struct _KTRAP_FRAME *TrapFrame
|
||
);
|
||
|
||
KBUS_ERROR_ROUTINE HalMachineCheck;
|
||
|
||
//
|
||
// HalpClockFrequency is the processor cycle counter frequency in units
|
||
// of cycles per second (Hertz). It is a large number (e.g., 125,000,000)
|
||
// but will still fit in a ULONG.
|
||
//
|
||
// HalpClockMegaHertz is the processor cycle counter frequency in units
|
||
// of megahertz. It is a small number (e.g., 125) and is also the number
|
||
// of cycles per microsecond. The assumption here is that clock rates will
|
||
// always be an integral number of megahertz.
|
||
//
|
||
// Having the frequency available in both units avoids multiplications, or
|
||
// especially divisions in time critical code.
|
||
//
|
||
|
||
ULONG HalpClockFrequency;
|
||
ULONG HalpClockMegaHertz;
|
||
|
||
//
|
||
// Use the square wave mode of the PIT to measure the processor
|
||
// speed. The timer has a frequency of 1.193MHz. We want a
|
||
// square wave with a period of 50ms so we must initialize the
|
||
// pit with a count of:
|
||
// 50ms*1.193MHz = 59650 cycles
|
||
//
|
||
|
||
#define TIMER_REF_VALUE 59650
|
||
|
||
ULONG
|
||
HalpQuerySystemFrequency(
|
||
ULONG SampleTime
|
||
);
|
||
|
||
BOOLEAN
|
||
HalInitSystem (
|
||
IN ULONG Phase,
|
||
IN PLOADER_PARAMETER_BLOCK LoaderBlock
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function initializes the Hardware Architecture Layer (HAL) for an
|
||
Alpha system.
|
||
|
||
Arguments:
|
||
|
||
Phase - Supplies the initialization phase (zero or one).
|
||
|
||
LoaderBlock - Supplies a pointer to a loader parameter block.
|
||
|
||
Return Value:
|
||
|
||
A value of TRUE is returned is the initialization was successfully
|
||
complete. Otherwise a value of FALSE is returend.
|
||
|
||
--*/
|
||
|
||
{
|
||
PKPRCB Prcb;
|
||
|
||
if (Phase == 0) {
|
||
|
||
//
|
||
// Phase 0 initialization.
|
||
//
|
||
|
||
//
|
||
// Set the time increment value.
|
||
//
|
||
|
||
HalpCurrentTimeIncrement = MAXIMUM_INCREMENT;
|
||
HalpNextTimeIncrement = MAXIMUM_INCREMENT;
|
||
HalpNextRateSelect = 0;
|
||
KeSetTimeIncrement( MAXIMUM_INCREMENT, MINIMUM_INCREMENT );
|
||
|
||
HalpMapIoSpace();
|
||
HalpInitializeInterrupts();
|
||
HalpCreateDmaStructures();
|
||
HalpInitializeDisplay(LoaderBlock);
|
||
HalpCachePcrValues();
|
||
|
||
//
|
||
// Fill in handlers for APIs which this HAL supports
|
||
//
|
||
|
||
HalQuerySystemInformation = HaliQuerySystemInformation;
|
||
HalSetSystemInformation = HaliSetSystemInformation;
|
||
|
||
//
|
||
// Establish the machine check handler for in the PCR.
|
||
//
|
||
|
||
PCR->MachineCheckError = HalMachineCheck;
|
||
|
||
//
|
||
// Verify Prcb major version number, and build options are
|
||
// all conforming to this binary image
|
||
//
|
||
|
||
Prcb = KeGetCurrentPrcb();
|
||
#if DBG
|
||
if (!(Prcb->BuildType & PRCB_BUILD_DEBUG)) {
|
||
// This checked hal requires a checked kernel
|
||
KeBugCheckEx (MISMATCHED_HAL, 2, Prcb->BuildType, PRCB_BUILD_DEBUG, 0);
|
||
}
|
||
#else
|
||
if (Prcb->BuildType & PRCB_BUILD_DEBUG) {
|
||
// This free hal requires a free kernel
|
||
KeBugCheckEx (MISMATCHED_HAL, 2, Prcb->BuildType, 0, 0);
|
||
}
|
||
#endif
|
||
#ifndef NT_UP
|
||
if (Prcb->BuildType & PRCB_BUILD_UNIPROCESSOR) {
|
||
// This MP hal requires an MP kernel
|
||
KeBugCheckEx (MISMATCHED_HAL, 2, Prcb->BuildType, 0, 0);
|
||
}
|
||
#endif
|
||
if (Prcb->MajorVersion != PRCB_MAJOR_VERSION) {
|
||
KeBugCheckEx (MISMATCHED_HAL,
|
||
1, Prcb->MajorVersion, PRCB_MAJOR_VERSION, 0);
|
||
}
|
||
|
||
//
|
||
// Now alocate a mapping buffer for buffered DMA.
|
||
//
|
||
|
||
LessThan16Mb = FALSE;
|
||
|
||
HalpMapBufferSize = INITIAL_MAP_BUFFER_LARGE_SIZE;
|
||
HalpMapBufferPhysicalAddress.LowPart =
|
||
HalpAllocPhysicalMemory (LoaderBlock, MAXIMUM_ISA_PHYSICAL_ADDRESS,
|
||
HalpMapBufferSize >> PAGE_SHIFT, TRUE);
|
||
HalpMapBufferPhysicalAddress.HighPart = 0;
|
||
|
||
if (!HalpMapBufferPhysicalAddress.LowPart) {
|
||
HalpMapBufferSize = 0;
|
||
}
|
||
|
||
//
|
||
// Setup special memory AFTER we've allocated our COMMON BUFFER!
|
||
//
|
||
|
||
HalpInitializeSpecialMemory( LoaderBlock );
|
||
|
||
return TRUE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Phase 1 initialization.
|
||
//
|
||
|
||
HalpCalibrateStall();
|
||
|
||
//
|
||
// Initialize the existing bus handlers.
|
||
//
|
||
|
||
HalpRegisterInternalBusHandlers();
|
||
|
||
//
|
||
// Allocate pool for evnironment variable support
|
||
//
|
||
|
||
if (HalpEnvironmentInitialize() != 0) {
|
||
HalDisplayString(" No pool available for Environment Variables\n");
|
||
}
|
||
|
||
return TRUE;
|
||
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
HalInitializeProcessor (
|
||
IN ULONG Number
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called early in the initialization of the kernel
|
||
to perform platform dependent initialization for each processor
|
||
before the HAL Is fully functional.
|
||
|
||
N.B. When this routine is called, the PCR is present but is not
|
||
fully initialized.
|
||
|
||
Arguments:
|
||
|
||
Number - Supplies the number of the processor to initialize.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
return;
|
||
}
|
||
|
||
BOOLEAN
|
||
HalStartNextProcessor (
|
||
IN PLOADER_PARAMETER_BLOCK LoaderBlock,
|
||
IN PKPROCESSOR_STATE ProcessorState
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function is called to start the next processor.
|
||
|
||
Arguments:
|
||
|
||
LoaderBlock - Supplies a pointer to the loader parameter block.
|
||
|
||
ProcessorState - Supplies a pointer to the processor state to be
|
||
used to start the processor.
|
||
|
||
Return Value:
|
||
|
||
If a processor is successfully started, then a value of TRUE is
|
||
returned. Otherwise a value of FALSE is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
return FALSE;
|
||
}
|
||
VOID
|
||
HalpVerifyPrcbVersion ()
|
||
{
|
||
|
||
}
|
||
|
||
|
||
ULONG
|
||
HalpQuerySystemFrequency(
|
||
ULONG SampleTime
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine returns the speed at which the system is running in hertz.
|
||
The system frequency is calculated by counting the number of processor
|
||
cycles that occur during 500ms, using the Programmable Interval Timer
|
||
(PIT) as the reference time. The PIT is used to generate a square
|
||
wave with a 50ms Period. We use the Speaker counter since we can
|
||
enable and disable the count from software. The output of the
|
||
speaker is obtained from the SIO NmiStatus register.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
The system frequency in Hertz.
|
||
|
||
--*/
|
||
{
|
||
TIMER_CONTROL TimerControlSetup;
|
||
TIMER_CONTROL TimerControlReadStatus;
|
||
TIMER_STATUS TimerStatus;
|
||
NMI_STATUS NmiStatus;
|
||
PEISA_CONTROL controlBase;
|
||
ULONGLONG Count1;
|
||
ULONGLONG Count2;
|
||
ULONG NumberOfIntervals;
|
||
ULONG SquareWaveState = 0;
|
||
|
||
// mdbfix - move this into eisa.h one day
|
||
#define SB_READ_STATUS_ONLY 2
|
||
|
||
controlBase = HalpEisaControlBase;
|
||
|
||
//
|
||
// Disable the speaker counter.
|
||
//
|
||
|
||
*((PUCHAR) &NmiStatus) = READ_PORT_UCHAR(&controlBase->NmiStatus);
|
||
|
||
NmiStatus.SpeakerGate = 0;
|
||
NmiStatus.SpeakerData = 0;
|
||
|
||
// these are MBZ when writing to NMIMISC
|
||
NmiStatus.RefreshToggle = 0;
|
||
NmiStatus.SpeakerTimer = 0;
|
||
NmiStatus.IochkNmi = 0;
|
||
|
||
WRITE_PORT_UCHAR(&controlBase->NmiStatus, *((PUCHAR) &NmiStatus));
|
||
|
||
//
|
||
// Number of Square Wave transitions to count.
|
||
// at 50ms period, count the number of 25ms
|
||
// square wave transitions for a sample reference
|
||
// time to against which we measure processor cycle count.
|
||
//
|
||
|
||
NumberOfIntervals = (SampleTime/50) * 2;
|
||
|
||
//
|
||
// Set the timer for counter 0 in binary mode, square wave output
|
||
//
|
||
|
||
TimerControlSetup.BcdMode = 0;
|
||
TimerControlSetup.Mode = TM_SQUARE_WAVE;
|
||
TimerControlSetup.SelectByte = SB_LSB_THEN_MSB;
|
||
TimerControlSetup.SelectCounter = SELECT_COUNTER_2;
|
||
|
||
//
|
||
// Set the counter for a latched read of the status.
|
||
// We will poll the PIT for the state of the square
|
||
// wave output.
|
||
//
|
||
|
||
TimerControlReadStatus.BcdMode = 0;
|
||
TimerControlReadStatus.Mode = (1 << SELECT_COUNTER_2);
|
||
TimerControlReadStatus.SelectByte = SB_READ_STATUS_ONLY;
|
||
TimerControlReadStatus.SelectCounter = SELECT_READ_BACK;
|
||
|
||
|
||
//
|
||
// Write the count value LSB and MSB for a 50ms clock period
|
||
//
|
||
|
||
WRITE_PORT_UCHAR( &controlBase->CommandMode1,
|
||
*(PUCHAR)&TimerControlSetup );
|
||
|
||
WRITE_PORT_UCHAR( &controlBase->SpeakerTone,
|
||
TIMER_REF_VALUE & 0xff );
|
||
|
||
WRITE_PORT_UCHAR( &controlBase->SpeakerTone,
|
||
(TIMER_REF_VALUE >> 8) & 0xff );
|
||
|
||
//
|
||
// Enable the speaker counter but disable the SPKR output signal.
|
||
//
|
||
|
||
*((PUCHAR) &NmiStatus) = READ_PORT_UCHAR(&controlBase->NmiStatus);
|
||
|
||
NmiStatus.SpeakerGate = 1;
|
||
NmiStatus.SpeakerData = 0;
|
||
|
||
// these are MBZ when writing to NMIMISC
|
||
NmiStatus.RefreshToggle = 0;
|
||
NmiStatus.SpeakerTimer = 0;
|
||
NmiStatus.IochkNmi = 0;
|
||
|
||
WRITE_PORT_UCHAR(&controlBase->NmiStatus, *((PUCHAR) &NmiStatus));
|
||
|
||
//
|
||
// Synchronize with the counter before taking the first
|
||
// sample of the Processor Cycle Count (PCC). Since we
|
||
// are using the Square Wave Mode, wait until the next
|
||
// state change and then observe half a cycle before
|
||
// sampling.
|
||
//
|
||
|
||
//
|
||
// observe the low transition of the square wave output.
|
||
//
|
||
do {
|
||
|
||
*((PUCHAR) &NmiStatus) = READ_PORT_UCHAR(&controlBase->NmiStatus);
|
||
|
||
} while (NmiStatus.SpeakerTimer != SquareWaveState);
|
||
|
||
SquareWaveState ^= 1;
|
||
|
||
//
|
||
// observe the next transition of the square wave output and then
|
||
// take the first cycle counter sample.
|
||
//
|
||
do {
|
||
|
||
*((PUCHAR) &NmiStatus) = READ_PORT_UCHAR(&controlBase->NmiStatus);
|
||
|
||
} while (NmiStatus.SpeakerTimer != SquareWaveState);
|
||
|
||
Count1 = __RCC();
|
||
|
||
//
|
||
// Wait for the 500ms time period to pass and then take the
|
||
// second sample of the PCC. For a 50ms period, we have to
|
||
// observe eight wave transitions (25ms each).
|
||
//
|
||
|
||
do {
|
||
|
||
SquareWaveState ^= 1;
|
||
|
||
//
|
||
// wait for wave transition
|
||
//
|
||
do {
|
||
|
||
*((PUCHAR) &NmiStatus) = READ_PORT_UCHAR(&controlBase->NmiStatus);
|
||
|
||
} while (NmiStatus.SpeakerTimer != SquareWaveState);
|
||
|
||
} while (--NumberOfIntervals);
|
||
|
||
Count2 = __RCC();
|
||
|
||
//
|
||
// Disable the speaker counter.
|
||
//
|
||
|
||
*((PUCHAR) &NmiStatus) = READ_PORT_UCHAR(&controlBase->NmiStatus);
|
||
|
||
NmiStatus.SpeakerGate = 0;
|
||
NmiStatus.SpeakerData = 0;
|
||
|
||
WRITE_PORT_UCHAR(&controlBase->NmiStatus, *((PUCHAR) &NmiStatus));
|
||
|
||
//
|
||
// Calculate the Hz by the number of processor cycles
|
||
// elapsed during 1s.
|
||
//
|
||
// Hz = PCC/SampleTime * 1000ms/s
|
||
// = PCC * (1000/SampleTime)
|
||
//
|
||
|
||
// did the counter wrap? if so add 2^32
|
||
if (Count1 > Count2) {
|
||
|
||
Count2 += (ULONGLONG)(1 << 32);
|
||
|
||
}
|
||
|
||
return ((Count2 - Count1)*(((ULONG)1000)/SampleTime));
|
||
}
|
||
|
||
|
||
VOID
|
||
HalpInitializeProcessorParameters(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initalize the performance counter parameters
|
||
HalpClockFrequency and HalpClockMegaHertz based on the
|
||
estimated CPU speed. A 1s reference time is used for
|
||
the estimation.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
|
||
HalpClockFrequency = HalpQuerySystemFrequency(1000);
|
||
HalpClockMegaHertz = (HalpClockFrequency + 500000)/ 1000000;
|
||
|
||
}
|
||
|
||
#if 0
|
||
VOID
|
||
HalpGatherProcessorParameterStats(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine gathers statistics on the method for
|
||
estimating the system frequency.
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG Index;
|
||
ULONG Hertz[32];
|
||
ULONGLONG Mean = 0;
|
||
ULONGLONG Variance = 0;
|
||
ULONGLONG TempHertz;
|
||
|
||
//
|
||
// take 32 samples of estimated CPU speed,
|
||
// calculating the mean in the process.
|
||
//
|
||
DbgPrint("Sample\tFrequency\tMegaHertz\n\n");
|
||
|
||
for (Index = 0; Index < 32; Index++) {
|
||
Hertz[Index] = HalpQuerySystemFrequency(500);
|
||
Mean += Hertz[Index];
|
||
|
||
DbgPrint(
|
||
"%d\t%d\t%d\n",
|
||
Index,
|
||
Hertz[Index],
|
||
(ULONG)((Hertz[Index] + 500000)/1000000)
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// calculate the mean
|
||
//
|
||
|
||
Mean /= 32;
|
||
|
||
//
|
||
// calculate the variance
|
||
//
|
||
for (Index = 0; Index < 32; Index++) {
|
||
TempHertz = (Mean > Hertz[Index])?
|
||
(Mean - Hertz[Index]) : (Hertz[Index] - Mean);
|
||
TempHertz = TempHertz*TempHertz;
|
||
Variance += TempHertz;
|
||
}
|
||
|
||
Variance /= 32;
|
||
|
||
DbgPrint("\nResults\n\n");
|
||
DbgPrint(
|
||
"Mean = %d\nVariance = %d\nMegaHertz (derived) = %d\n",
|
||
Mean,
|
||
Variance,
|
||
(Mean + 500000)/ 1000000
|
||
);
|
||
|
||
}
|
||
#endif
|
||
|