2 Call into 16-bit BIOS code
4 BugBug: Thunker does A20 gate. Can we get rid of this code or
5 put it into Legacy16 code.
7 Copyright (c) 1999 - 2014, Intel Corporation. All rights reserved.<BR>
9 This program and the accompanying materials
10 are licensed and made available under the terms and conditions
11 of the BSD License which accompanies this distribution. The
12 full text of the license may be found at
13 http://opensource.org/licenses/bsd-license.php
15 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
16 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
20 #include "LegacyBiosInterface.h"
24 Gets the current flat GDT and IDT descriptors and store them in
25 Private->IntThunk. These values are used by the Thunk code.
26 This method must be called before every thunk in order to assure
27 that the correct GDT and IDT are restored after the thunk.
29 @param Private Private context for Legacy BIOS
31 @retval EFI_SUCCESS Should only pass.
35 LegacyBiosGetFlatDescs (
36 IN LEGACY_BIOS_INSTANCE
*Private
44 BIOS interrupt call function.
46 @param BiosInt Int number of BIOS call
47 @param Segment Segment number
48 @param Offset Offset in segment
49 @param Regs IA32 Register set.
50 @param Stack Base address of stack
51 @param StackSize Size of stack
53 @retval EFI_SUCCESS BIOS interrupt call succeeds.
61 IN EFI_IA32_REGISTER_SET
*Regs
,
66 IPF_DWORD_REGS DwordRegs
;
67 UINT64 IntTypeVariable
;
69 IntTypeVariable
= 0x8000000000000000;
70 IntTypeVariable
|= (UINT64
)BiosInt
;
72 DwordRegs
.Cs
= Segment
;
73 DwordRegs
.Eip
= Offset
;
75 DwordRegs
.Ds
= Regs
->X
.DS
;
76 DwordRegs
.Es
= Regs
->X
.ES
;
77 DwordRegs
.Fs
= Regs
->X
.ES
;
78 DwordRegs
.Gs
= Regs
->X
.ES
;
79 DwordRegs
.Ss
= 0xFFFF;
81 DwordRegs
.Eax
= Regs
->X
.AX
;
82 DwordRegs
.Ebx
= Regs
->X
.BX
;
84 // Sometimes, ECX is used to pass in 32 bit data. For example, INT 1Ah, AX = B10Dh is
85 // "PCI BIOS v2.0c + Write Configuration DWORD" and ECX has the dword to write.
87 DwordRegs
.Ecx
= Regs
->E
.ECX
;
88 DwordRegs
.Edx
= Regs
->X
.DX
;
90 DwordRegs
.Ebp
= Regs
->X
.BP
;
91 DwordRegs
.Eflag
= *((UINT16
*) &Regs
->X
.Flags
);
93 DwordRegs
.Edi
= Regs
->X
.DI
;
94 DwordRegs
.Esi
= Regs
->X
.SI
;
95 DwordRegs
.Esp
= 0xFFFFFFFF;
97 EfiIaEntryPoint (IntTypeVariable
, &DwordRegs
, ((UINTN
) Stack
+ StackSize
), StackSize
);
99 Regs
->X
.CS
= DwordRegs
.Cs
;
101 Regs
->X
.DS
= (UINT16
) DwordRegs
.Ds
;
102 Regs
->X
.SS
= (UINT16
) DwordRegs
.Ss
;
104 Regs
->E
.EAX
= DwordRegs
.Eax
;
105 Regs
->E
.EBX
= DwordRegs
.Ebx
;
106 Regs
->E
.ECX
= DwordRegs
.Ecx
;
107 Regs
->E
.EDX
= DwordRegs
.Edx
;
109 Regs
->E
.EBP
= DwordRegs
.Ebp
;
110 CopyMem (&Regs
->X
.Flags
, &DwordRegs
.Eflag
, sizeof (EFI_FLAGS_REG
));
112 Regs
->E
.EDI
= DwordRegs
.Edi
;
113 Regs
->E
.ESI
= DwordRegs
.Esi
;
120 Template of real mode code.
122 @param CodeStart Start address of code.
123 @param CodeEnd End address of code
124 @param ReverseThunkStart Start of reverse thunk.
125 @param IntThunk Low memory thunk.
130 OUT UINTN
*CodeStart
,
132 OUT UINTN
*ReverseThunkStart
,
133 LOW_MEMORY_THUNK
*IntThunk
136 SAL_RETURN_REGS SalStatus
;
138 SalStatus
= EsalGetReverseThunkAddress ();
140 *CodeStart
= SalStatus
.r9
;
141 *CodeEnd
= SalStatus
.r10
;
142 *ReverseThunkStart
= SalStatus
.r11
;
148 Allocate memory < 1 MB and copy the thunker code into low memory. Se up
151 @param Private Private context for Legacy BIOS
153 @retval EFI_SUCCESS Should only pass.
157 LegacyBiosInitializeThunk (
158 IN LEGACY_BIOS_INSTANCE
*Private
165 UINTN ReverseThunkStart
;
167 LOW_MEMORY_THUNK
*IntThunk
;
172 IntThunk
= Private
->IntThunk
;
175 // Clear the reserved descriptor
177 ZeroMem (&(IntThunk
->RealModeGdt
[0]), sizeof (GDT32
));
180 // Setup a descriptor for real-mode code
182 CodeGdt
= &(IntThunk
->RealModeGdt
[1]);
185 // Fill in the descriptor with our real-mode segment value
193 CodeGdt
->Present
= 1;
194 CodeGdt
->Software
= 0;
195 CodeGdt
->Reserved
= 0;
196 CodeGdt
->DefaultSize
= 0;
200 CodeGdt
->Granularity
= 0;
202 CodeGdt
->LimitHi
= 0;
203 CodeGdt
->LimitLo
= 0xffff;
205 Base
= (*((UINT32
*) &IntThunk
->Code
));
206 CodeGdt
->BaseHi
= (Base
>> 24) & 0xFF;
207 CodeGdt
->BaseMid
= (Base
>> 16) & 0xFF;
208 CodeGdt
->BaseLo
= Base
& 0xFFFF;
211 // Setup a descriptor for read-mode data
213 DataGdt
= &(IntThunk
->RealModeGdt
[2]);
214 CopyMem (DataGdt
, CodeGdt
, sizeof (GDT32
));
220 DataGdt
->BaseHi
= 0x0;
224 DataGdt
->BaseMid
= 0x0;
226 DataGdt
->BaseLo
= 0x0;
228 DataGdt
->LimitHi
= 0x0F;
232 DataGdt
->LimitLo
= 0xFFFF;
234 DataGdt
->Granularity
= 0x1;
237 // Compute selector value
239 IntThunk
->RealModeGdtDesc
.Limit
= (UINT16
) (sizeof (IntThunk
->RealModeGdt
) - 1);
240 CopyMem (&IntThunk
->RealModeGdtDesc
.Base
, (UINT32
*) &IntThunk
->RealModeGdt
, sizeof (UINT32
));
242 // IntThunk->RealModeGdtDesc.Base = *((UINT32*) &IntThunk->RealModeGdt);
244 IntThunk
->RealModeIdtDesc
.Limit
= 0xFFFF;
245 IntThunk
->RealModeIdtDesc
.Base
= 0;
246 IntThunk
->LowCodeSelector
= (UINT32
) ((UINTN
) CodeGdt
- IntThunk
->RealModeGdtDesc
.Base
);
247 IntThunk
->LowDataSelector
= (UINT32
) ((UINTN
) DataGdt
- IntThunk
->RealModeGdtDesc
.Base
);
250 // Initialize low real-mode code thunk
252 RealModeTemplate (&CodeStart
, &CodeEnd
, &ReverseThunkStart
, IntThunk
);
254 TempData
= (UINTN
) &(IntThunk
->Code
);
255 IntThunk
->LowReverseThunkStart
= ((UINT32
) TempData
+ (UINT32
) (ReverseThunkStart
- CodeStart
));
257 EsalSetSalDataArea (TempData
, (UINTN
) IntThunk
);
258 CopyMem (IntThunk
->Code
, (VOID
*) CodeStart
, CodeEnd
- CodeStart
);
260 IntThunk
->EfiToLegacy16InitTable
.ReverseThunkCallSegment
= EFI_SEGMENT (*((UINT32
*) &IntThunk
->LowReverseThunkStart
));
261 IntThunk
->EfiToLegacy16InitTable
.ReverseThunkCallOffset
= EFI_OFFSET (*((UINT32
*) &IntThunk
->LowReverseThunkStart
));
268 Thunk to 16-bit real mode and execute a software interrupt with a vector
269 of BiosInt. Regs will contain the 16-bit register context on entry and
272 @param This Protocol instance pointer.
273 @param BiosInt Processor interrupt vector to invoke
274 @param Regs Register contexted passed into (and returned) from
277 @retval FALSE Thunk completed, and there were no BIOS errors in the
278 target code. See Regs for status.
279 @retval TRUE There was a BIOS erro in the target code.
285 IN EFI_LEGACY_BIOS_PROTOCOL
*This
,
287 IN EFI_IA32_REGISTER_SET
*Regs
291 LEGACY_BIOS_INSTANCE
*Private
;
292 LOW_MEMORY_THUNK
*IntThunk
;
300 Private
= LEGACY_BIOS_INSTANCE_FROM_THIS (This
);
301 IntThunk
= Private
->IntThunk
;
304 // Get the current flat GDT, IDT, and SS and store them in Private->IntThunk.
306 Status
= LegacyBiosGetFlatDescs (Private
);
307 ASSERT_EFI_ERROR (Status
);
309 Regs
->X
.Flags
.Reserved1
= 1;
310 Regs
->X
.Flags
.Reserved2
= 0;
311 Regs
->X
.Flags
.Reserved3
= 0;
312 Regs
->X
.Flags
.Reserved4
= 0;
313 Regs
->X
.Flags
.IOPL
= 3;
314 Regs
->X
.Flags
.NT
= 0;
315 Regs
->X
.Flags
.IF
= 1;
316 Regs
->X
.Flags
.TF
= 0;
317 Regs
->X
.Flags
.CF
= 0;
319 // Clear the error flag; thunk code may set it.
321 Stack16
= (UINT16
*) (IntThunk
->Stack
+ LOW_STACK_SIZE
);
324 // Copy regs to low memory stack
326 Stack16
-= sizeof (EFI_IA32_REGISTER_SET
) / sizeof (UINT16
);
327 CopyMem (Stack16
, Regs
, sizeof (EFI_IA32_REGISTER_SET
));
330 // Provide low stack esp
332 TempData
= ((UINTN
) Stack16
) - ((UINTN
) IntThunk
);
333 IntThunk
->LowStack
= *((UINT32
*) &TempData
);
336 // Stack for reverse thunk flat mode.
337 // It must point to top of stack (end of stack space).
339 TempData
= ((UINTN
) IntThunk
->RevThunkStack
) + LOW_STACK_SIZE
;
340 IntThunk
->RevFlatStack
= *((UINT32
*) &TempData
);
343 // The call to Legacy16 is a critical section to EFI
345 OriginalTpl
= gBS
->RaiseTPL (TPL_HIGH_LEVEL
);
348 // Set Legacy16 state. 0x08, 0x70 is legacy 8259 vector bases.
350 Status
= Private
->Legacy8259
->SetMode (Private
->Legacy8259
, Efi8259LegacyMode
, NULL
, NULL
);
351 ASSERT_EFI_ERROR (Status
);
354 // Call the real mode thunk code
356 TempData
= BiosInt
* 4;
357 Address
= (UINTN
*) TempData
;
358 IaOffset
= 0xFFFF & (*Address
);
359 IaSegment
= 0xFFFF & ((*Address
) >> 16);
361 Status
= BiosIntCall (
365 (EFI_IA32_REGISTER_SET
*) Stack16
,
371 // Check for errors with the thunk
377 case THUNK_ERR_A20_UNSUP
:
378 case THUNK_ERR_A20_FAILED
:
381 // For all errors, set EFLAGS.CF (used by legacy BIOS to indicate error).
383 Regs
->X
.Flags
.CF
= 1;
387 Status
= Private
->Legacy8259
->SetMode (Private
->Legacy8259
, Efi8259ProtectedMode
, NULL
, NULL
);
388 ASSERT_EFI_ERROR (Status
);
391 // End critical section
393 gBS
->RestoreTPL (OriginalTpl
);
396 // Return the resulting registers
398 CopyMem (Regs
, Stack16
, sizeof (EFI_IA32_REGISTER_SET
));
400 return (BOOLEAN
) (Regs
->X
.Flags
.CF
!= 0);
405 Thunk to 16-bit real mode and call Segment:Offset. Regs will contain the
406 16-bit register context on entry and exit. Arguments can be passed on
409 @param This Protocol instance pointer.
410 @param Segment Segemnt of 16-bit mode call
411 @param Offset Offset of 16-bit mdoe call
412 @param Regs Register contexted passed into (and returned) from
414 @param Stack Caller allocated stack used to pass arguments
415 @param StackSize Size of Stack in bytes
417 @retval FALSE Thunk completed, and there were no BIOS errors in the
418 target code. See Regs for status.
419 @retval TRUE There was a BIOS erro in the target code.
424 LegacyBiosFarCall86 (
425 IN EFI_LEGACY_BIOS_PROTOCOL
*This
,
428 IN EFI_IA32_REGISTER_SET
*Regs
,
434 LEGACY_BIOS_INSTANCE
*Private
;
435 LOW_MEMORY_THUNK
*IntThunk
;
442 Private
= LEGACY_BIOS_INSTANCE_FROM_THIS (This
);
443 IntThunk
= Private
->IntThunk
;
448 // Get the current flat GDT and IDT and store them in Private->IntThunk.
450 Status
= LegacyBiosGetFlatDescs (Private
);
451 ASSERT_EFI_ERROR (Status
);
453 Regs
->X
.Flags
.Reserved1
= 1;
454 Regs
->X
.Flags
.Reserved2
= 0;
455 Regs
->X
.Flags
.Reserved3
= 0;
456 Regs
->X
.Flags
.Reserved4
= 0;
457 Regs
->X
.Flags
.IOPL
= 3;
458 Regs
->X
.Flags
.NT
= 0;
459 Regs
->X
.Flags
.IF
= 1;
460 Regs
->X
.Flags
.TF
= 0;
461 Regs
->X
.Flags
.CF
= 0;
463 // Clear the error flag; thunk code may set it.
465 Stack16
= (UINT16
*) (IntThunk
->Stack
+ LOW_STACK_SIZE
);
466 if (Stack
!= NULL
&& StackSize
!= 0) {
468 // Copy Stack to low memory stack
470 Stack16
-= StackSize
/ sizeof (UINT16
);
471 CopyMem (Stack16
, Stack
, StackSize
);
474 // Copy regs to low memory stack
476 Stack16
-= sizeof (EFI_IA32_REGISTER_SET
) / sizeof (UINT16
);
477 CopyMem (Stack16
, Regs
, sizeof (EFI_IA32_REGISTER_SET
));
480 // Provide low stack esp
482 TempData
= ((UINTN
) Stack16
) - ((UINTN
) IntThunk
);
483 IntThunk
->LowStack
= *((UINT32
*) &TempData
);
486 // The call to Legacy16 is a critical section to EFI
488 OriginalTpl
= gBS
->RaiseTPL (TPL_HIGH_LEVEL
);
491 // Set Legacy16 state. 0x08, 0x70 is legacy 8259 vector bases.
493 Status
= Private
->Legacy8259
->SetMode (Private
->Legacy8259
, Efi8259LegacyMode
, NULL
, NULL
);
494 ASSERT_EFI_ERROR (Status
);
497 // Call the real mode thunk code
499 Status
= BiosIntCall (
503 (EFI_IA32_REGISTER_SET
*) Stack16
,
509 // Check for errors with the thunk
515 case THUNK_ERR_A20_UNSUP
:
516 case THUNK_ERR_A20_FAILED
:
519 // For all errors, set EFLAGS.CF (used by legacy BIOS to indicate error).
521 Regs
->X
.Flags
.CF
= 1;
525 // Restore protected mode interrupt state
527 Status
= Private
->Legacy8259
->SetMode (Private
->Legacy8259
, Efi8259ProtectedMode
, NULL
, NULL
);
528 ASSERT_EFI_ERROR (Status
);
531 // End critical section
533 gBS
->RestoreTPL (OriginalTpl
);
536 // Return the resulting registers
538 CopyMem (Regs
, Stack16
, sizeof (EFI_IA32_REGISTER_SET
));
539 Stack16
+= sizeof (EFI_IA32_REGISTER_SET
) / sizeof (UINT16
);
541 if (Stack
!= NULL
&& StackSize
!= 0) {
543 // Copy low memory stack to Stack
545 CopyMem (Stack
, Stack16
, StackSize
);
546 Stack16
+= StackSize
/ sizeof (UINT16
);
549 return (BOOLEAN
) (Regs
->X
.Flags
.CF
!= 0);