/*++ Copyright (c) 1991 Microsoft Corporation Copyright (c) 1992, 1993 Digital Equipment Corporation Module Name: inithal.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. 24-Sep-1993 Joe Notarangelo Restructured to make this module platform-independent. --*/ #include "halp.h" #include "eisa.h" // // Declare the extern variable UncorrectableError declared in // inithal.c. // PERROR_FRAME PUncorrectableError; // // external // ULONG HalDisablePCIParityChecking = 0xffffffff; // // Define HAL spinlocks. // KSPIN_LOCK HalpBeepLock; KSPIN_LOCK HalpDisplayAdapterLock; KSPIN_LOCK HalpSystemInterruptLock; // // Mask of all of the processors that are currently active. // KAFFINITY HalpActiveProcessors; // // Mapping of the logical processor numbers to the physical processors. // ULONG HalpLogicalToPhysicalProcessor[HAL_MAXIMUM_PROCESSOR+1]; ULONG AlreadySet = 0; // // 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 = DEFAULT_PROCESSOR_FREQUENCY_MHZ; ULONGLONG HalpContiguousPhysicalMemorySize; // // 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 VOID HalpVerifyPrcbVersion( VOID ); VOID HalpRecurseLoaderBlock( IN PCONFIGURATION_COMPONENT_DATA CurrentEntry ); ULONG HalpQuerySystemFrequency( ULONG SampleTime ); VOID HalpAllocateUncorrectableFrame( VOID ); 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; ULONG BuildType = 0; Prcb = PCR->Prcb; // // Perform initialization for the primary processor. // if( Prcb->Number == HAL_PRIMARY_PROCESSOR ){ if (Phase == 0) { #if HALDBG DbgPrint( "HAL/HalInitSystem: Phase = %d\n", Phase ); DbgBreakPoint(); HalpDumpMemoryDescriptors( LoaderBlock ); #endif //HALDBG // // Get the memory Size. // HalpContiguousPhysicalMemorySize = HalpGetContiguousMemorySize( LoaderBlock ); // // Set second level cache size // NOTE: Although we set the PCR with the right cache size this // could be overridden by setting the Registry key // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet // \Control\Session Manager // \Memory Management\SecondLevelDataCache. // // If the secondlevel cache size is 0 or 512KB then it is // possible that the firmware is an old one. In which case // we determine the cache size here. If the value is anything // other than these then it is a new firmware and probably // reporting the correct cache size hence use this value. // if(LoaderBlock->u.Alpha.SecondLevelDcacheSize == 0 || LoaderBlock->u.Alpha.SecondLevelDcacheSize == 512*__1K){ PCR->SecondLevelCacheSize = HalpGetBCacheSize( HalpContiguousPhysicalMemorySize ); } else { PCR->SecondLevelCacheSize = LoaderBlock->u.Alpha.SecondLevelDcacheSize; } // // Initialize HAL spinlocks. // KeInitializeSpinLock(&HalpBeepLock); KeInitializeSpinLock(&HalpDisplayAdapterLock); KeInitializeSpinLock(&HalpSystemInterruptLock); // // Fill in handlers for APIs which this HAL supports // HalQuerySystemInformation = HaliQuerySystemInformation; HalSetSystemInformation = HaliSetSystemInformation; // // Phase 0 initialization. // HalpSetTimeIncrement(); HalpMapIoSpace(); HalpEstablishErrorHandler(); HalpInitializeDisplay(LoaderBlock); HalpInitializeMachineDependent( Phase, LoaderBlock ); HalpCreateDmaStructures(LoaderBlock); HalpInitializeInterrupts(); HalpVerifyPrcbVersion(); // // Set the processor active in the HAL active processor mask. // HalpActiveProcessors = 1 << Prcb->Number; // // Initialize the logical to physical processor mapping // for the primary processor. // HalpLogicalToPhysicalProcessor[0] = HAL_PRIMARY_PROCESSOR; return TRUE; } else { #if HALDBG DbgPrint( "HAL/HalInitSystem: Phase = %d\n", Phase ); DbgBreakPoint(); #endif //HALDBG // // Phase 1 initialization. // HalpInitializeClockInterrupts(); HalpInitializeMachineDependent( Phase, LoaderBlock ); // // Allocate memory for the uncorrectable frame // HalpAllocateUncorrectableFrame(); // // Initialize the Buffer for Uncorrectable Error. // HalpInitializeUncorrectableErrorFrame(); return TRUE; } } // // Perform necessary processor-specific initialization for // secondary processors. Phase is ignored as this will be called // only once on each secondary processor. // HalpMapIoSpace(); HalpInitializeInterrupts(); HalpInitializeMachineDependent( Phase, LoaderBlock ); // // Set the processor active in the HAL active processor mask. // HalpActiveProcessors |= 1 << Prcb->Number; #if HALDBG DbgPrint( "Secondary %d is alive\n", Prcb->Number ); #endif //HALDBG return TRUE; } VOID HalpAllocateUncorrectableFrame( VOID ) /*++ Routine Description: This function is called after the Phase1 Machine Dependent initialization. It must be called only after Phase1 machine-dependent initialization. This function allocates the necessary amountof memory for storing the uncorrectable error frame. This function makes a call to a machine- dependent function 'HalpGetMachineDependentErrorFrameSizes' for getting the size of the Processor Specific and System Specific error frame size. The machine-dependent code will know the size of these frames after the machine-dependent Phase1 initialization. Arguments: None. Return Value: None. --*/ { ULONG RawProcessorFrameSize; ULONG RawSystemFrameSize; ULONG EntireErrorFrameSize; // // First get the machine-dependent error frame sizes. // HalpGetMachineDependentErrorFrameSizes( &RawProcessorFrameSize, &RawSystemFrameSize); // // Compute the total size of the error frame // EntireErrorFrameSize = sizeof(ERROR_FRAME) + RawProcessorFrameSize + RawSystemFrameSize; // // Allocate space to store the error frame. // Not sure if it is OK to use ExAllocatePool at this instant. // We will give this a try if it doesn't work What do we do??!! // PUncorrectableError = ExAllocatePool(NonPagedPool, EntireErrorFrameSize); if(PUncorrectableError == NULL) { return; } PUncorrectableError->LengthOfEntireErrorFrame = EntireErrorFrameSize; // // if the size is not equal to zero then set the RawInformation pointers // to point to the right place. If not set the pointer to NULL and set // size to 0. // // // make Raw processor info to point right after the error frame. // if(RawProcessorFrameSize) { PUncorrectableError->UncorrectableFrame.RawProcessorInformation = (PVOID)((PUCHAR)PUncorrectableError + sizeof(ERROR_FRAME) ); PUncorrectableError->UncorrectableFrame.RawProcessorInformationLength = RawProcessorFrameSize; } else{ PUncorrectableError->UncorrectableFrame.RawProcessorInformation = NULL; PUncorrectableError->UncorrectableFrame.RawProcessorInformationLength = 0; } if(RawSystemFrameSize){ PUncorrectableError->UncorrectableFrame.RawSystemInformation = (PVOID)((PUCHAR)PUncorrectableError->UncorrectableFrame. RawProcessorInformation + RawProcessorFrameSize); PUncorrectableError->UncorrectableFrame.RawSystemInformationLength = RawSystemFrameSize; } else{ PUncorrectableError->UncorrectableFrame.RawSystemInformation = NULL; PUncorrectableError->UncorrectableFrame.RawSystemInformationLength = 0; } } VOID HalpGetProcessorInfo( PPROCESSOR_INFO pProcessorInfo ) /*++ Routine Description: Collects the Processor Information and fills in the buffer. Arguments: pProcessorInfo - Pointer to the PROCESSOR_INFO structure into which the processor information will be filled in. Return Value: None. --*/ { PKPRCB Prcb; pProcessorInfo->ProcessorType = PCR->ProcessorType; pProcessorInfo->ProcessorRevision = PCR->ProcessorRevision; Prcb = PCR->Prcb; pProcessorInfo->LogicalProcessorNumber = Prcb->Number; pProcessorInfo->PhysicalProcessorNumber = HalpLogicalToPhysicalProcessor[Prcb->Number]; return; } 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. --*/ { //jnfix - for mp systems if processor != 0 then init PALcode? //either here or in next module? 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. If a value of TRUE is returned, then the logical processor number is stored in the processor control block specified by the loader block. --*/ { ULONG LogicalNumber; PRESTART_BLOCK NextRestartBlock; ULONG PhysicalNumber; PKPRCB Prcb; #if !defined(NT_UP) // // If the address of the first restart parameter block is NULL, then // the host system is a uniprocessor system running with old firmware. // Otherwise, the host system may be a multiprocessor system if more // than one restart block is present. // // N.B. The first restart parameter block must be for the boot master // and must represent logical processor 0. // NextRestartBlock = SYSTEM_BLOCK->RestartBlock; if (NextRestartBlock == NULL) { return FALSE; } // // Scan the restart parameter blocks for a processor that is ready, // but not running. If a processor is found, then fill in the restart // processor state, set the logical processor number, and set start // in the boot status. // LogicalNumber = 0; PhysicalNumber = 0; do { // // If the processor is not ready then we assume that it is not // present. We must increment the physical processor number but // the logical processor number does not changed. // if( NextRestartBlock->BootStatus.ProcessorReady == FALSE ){ PhysicalNumber += 1; } else { // // Check if this processor has already been started. // If it has not then start it now. // if( NextRestartBlock->BootStatus.ProcessorStart == FALSE ){ RtlZeroMemory( &NextRestartBlock->u.Alpha, sizeof(ALPHA_RESTART_STATE)); NextRestartBlock->u.Alpha.IntA0 = ProcessorState->ContextFrame.IntA0; NextRestartBlock->u.Alpha.IntSp = ProcessorState->ContextFrame.IntSp; NextRestartBlock->u.Alpha.ReiRestartAddress = (ULONG)ProcessorState->ContextFrame.Fir; Prcb = (PKPRCB)(LoaderBlock->Prcb); Prcb->Number = (CCHAR)LogicalNumber; Prcb->RestartBlock = NextRestartBlock; NextRestartBlock->BootStatus.ProcessorStart = 1; HalpLogicalToPhysicalProcessor[LogicalNumber] = PhysicalNumber; return TRUE; } else { // // Ensure that the logical to physical mapping has been // established for this processor. // HalpLogicalToPhysicalProcessor[LogicalNumber] = PhysicalNumber; } LogicalNumber += 1; PhysicalNumber += 1; } NextRestartBlock = NextRestartBlock->NextRestartBlock; } while (NextRestartBlock != NULL); #endif // !defined(NT_UP) return FALSE; } VOID HalpVerifyPrcbVersion( VOID ) /*++ Routine Description: This function verifies that the HAL matches the kernel. If there is a mismatch the HAL bugchecks the system. Arguments: None. Return Value: None. --*/ { PKPRCB Prcb; // // 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); } } VOID HalpParseLoaderBlock( IN PLOADER_PARAMETER_BLOCK LoaderBlock ) { if (LoaderBlock == NULL) { return; } HalpRecurseLoaderBlock( (PCONFIGURATION_COMPONENT_DATA) LoaderBlock->ConfigurationRoot); } VOID HalpRecurseLoaderBlock( IN PCONFIGURATION_COMPONENT_DATA CurrentEntry ) /*++ Routine Description: This routine parses the loader parameter block looking for the PCI node. Once found, used to determine if PCI parity checking should be enabled or disabled. Set the default to not disable checking. Arguments: CurrentEntry - Supplies a pointer to a loader configuration tree or subtree. Return Value: None. --*/ { PCONFIGURATION_COMPONENT Component; PWSTR NameString; // // Quick out // if (AlreadySet) { return; } if (CurrentEntry) { Component = &CurrentEntry->ComponentEntry; if (Component->Class == AdapterClass && Component->Type == MultiFunctionAdapter) { if (strcmp(Component->Identifier, "PCI") == 0) { HalDisablePCIParityChecking = Component->Flags.ConsoleOut; AlreadySet = TRUE; #if HALDBG DbgPrint("ARC tree sets PCI parity checking to %s\n", HalDisablePCIParityChecking ? "OFF" : "ON"); #endif return; } } // // Process all the Siblings of current entry // HalpRecurseLoaderBlock(CurrentEntry->Sibling); // // Process all the Childeren of current entry // HalpRecurseLoaderBlock(CurrentEntry->Child); } } 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 (ULONG) ((Count2 - Count1)*(((ULONG)1000)/SampleTime)); } VOID HalpInitializeProcessorParameters( VOID ) /*++ Routine Description: This routine initalize the processor 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 DBG DbgPrint( "Frequency = %d\nMegaHertz = %d\n", HalpClockFrequency, HalpClockMegaHertz ); #endif } #if 0 VOID HalpGatherPerformanceParameterStats( 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