diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBios.c b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBios.c index 75add5e39d..37f009af58 100644 --- a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBios.c +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBios.c @@ -736,6 +736,9 @@ LegacyBiosInstall ( Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &Private->Cpu); ASSERT_EFI_ERROR (Status); + Status = gBS->LocateProtocol (&gEfiTimerArchProtocolGuid, NULL, (VOID **) &Private->Timer); + ASSERT_EFI_ERROR (Status); + Status = gBS->LocateProtocol (&gEfiLegacyRegion2ProtocolGuid, NULL, (VOID **) &Private->LegacyRegion); ASSERT_EFI_ERROR (Status); diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBiosDxe.inf b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBiosDxe.inf index 593383c510..d8806a84ec 100644 --- a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBiosDxe.inf +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBiosDxe.inf @@ -121,6 +121,7 @@ gEfiDevicePathProtocolGuid # PROTOCOL ALWAYS_CONSUMED gEfiPciRootBridgeIoProtocolGuid # PROTOCOL ALWAYS_CONSUMED gEfiCpuArchProtocolGuid # PROTOCOL ALWAYS_CONSUMED + gEfiTimerArchProtocolGuid # PROTOCOL ALWAYS_CONSUMED gEfiIsaIoProtocolGuid # PROTOCOL ALWAYS_CONSUMED gEfiBlockIoProtocolGuid # PROTOCOL ALWAYS_CONSUMED gEfiPciIoProtocolGuid # PROTOCOL ALWAYS_CONSUMED @@ -138,5 +139,5 @@ gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdEbdaReservedMemorySize [Depex] - gEfiLegacyRegion2ProtocolGuid AND gEfiLegacyInterruptProtocolGuid AND gEfiLegacyBiosPlatformProtocolGuid AND gEfiLegacy8259ProtocolGuid AND gEfiGenericMemTestProtocolGuid AND gEfiCpuArchProtocolGuid + gEfiLegacyRegion2ProtocolGuid AND gEfiLegacyInterruptProtocolGuid AND gEfiLegacyBiosPlatformProtocolGuid AND gEfiLegacy8259ProtocolGuid AND gEfiGenericMemTestProtocolGuid AND gEfiCpuArchProtocolGuid AND gEfiTimerArchProtocolGuid diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBiosInterface.h b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBiosInterface.h index 15b3b7048c..8ab26fe326 100644 --- a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBiosInterface.h +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/LegacyBiosInterface.h @@ -29,6 +29,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include #include +#include #include #include #include @@ -517,6 +518,18 @@ extern UINTN mEndOpromShadowAddress; #define CMOS_31 0x31 ///< CMOS 0x18 #define CMOS_32 0x32 ///< Century byte +// +// 8254 Timer registers +// +#define TIMER0_COUNT_PORT 0x40 +#define TIMER1_COUNT_PORT 0x41 +#define TIMER2_COUNT_PORT 0x42 +#define TIMER_CONTROL_PORT 0x43 + +// +// Timer 0, Read/Write LSB then MSB, Square wave output, binary count use. +// +#define TIMER0_CONTROL_WORD 0x36 #define LEGACY_BIOS_INSTANCE_SIGNATURE SIGNATURE_32 ('L', 'B', 'I', 'T') typedef struct { @@ -532,6 +545,12 @@ typedef struct { // EFI_CPU_ARCH_PROTOCOL *Cpu; + // + // Timer Architectural Protocol + // + EFI_TIMER_ARCH_PROTOCOL *Timer; + BOOLEAN TimerUses8254; + // // Protocol to Lock and Unlock 0xc0000 - 0xfffff // @@ -543,7 +562,7 @@ typedef struct { // Interrupt control for thunk and PCI IRQ // EFI_LEGACY_8259_PROTOCOL *Legacy8259; - + // // PCI Interrupt PIRQ control // diff --git a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Thunk.c b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Thunk.c index c33288a2dd..9a62499f05 100644 --- a/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Thunk.c +++ b/IntelFrameworkModulePkg/Csm/LegacyBiosDxe/Thunk.c @@ -1,7 +1,7 @@ /** @file Call into 16-bit BIOS code, Use AsmThunk16 function of BaseLib. -Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions @@ -18,6 +18,22 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. THUNK_CONTEXT mThunkContext; +/** + Sets the counter value for Timer #0 in a legacy 8254 timer. + + @param Count - The 16-bit counter value to program into Timer #0 of the legacy 8254 timer. + +**/ +VOID +SetPitCount ( + IN UINT16 Count + ) +{ + IoWrite8 (TIMER_CONTROL_PORT, TIMER0_CONTROL_WORD); + IoWrite8 (TIMER0_COUNT_PORT, (UINT8) (Count & 0xFF)); + IoWrite8 (TIMER0_COUNT_PORT, (UINT8) ((Count>>8) & 0xFF)); +} + /** Thunk to 16-bit real mode and execute a software interrupt with a vector of BiosInt. Regs will contain the 16-bit register context on entry and @@ -103,6 +119,23 @@ LegacyBiosFarCall86 ( return InternalLegacyBiosFarCall (This, Segment, Offset, Regs, Stack, StackSize); } +/** + Provide NULL interrupt handler which is used to check + if there is more than one HW interrupt registers with the CPU AP. + + @param InterruptType - The type of interrupt that occured + @param SystemContext - A pointer to the system context when the interrupt occured + +**/ +VOID +EFIAPI +LegacyBiosNullInterruptHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ +} + /** Thunk to 16-bit real mode and call Segment:Offset. Regs will contain the 16-bit register context on entry and exit. Arguments can be passed on @@ -138,6 +171,7 @@ InternalLegacyBiosFarCall ( EFI_TPL OriginalTpl; IA32_REGISTER_SET ThunkRegSet; BOOLEAN InterruptState; + UINT64 TimerPeriod; Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This); @@ -165,7 +199,17 @@ InternalLegacyBiosFarCall ( Stack16 = (UINT16 *)((UINT8 *) mThunkContext.RealModeBuffer + mThunkContext.RealModeBufferSize - sizeof (UINT16)); // - // Save and disable interrutp of debug timer + // Save current rate of DXE Timer + // + Private->Timer->GetTimerPeriod (Private->Timer, &TimerPeriod); + + // + // Disable DXE Timer while executing in real mode + // + Private->Timer->SetTimerPeriod (Private->Timer, 0); + + // + // Save and disable interrupt of debug timer // InterruptState = SaveAndSetDebugTimerInterrupt (FALSE); @@ -174,6 +218,40 @@ InternalLegacyBiosFarCall ( // OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL); + // + // Check to see if there is more than one HW interrupt registers with the CPU AP. + // If there is, then ASSERT() since that is not compatible with the CSM because + // interupts other than the Timer interrupt that was disabled above can not be + // handled properly from real mode. + // + DEBUG_CODE ( + UINTN Vector; + UINTN Count; + + for (Vector = 0x20, Count = 0; Vector < 0x100; Vector++) { + Status = Private->Cpu->RegisterInterruptHandler (Private->Cpu, Vector, LegacyBiosNullInterruptHandler); + if (Status == EFI_ALREADY_STARTED) { + Count++; + } + if (Status == EFI_SUCCESS) { + Private->Cpu->RegisterInterruptHandler (Private->Cpu, Vector, NULL); + } + } + if (Count >= 2) { + DEBUG ((EFI_D_ERROR, "ERROR: More than one HW interrupt active with CSM enabled\n")); + } + ASSERT (Count < 2); + ); + + // + // If the Timer AP has enabled the 8254 timer IRQ and the current 8254 timer + // period is less than the CSM required rate of 54.9254, then force the 8254 + // PIT counter to 0, which is the CSM required rate of 54.9254 ms + // + if (Private->TimerUses8254 && TimerPeriod < 549254) { + SetPitCount (0); + } + if (Stack != NULL && StackSize != 0) { // // Copy Stack to low memory stack @@ -235,7 +313,12 @@ InternalLegacyBiosFarCall ( gBS->RestoreTPL (OriginalTpl); // - // Restore interrutp of debug timer + // Enable and restore rate of DXE Timer + // + Private->Timer->SetTimerPeriod (Private->Timer, TimerPeriod); + + // + // Restore interrupt of debug timer // SaveAndSetDebugTimerInterrupt (InterruptState); @@ -270,7 +353,9 @@ LegacyBiosInitializeThunk ( IN LEGACY_BIOS_INSTANCE *Private ) { + EFI_STATUS Status; EFI_PHYSICAL_ADDRESS MemoryAddress; + UINT8 TimerVector; MemoryAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) Private->IntThunk; @@ -280,5 +365,48 @@ LegacyBiosInitializeThunk ( AsmPrepareThunk16 (&mThunkContext); + // + // Get the interrupt vector number corresponding to IRQ0 from the 8259 driver + // + TimerVector = 0; + Status = Private->Legacy8259->GetVector (Private->Legacy8259, Efi8259Irq0, &TimerVector); + ASSERT_EFI_ERROR (Status); + + // + // Check to see if the Timer AP has hooked the IRQ0 from the 8254 PIT + // + Status = Private->Cpu->RegisterInterruptHandler ( + Private->Cpu, + TimerVector, + LegacyBiosNullInterruptHandler + ); + if (Status == EFI_SUCCESS) { + // + // If the Timer AP has not enabled the 8254 timer IRQ, then force the 8254 PIT + // counter to 0, which is the CSM required rate of 54.9254 ms + // + Private->Cpu->RegisterInterruptHandler ( + Private->Cpu, + TimerVector, + NULL + ); + SetPitCount (0); + + // + // Save status that the Timer AP is not using the 8254 PIT + // + Private->TimerUses8254 = FALSE; + } else if (Status == EFI_ALREADY_STARTED) { + // + // Save status that the Timer AP is using the 8254 PIT + // + Private->TimerUses8254 = TRUE; + } else { + // + // Unexpected status from CPU AP RegisterInterruptHandler() + // + ASSERT (FALSE); + } + return EFI_SUCCESS; }