2 Call into 16-bit BIOS code, Use AsmThunk16 function of BaseLib.
4 Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions
8 of the BSD License which accompanies this distribution. The
9 full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 #include "LegacyBiosInterface.h"
19 THUNK_CONTEXT mThunkContext
;
22 Sets the counter value for Timer #0 in a legacy 8254 timer.
24 @param Count - The 16-bit counter value to program into Timer #0 of the legacy 8254 timer.
32 IoWrite8 (TIMER_CONTROL_PORT
, TIMER0_CONTROL_WORD
);
33 IoWrite8 (TIMER0_COUNT_PORT
, (UINT8
) (Count
& 0xFF));
34 IoWrite8 (TIMER0_COUNT_PORT
, (UINT8
) ((Count
>>8) & 0xFF));
38 Thunk to 16-bit real mode and execute a software interrupt with a vector
39 of BiosInt. Regs will contain the 16-bit register context on entry and
42 @param This Protocol instance pointer.
43 @param BiosInt Processor interrupt vector to invoke
44 @param Regs Register contexted passed into (and returned) from thunk to
47 @retval FALSE Thunk completed, and there were no BIOS errors in the target code.
49 @retval TRUE There was a BIOS erro in the target code.
55 IN EFI_LEGACY_BIOS_PROTOCOL
*This
,
57 IN EFI_IA32_REGISTER_SET
*Regs
62 LEGACY_BIOS_INSTANCE
*Private
;
64 Private
= LEGACY_BIOS_INSTANCE_FROM_THIS (This
);
66 Regs
->X
.Flags
.Reserved1
= 1;
67 Regs
->X
.Flags
.Reserved2
= 0;
68 Regs
->X
.Flags
.Reserved3
= 0;
69 Regs
->X
.Flags
.Reserved4
= 0;
70 Regs
->X
.Flags
.IOPL
= 3;
76 // The base address of legacy interrupt vector table is 0.
77 // We use this base address to get the legacy interrupt handler.
79 DisableNullDetection ();
80 Segment
= (UINT16
)(((UINT32
*)0)[BiosInt
] >> 16);
81 Offset
= (UINT16
)((UINT32
*)0)[BiosInt
];
82 EnableNullDetection ();
84 return InternalLegacyBiosFarCall (
90 sizeof (Regs
->X
.Flags
)
95 Thunk to 16-bit real mode and call Segment:Offset. Regs will contain the
96 16-bit register context on entry and exit. Arguments can be passed on
99 @param This Protocol instance pointer.
100 @param Segment Segemnt of 16-bit mode call
101 @param Offset Offset of 16-bit mdoe call
102 @param Regs Register contexted passed into (and returned) from
104 @param Stack Caller allocated stack used to pass arguments
105 @param StackSize Size of Stack in bytes
107 @retval FALSE Thunk completed, and there were no BIOS errors in
108 the target code. See Regs for status.
109 @retval TRUE There was a BIOS erro in the target code.
114 LegacyBiosFarCall86 (
115 IN EFI_LEGACY_BIOS_PROTOCOL
*This
,
118 IN EFI_IA32_REGISTER_SET
*Regs
,
123 Regs
->X
.Flags
.Reserved1
= 1;
124 Regs
->X
.Flags
.Reserved2
= 0;
125 Regs
->X
.Flags
.Reserved3
= 0;
126 Regs
->X
.Flags
.Reserved4
= 0;
127 Regs
->X
.Flags
.IOPL
= 3;
128 Regs
->X
.Flags
.NT
= 0;
129 Regs
->X
.Flags
.IF
= 1;
130 Regs
->X
.Flags
.TF
= 0;
131 Regs
->X
.Flags
.CF
= 0;
133 return InternalLegacyBiosFarCall (This
, Segment
, Offset
, Regs
, Stack
, StackSize
);
137 Provide NULL interrupt handler which is used to check
138 if there is more than one HW interrupt registers with the CPU AP.
140 @param InterruptType - The type of interrupt that occured
141 @param SystemContext - A pointer to the system context when the interrupt occured
146 LegacyBiosNullInterruptHandler (
147 IN EFI_EXCEPTION_TYPE InterruptType
,
148 IN EFI_SYSTEM_CONTEXT SystemContext
154 Thunk to 16-bit real mode and call Segment:Offset. Regs will contain the
155 16-bit register context on entry and exit. Arguments can be passed on
158 @param This Protocol instance pointer.
159 @param Segment Segemnt of 16-bit mode call
160 @param Offset Offset of 16-bit mdoe call
161 @param Regs Register contexted passed into (and returned) from thunk to
163 @param Stack Caller allocated stack used to pass arguments
164 @param StackSize Size of Stack in bytes
166 @retval FALSE Thunk completed, and there were no BIOS errors in the target code.
168 @retval TRUE There was a BIOS erro in the target code.
173 InternalLegacyBiosFarCall (
174 IN EFI_LEGACY_BIOS_PROTOCOL
*This
,
177 IN EFI_IA32_REGISTER_SET
*Regs
,
183 LEGACY_BIOS_INSTANCE
*Private
;
186 IA32_REGISTER_SET ThunkRegSet
;
187 BOOLEAN InterruptState
;
190 Private
= LEGACY_BIOS_INSTANCE_FROM_THIS (This
);
192 ZeroMem (&ThunkRegSet
, sizeof (ThunkRegSet
));
193 ThunkRegSet
.X
.DI
= Regs
->X
.DI
;
194 ThunkRegSet
.X
.SI
= Regs
->X
.SI
;
195 ThunkRegSet
.X
.BP
= Regs
->X
.BP
;
196 ThunkRegSet
.X
.BX
= Regs
->X
.BX
;
197 ThunkRegSet
.X
.DX
= Regs
->X
.DX
;
199 // Sometimes, ECX is used to pass in 32 bit data. For example, INT 1Ah, AX = B10Dh is
200 // "PCI BIOS v2.0c + Write Configuration DWORD" and ECX has the dword to write.
202 ThunkRegSet
.E
.ECX
= Regs
->E
.ECX
;
203 ThunkRegSet
.X
.AX
= Regs
->X
.AX
;
204 ThunkRegSet
.E
.DS
= Regs
->X
.DS
;
205 ThunkRegSet
.E
.ES
= Regs
->X
.ES
;
207 CopyMem (&(ThunkRegSet
.E
.EFLAGS
.UintN
), &(Regs
->X
.Flags
), sizeof (Regs
->X
.Flags
));
210 // Clear the error flag; thunk code may set it. Stack16 should be the high address
211 // Make Statk16 address the low 16 bit must be not zero.
213 Stack16
= (UINT16
*)((UINT8
*) mThunkContext
.RealModeBuffer
+ mThunkContext
.RealModeBufferSize
- sizeof (UINT16
));
216 // Save current rate of DXE Timer
218 Private
->Timer
->GetTimerPeriod (Private
->Timer
, &TimerPeriod
);
221 // Disable DXE Timer while executing in real mode
223 Private
->Timer
->SetTimerPeriod (Private
->Timer
, 0);
226 // Save and disable interrupt of debug timer
228 InterruptState
= SaveAndSetDebugTimerInterrupt (FALSE
);
231 // The call to Legacy16 is a critical section to EFI
233 OriginalTpl
= gBS
->RaiseTPL (TPL_HIGH_LEVEL
);
236 // Check to see if there is more than one HW interrupt registers with the CPU AP.
237 // If there is, then ASSERT() since that is not compatible with the CSM because
238 // interupts other than the Timer interrupt that was disabled above can not be
239 // handled properly from real mode.
245 for (Vector
= 0x20, Count
= 0; Vector
< 0x100; Vector
++) {
246 Status
= Private
->Cpu
->RegisterInterruptHandler (Private
->Cpu
, Vector
, LegacyBiosNullInterruptHandler
);
247 if (Status
== EFI_ALREADY_STARTED
) {
250 if (Status
== EFI_SUCCESS
) {
251 Private
->Cpu
->RegisterInterruptHandler (Private
->Cpu
, Vector
, NULL
);
255 DEBUG ((EFI_D_ERROR
, "ERROR: More than one HW interrupt active with CSM enabled\n"));
261 // If the Timer AP has enabled the 8254 timer IRQ and the current 8254 timer
262 // period is less than the CSM required rate of 54.9254, then force the 8254
263 // PIT counter to 0, which is the CSM required rate of 54.9254 ms
265 if (Private
->TimerUses8254
&& TimerPeriod
< 549254) {
269 if (Stack
!= NULL
&& StackSize
!= 0) {
271 // Copy Stack to low memory stack
273 Stack16
-= StackSize
/ sizeof (UINT16
);
274 CopyMem (Stack16
, Stack
, StackSize
);
277 ThunkRegSet
.E
.SS
= (UINT16
) (((UINTN
) Stack16
>> 16) << 12);
278 ThunkRegSet
.E
.ESP
= (UINT16
) (UINTN
) Stack16
;
279 ThunkRegSet
.E
.CS
= Segment
;
280 ThunkRegSet
.E
.Eip
= Offset
;
282 mThunkContext
.RealModeState
= &ThunkRegSet
;
285 // Set Legacy16 state. 0x08, 0x70 is legacy 8259 vector bases.
287 Status
= Private
->Legacy8259
->SetMode (Private
->Legacy8259
, Efi8259LegacyMode
, NULL
, NULL
);
288 ASSERT_EFI_ERROR (Status
);
290 AsmThunk16 (&mThunkContext
);
293 // OPROM may allocate EBDA range by itself and change EBDA base and EBDA size.
294 // Get the current EBDA base address, and compared with pre-allocate minimum
295 // EBDA base address, if the current EBDA base address is smaller, it indicates
296 // PcdEbdaReservedMemorySize should be adjusted to larger for more OPROMs.
300 UINTN EbdaBaseAddress
;
301 UINTN ReservedEbdaBaseAddress
;
304 // Skip this part of debug code if NULL pointer detection is enabled
306 if ((PcdGet8 (PcdNullPointerDetectionPropertyMask
) & BIT0
) == 0) {
307 EbdaBaseAddress
= (*(UINT16
*) (UINTN
) 0x40E) << 4;
308 ReservedEbdaBaseAddress
= CONVENTIONAL_MEMORY_TOP
309 - PcdGet32 (PcdEbdaReservedMemorySize
);
310 ASSERT (ReservedEbdaBaseAddress
<= EbdaBaseAddress
);
315 if (Stack
!= NULL
&& StackSize
!= 0) {
317 // Copy low memory stack to Stack
319 CopyMem (Stack
, Stack16
, StackSize
);
323 // Restore protected mode interrupt state
325 Status
= Private
->Legacy8259
->SetMode (Private
->Legacy8259
, Efi8259ProtectedMode
, NULL
, NULL
);
326 ASSERT_EFI_ERROR (Status
);
328 mThunkContext
.RealModeState
= NULL
;
331 // Enable and restore rate of DXE Timer
333 Private
->Timer
->SetTimerPeriod (Private
->Timer
, TimerPeriod
);
336 // End critical section
338 gBS
->RestoreTPL (OriginalTpl
);
341 // Restore interrupt of debug timer
343 SaveAndSetDebugTimerInterrupt (InterruptState
);
345 Regs
->E
.EDI
= ThunkRegSet
.E
.EDI
;
346 Regs
->E
.ESI
= ThunkRegSet
.E
.ESI
;
347 Regs
->E
.EBP
= ThunkRegSet
.E
.EBP
;
348 Regs
->E
.EBX
= ThunkRegSet
.E
.EBX
;
349 Regs
->E
.EDX
= ThunkRegSet
.E
.EDX
;
350 Regs
->E
.ECX
= ThunkRegSet
.E
.ECX
;
351 Regs
->E
.EAX
= ThunkRegSet
.E
.EAX
;
352 Regs
->X
.SS
= ThunkRegSet
.E
.SS
;
353 Regs
->X
.CS
= ThunkRegSet
.E
.CS
;
354 Regs
->X
.DS
= ThunkRegSet
.E
.DS
;
355 Regs
->X
.ES
= ThunkRegSet
.E
.ES
;
357 CopyMem (&(Regs
->X
.Flags
), &(ThunkRegSet
.E
.EFLAGS
.UintN
), sizeof (Regs
->X
.Flags
));
359 return (BOOLEAN
) (Regs
->X
.Flags
.CF
== 1);
363 Allocate memory < 1 MB and copy the thunker code into low memory. Se up
366 @param Private Private context for Legacy BIOS
368 @retval EFI_SUCCESS Should only pass.
372 LegacyBiosInitializeThunk (
373 IN LEGACY_BIOS_INSTANCE
*Private
377 EFI_PHYSICAL_ADDRESS MemoryAddress
;
380 MemoryAddress
= (EFI_PHYSICAL_ADDRESS
) (UINTN
) Private
->IntThunk
;
382 mThunkContext
.RealModeBuffer
= (VOID
*) (UINTN
) (MemoryAddress
+ ((sizeof (LOW_MEMORY_THUNK
) / EFI_PAGE_SIZE
) + 1) * EFI_PAGE_SIZE
);
383 mThunkContext
.RealModeBufferSize
= EFI_PAGE_SIZE
;
384 mThunkContext
.ThunkAttributes
= THUNK_ATTRIBUTE_BIG_REAL_MODE
| THUNK_ATTRIBUTE_DISABLE_A20_MASK_INT_15
;
386 AsmPrepareThunk16 (&mThunkContext
);
389 // Get the interrupt vector number corresponding to IRQ0 from the 8259 driver
392 Status
= Private
->Legacy8259
->GetVector (Private
->Legacy8259
, Efi8259Irq0
, &TimerVector
);
393 ASSERT_EFI_ERROR (Status
);
396 // Check to see if the Timer AP has hooked the IRQ0 from the 8254 PIT
398 Status
= Private
->Cpu
->RegisterInterruptHandler (
401 LegacyBiosNullInterruptHandler
403 if (Status
== EFI_SUCCESS
) {
405 // If the Timer AP has not enabled the 8254 timer IRQ, then force the 8254 PIT
406 // counter to 0, which is the CSM required rate of 54.9254 ms
408 Private
->Cpu
->RegisterInterruptHandler (
416 // Save status that the Timer AP is not using the 8254 PIT
418 Private
->TimerUses8254
= FALSE
;
419 } else if (Status
== EFI_ALREADY_STARTED
) {
421 // Save status that the Timer AP is using the 8254 PIT
423 Private
->TimerUses8254
= TRUE
;
426 // Unexpected status from CPU AP RegisterInterruptHandler()