/*++
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