#include <Protocol/LoadedImage.h>\r
#include <Protocol/PciIo.h>\r
#include <Protocol/Cpu.h>\r
+#include <Protocol/Timer.h>\r
#include <Protocol/IsaIo.h>\r
#include <Protocol/LegacyRegion2.h>\r
#include <Protocol/SimpleTextIn.h>\r
#define CMOS_31 0x31 ///< CMOS 0x18\r
#define CMOS_32 0x32 ///< Century byte\r
\r
+//\r
+// 8254 Timer registers\r
+//\r
+#define TIMER0_COUNT_PORT 0x40\r
+#define TIMER1_COUNT_PORT 0x41\r
+#define TIMER2_COUNT_PORT 0x42\r
+#define TIMER_CONTROL_PORT 0x43\r
+\r
+//\r
+// Timer 0, Read/Write LSB then MSB, Square wave output, binary count use.\r
+//\r
+#define TIMER0_CONTROL_WORD 0x36 \r
\r
#define LEGACY_BIOS_INSTANCE_SIGNATURE SIGNATURE_32 ('L', 'B', 'I', 'T')\r
typedef struct {\r
//\r
EFI_CPU_ARCH_PROTOCOL *Cpu;\r
\r
+ //\r
+ // Timer Architectural Protocol \r
+ //\r
+ EFI_TIMER_ARCH_PROTOCOL *Timer;\r
+ BOOLEAN TimerUses8254; \r
+ \r
//\r
// Protocol to Lock and Unlock 0xc0000 - 0xfffff\r
//\r
// Interrupt control for thunk and PCI IRQ\r
//\r
EFI_LEGACY_8259_PROTOCOL *Legacy8259;\r
-\r
+ \r
//\r
// PCI Interrupt PIRQ control\r
//\r
/** @file\r
Call into 16-bit BIOS code, Use AsmThunk16 function of BaseLib.\r
\r
-Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>\r
\r
This program and the accompanying materials\r
are licensed and made available under the terms and conditions\r
\r
THUNK_CONTEXT mThunkContext;\r
\r
+/**\r
+ Sets the counter value for Timer #0 in a legacy 8254 timer.\r
+\r
+ @param Count - The 16-bit counter value to program into Timer #0 of the legacy 8254 timer.\r
+\r
+**/\r
+VOID\r
+SetPitCount (\r
+ IN UINT16 Count\r
+ )\r
+{\r
+ IoWrite8 (TIMER_CONTROL_PORT, TIMER0_CONTROL_WORD);\r
+ IoWrite8 (TIMER0_COUNT_PORT, (UINT8) (Count & 0xFF));\r
+ IoWrite8 (TIMER0_COUNT_PORT, (UINT8) ((Count>>8) & 0xFF));\r
+}\r
+\r
/**\r
Thunk to 16-bit real mode and execute a software interrupt with a vector\r
of BiosInt. Regs will contain the 16-bit register context on entry and\r
return InternalLegacyBiosFarCall (This, Segment, Offset, Regs, Stack, StackSize);\r
}\r
\r
+/**\r
+ Provide NULL interrupt handler which is used to check \r
+ if there is more than one HW interrupt registers with the CPU AP.\r
+\r
+ @param InterruptType - The type of interrupt that occured\r
+ @param SystemContext - A pointer to the system context when the interrupt occured\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+LegacyBiosNullInterruptHandler (\r
+ IN EFI_EXCEPTION_TYPE InterruptType,\r
+ IN EFI_SYSTEM_CONTEXT SystemContext\r
+ )\r
+{\r
+}\r
+\r
/**\r
Thunk to 16-bit real mode and call Segment:Offset. Regs will contain the\r
16-bit register context on entry and exit. Arguments can be passed on\r
EFI_TPL OriginalTpl;\r
IA32_REGISTER_SET ThunkRegSet;\r
BOOLEAN InterruptState;\r
+ UINT64 TimerPeriod;\r
\r
Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);\r
\r
Stack16 = (UINT16 *)((UINT8 *) mThunkContext.RealModeBuffer + mThunkContext.RealModeBufferSize - sizeof (UINT16));\r
\r
//\r
- // Save and disable interrutp of debug timer\r
+ // Save current rate of DXE Timer\r
+ //\r
+ Private->Timer->GetTimerPeriod (Private->Timer, &TimerPeriod);\r
+\r
+ //\r
+ // Disable DXE Timer while executing in real mode\r
+ //\r
+ Private->Timer->SetTimerPeriod (Private->Timer, 0);\r
+ \r
+ //\r
+ // Save and disable interrupt of debug timer\r
//\r
InterruptState = SaveAndSetDebugTimerInterrupt (FALSE);\r
\r
//\r
OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);\r
\r
+ //\r
+ // Check to see if there is more than one HW interrupt registers with the CPU AP.\r
+ // If there is, then ASSERT() since that is not compatible with the CSM because \r
+ // interupts other than the Timer interrupt that was disabled above can not be \r
+ // handled properly from real mode.\r
+ //\r
+ DEBUG_CODE (\r
+ UINTN Vector;\r
+ UINTN Count;\r
+ \r
+ for (Vector = 0x20, Count = 0; Vector < 0x100; Vector++) {\r
+ Status = Private->Cpu->RegisterInterruptHandler (Private->Cpu, Vector, LegacyBiosNullInterruptHandler);\r
+ if (Status == EFI_ALREADY_STARTED) {\r
+ Count++;\r
+ }\r
+ if (Status == EFI_SUCCESS) {\r
+ Private->Cpu->RegisterInterruptHandler (Private->Cpu, Vector, NULL);\r
+ }\r
+ }\r
+ if (Count >= 2) {\r
+ DEBUG ((EFI_D_ERROR, "ERROR: More than one HW interrupt active with CSM enabled\n"));\r
+ }\r
+ ASSERT (Count < 2);\r
+ );\r
+\r
+ //\r
+ // If the Timer AP has enabled the 8254 timer IRQ and the current 8254 timer \r
+ // period is less than the CSM required rate of 54.9254, then force the 8254 \r
+ // PIT counter to 0, which is the CSM required rate of 54.9254 ms\r
+ //\r
+ if (Private->TimerUses8254 && TimerPeriod < 549254) {\r
+ SetPitCount (0);\r
+ }\r
+ \r
if (Stack != NULL && StackSize != 0) {\r
//\r
// Copy Stack to low memory stack\r
gBS->RestoreTPL (OriginalTpl);\r
\r
//\r
- // Restore interrutp of debug timer\r
+ // Enable and restore rate of DXE Timer\r
+ //\r
+ Private->Timer->SetTimerPeriod (Private->Timer, TimerPeriod);\r
+ \r
+ //\r
+ // Restore interrupt of debug timer\r
//\r
SaveAndSetDebugTimerInterrupt (InterruptState);\r
\r
IN LEGACY_BIOS_INSTANCE *Private\r
)\r
{\r
+ EFI_STATUS Status;\r
EFI_PHYSICAL_ADDRESS MemoryAddress;\r
+ UINT8 TimerVector;\r
\r
MemoryAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) Private->IntThunk;\r
\r
\r
AsmPrepareThunk16 (&mThunkContext);\r
\r
+ //\r
+ // Get the interrupt vector number corresponding to IRQ0 from the 8259 driver\r
+ //\r
+ TimerVector = 0;\r
+ Status = Private->Legacy8259->GetVector (Private->Legacy8259, Efi8259Irq0, &TimerVector);\r
+ ASSERT_EFI_ERROR (Status);\r
+ \r
+ //\r
+ // Check to see if the Timer AP has hooked the IRQ0 from the 8254 PIT\r
+ // \r
+ Status = Private->Cpu->RegisterInterruptHandler (\r
+ Private->Cpu, \r
+ TimerVector, \r
+ LegacyBiosNullInterruptHandler\r
+ );\r
+ if (Status == EFI_SUCCESS) {\r
+ //\r
+ // If the Timer AP has not enabled the 8254 timer IRQ, then force the 8254 PIT \r
+ // counter to 0, which is the CSM required rate of 54.9254 ms\r
+ //\r
+ Private->Cpu->RegisterInterruptHandler (\r
+ Private->Cpu, \r
+ TimerVector, \r
+ NULL\r
+ );\r
+ SetPitCount (0);\r
+ \r
+ //\r
+ // Save status that the Timer AP is not using the 8254 PIT\r
+ //\r
+ Private->TimerUses8254 = FALSE;\r
+ } else if (Status == EFI_ALREADY_STARTED) {\r
+ //\r
+ // Save status that the Timer AP is using the 8254 PIT\r
+ //\r
+ Private->TimerUses8254 = TRUE;\r
+ } else {\r
+ //\r
+ // Unexpected status from CPU AP RegisterInterruptHandler()\r
+ //\r
+ ASSERT (FALSE);\r
+ }\r
+ \r
return EFI_SUCCESS;\r
}\r