/** @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 - 2018, 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
-of the BSD License which accompanies this distribution. The\r
-full text of the license may be found at\r
-http://opensource.org/licenses/bsd-license.php\r
-\r
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\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
IN EFI_IA32_REGISTER_SET *Regs\r
)\r
{\r
+ UINT16 Segment;\r
+ UINT16 Offset;\r
+\r
Regs->X.Flags.Reserved1 = 1;\r
Regs->X.Flags.Reserved2 = 0;\r
Regs->X.Flags.Reserved3 = 0;\r
Regs->X.Flags.IF = 0;\r
Regs->X.Flags.TF = 0;\r
Regs->X.Flags.CF = 0;\r
+ //\r
+ // The base address of legacy interrupt vector table is 0.\r
+ // We use this base address to get the legacy interrupt handler.\r
+ //\r
+ ACCESS_PAGE0_CODE (\r
+ Segment = (UINT16)(((UINT32 *)0)[BiosInt] >> 16);\r
+ Offset = (UINT16)((UINT32 *)0)[BiosInt];\r
+ );\r
\r
return InternalLegacyBiosFarCall (\r
This,\r
- (UINT16) (((UINT32 *)NULL)[BiosInt] >> 16),\r
- (UINT16) ((UINT32 *)NULL)[BiosInt],\r
+ Segment,\r
+ Offset,\r
Regs,\r
&Regs->X.Flags,\r
sizeof (Regs->X.Flags)\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
\r
AsmThunk16 (&mThunkContext);\r
\r
- //\r
- // OPROM may allocate EBDA range by itself and change EBDA base and EBDA size.\r
- // Get the current EBDA base address, and compared with pre-allocate minimum\r
- // EBDA base address, if the current EBDA base address is smaller, it indicates\r
- // PcdEbdaReservedMemorySize should be adjusted to larger for more OPROMs.\r
- //\r
- DEBUG_CODE (\r
- {\r
- UINTN EbdaBaseAddress;\r
- UINTN ReservedEbdaBaseAddress;\r
-\r
- EbdaBaseAddress = (*(UINT16 *) (UINTN) 0x40E) << 4;\r
- ReservedEbdaBaseAddress = CONVENTIONAL_MEMORY_TOP - PcdGet32 (PcdEbdaReservedMemorySize);\r
- ASSERT (ReservedEbdaBaseAddress <= EbdaBaseAddress);\r
- }\r
- );\r
-\r
if (Stack != NULL && StackSize != 0) {\r
//\r
// Copy low memory stack to Stack\r
\r
mThunkContext.RealModeState = NULL;\r
\r
+ //\r
+ // Enable and restore rate of DXE Timer\r
+ //\r
+ Private->Timer->SetTimerPeriod (Private->Timer, TimerPeriod);\r
+\r
//\r
// End critical section\r
//\r
gBS->RestoreTPL (OriginalTpl);\r
\r
//\r
- // Restore interrutp of debug timer\r
+ // OPROM may allocate EBDA range by itself and change EBDA base and EBDA size.\r
+ // Get the current EBDA base address, and compared with pre-allocate minimum\r
+ // EBDA base address, if the current EBDA base address is smaller, it indicates\r
+ // PcdEbdaReservedMemorySize should be adjusted to larger for more OPROMs.\r
+ //\r
+ DEBUG_CODE (\r
+ {\r
+ UINTN EbdaBaseAddress;\r
+ UINTN ReservedEbdaBaseAddress;\r
+\r
+ ACCESS_PAGE0_CODE (\r
+ EbdaBaseAddress = (*(UINT16 *) (UINTN) 0x40E) << 4;\r
+ ReservedEbdaBaseAddress = CONVENTIONAL_MEMORY_TOP\r
+ - PcdGet32 (PcdEbdaReservedMemorySize);\r
+ ASSERT (ReservedEbdaBaseAddress <= EbdaBaseAddress);\r
+ );\r
+ }\r
+ );\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