mirror of https://github.com/acidanthera/audk.git
551 lines
15 KiB
C
551 lines
15 KiB
C
/** @file
|
|
Call into 16-bit BIOS code
|
|
|
|
BugBug: Thunker does A20 gate. Can we get rid of this code or
|
|
put it into Legacy16 code.
|
|
|
|
Copyright (c) 1999 - 2014, Intel Corporation. All rights reserved.<BR>
|
|
|
|
This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions
|
|
of the BSD License which accompanies this distribution. The
|
|
full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
**/
|
|
|
|
#include "LegacyBiosInterface.h"
|
|
#include "IpfThunk.h"
|
|
|
|
/**
|
|
Gets the current flat GDT and IDT descriptors and store them in
|
|
Private->IntThunk. These values are used by the Thunk code.
|
|
This method must be called before every thunk in order to assure
|
|
that the correct GDT and IDT are restored after the thunk.
|
|
|
|
@param Private Private context for Legacy BIOS
|
|
|
|
@retval EFI_SUCCESS Should only pass.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
LegacyBiosGetFlatDescs (
|
|
IN LEGACY_BIOS_INSTANCE *Private
|
|
)
|
|
{
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
BIOS interrupt call function.
|
|
|
|
@param BiosInt Int number of BIOS call
|
|
@param Segment Segment number
|
|
@param Offset Offset in segment
|
|
@param Regs IA32 Register set.
|
|
@param Stack Base address of stack
|
|
@param StackSize Size of stack
|
|
|
|
@retval EFI_SUCCESS BIOS interrupt call succeeds.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
BiosIntCall (
|
|
IN UINT16 BiosInt,
|
|
IN UINT16 Segment,
|
|
IN UINT16 Offset,
|
|
IN EFI_IA32_REGISTER_SET *Regs,
|
|
IN VOID *Stack,
|
|
IN UINTN StackSize
|
|
)
|
|
{
|
|
IPF_DWORD_REGS DwordRegs;
|
|
UINT64 IntTypeVariable;
|
|
|
|
IntTypeVariable = 0x8000000000000000;
|
|
IntTypeVariable |= (UINT64)BiosInt;
|
|
|
|
DwordRegs.Cs = Segment;
|
|
DwordRegs.Eip = Offset;
|
|
|
|
DwordRegs.Ds = Regs->X.DS;
|
|
DwordRegs.Es = Regs->X.ES;
|
|
DwordRegs.Fs = Regs->X.ES;
|
|
DwordRegs.Gs = Regs->X.ES;
|
|
DwordRegs.Ss = 0xFFFF;
|
|
|
|
DwordRegs.Eax = Regs->X.AX;
|
|
DwordRegs.Ebx = Regs->X.BX;
|
|
//
|
|
// Sometimes, ECX is used to pass in 32 bit data. For example, INT 1Ah, AX = B10Dh is
|
|
// "PCI BIOS v2.0c + Write Configuration DWORD" and ECX has the dword to write.
|
|
//
|
|
DwordRegs.Ecx = Regs->E.ECX;
|
|
DwordRegs.Edx = Regs->X.DX;
|
|
|
|
DwordRegs.Ebp = Regs->X.BP;
|
|
DwordRegs.Eflag = *((UINT16 *) &Regs->X.Flags);
|
|
|
|
DwordRegs.Edi = Regs->X.DI;
|
|
DwordRegs.Esi = Regs->X.SI;
|
|
DwordRegs.Esp = 0xFFFFFFFF;
|
|
|
|
EfiIaEntryPoint (IntTypeVariable, &DwordRegs, ((UINTN) Stack + StackSize), StackSize);
|
|
|
|
Regs->X.CS = DwordRegs.Cs;
|
|
|
|
Regs->X.DS = (UINT16) DwordRegs.Ds;
|
|
Regs->X.SS = (UINT16) DwordRegs.Ss;
|
|
|
|
Regs->E.EAX = DwordRegs.Eax;
|
|
Regs->E.EBX = DwordRegs.Ebx;
|
|
Regs->E.ECX = DwordRegs.Ecx;
|
|
Regs->E.EDX = DwordRegs.Edx;
|
|
|
|
Regs->E.EBP = DwordRegs.Ebp;
|
|
CopyMem (&Regs->X.Flags, &DwordRegs.Eflag, sizeof (EFI_FLAGS_REG));
|
|
|
|
Regs->E.EDI = DwordRegs.Edi;
|
|
Regs->E.ESI = DwordRegs.Esi;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Template of real mode code.
|
|
|
|
@param CodeStart Start address of code.
|
|
@param CodeEnd End address of code
|
|
@param ReverseThunkStart Start of reverse thunk.
|
|
@param IntThunk Low memory thunk.
|
|
|
|
**/
|
|
VOID
|
|
RealModeTemplate (
|
|
OUT UINTN *CodeStart,
|
|
OUT UINTN *CodeEnd,
|
|
OUT UINTN *ReverseThunkStart,
|
|
LOW_MEMORY_THUNK *IntThunk
|
|
)
|
|
{
|
|
SAL_RETURN_REGS SalStatus;
|
|
|
|
SalStatus = EsalGetReverseThunkAddress ();
|
|
|
|
*CodeStart = SalStatus.r9;
|
|
*CodeEnd = SalStatus.r10;
|
|
*ReverseThunkStart = SalStatus.r11;
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
Allocate memory < 1 MB and copy the thunker code into low memory. Se up
|
|
all the descriptors.
|
|
|
|
@param Private Private context for Legacy BIOS
|
|
|
|
@retval EFI_SUCCESS Should only pass.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
LegacyBiosInitializeThunk (
|
|
IN LEGACY_BIOS_INSTANCE *Private
|
|
)
|
|
{
|
|
GDT32 *CodeGdt;
|
|
GDT32 *DataGdt;
|
|
UINTN CodeStart;
|
|
UINTN CodeEnd;
|
|
UINTN ReverseThunkStart;
|
|
UINT32 Base;
|
|
LOW_MEMORY_THUNK *IntThunk;
|
|
UINTN TempData;
|
|
|
|
ASSERT (Private);
|
|
|
|
IntThunk = Private->IntThunk;
|
|
|
|
//
|
|
// Clear the reserved descriptor
|
|
//
|
|
ZeroMem (&(IntThunk->RealModeGdt[0]), sizeof (GDT32));
|
|
|
|
//
|
|
// Setup a descriptor for real-mode code
|
|
//
|
|
CodeGdt = &(IntThunk->RealModeGdt[1]);
|
|
|
|
//
|
|
// Fill in the descriptor with our real-mode segment value
|
|
//
|
|
CodeGdt->Type = 0xA;
|
|
//
|
|
// code/read
|
|
//
|
|
CodeGdt->System = 1;
|
|
CodeGdt->Dpl = 0;
|
|
CodeGdt->Present = 1;
|
|
CodeGdt->Software = 0;
|
|
CodeGdt->Reserved = 0;
|
|
CodeGdt->DefaultSize = 0;
|
|
//
|
|
// 16 bit operands
|
|
//
|
|
CodeGdt->Granularity = 0;
|
|
|
|
CodeGdt->LimitHi = 0;
|
|
CodeGdt->LimitLo = 0xffff;
|
|
|
|
Base = (*((UINT32 *) &IntThunk->Code));
|
|
CodeGdt->BaseHi = (Base >> 24) & 0xFF;
|
|
CodeGdt->BaseMid = (Base >> 16) & 0xFF;
|
|
CodeGdt->BaseLo = Base & 0xFFFF;
|
|
|
|
//
|
|
// Setup a descriptor for read-mode data
|
|
//
|
|
DataGdt = &(IntThunk->RealModeGdt[2]);
|
|
CopyMem (DataGdt, CodeGdt, sizeof (GDT32));
|
|
|
|
DataGdt->Type = 0x2;
|
|
//
|
|
// read/write data
|
|
//
|
|
DataGdt->BaseHi = 0x0;
|
|
//
|
|
// Base = 0
|
|
//
|
|
DataGdt->BaseMid = 0x0;
|
|
//
|
|
DataGdt->BaseLo = 0x0;
|
|
//
|
|
DataGdt->LimitHi = 0x0F;
|
|
//
|
|
// Limit = 4Gb
|
|
//
|
|
DataGdt->LimitLo = 0xFFFF;
|
|
//
|
|
DataGdt->Granularity = 0x1;
|
|
//
|
|
//
|
|
// Compute selector value
|
|
//
|
|
IntThunk->RealModeGdtDesc.Limit = (UINT16) (sizeof (IntThunk->RealModeGdt) - 1);
|
|
CopyMem (&IntThunk->RealModeGdtDesc.Base, (UINT32 *) &IntThunk->RealModeGdt, sizeof (UINT32));
|
|
//
|
|
// IntThunk->RealModeGdtDesc.Base = *((UINT32*) &IntThunk->RealModeGdt);
|
|
//
|
|
IntThunk->RealModeIdtDesc.Limit = 0xFFFF;
|
|
IntThunk->RealModeIdtDesc.Base = 0;
|
|
IntThunk->LowCodeSelector = (UINT32) ((UINTN) CodeGdt - IntThunk->RealModeGdtDesc.Base);
|
|
IntThunk->LowDataSelector = (UINT32) ((UINTN) DataGdt - IntThunk->RealModeGdtDesc.Base);
|
|
|
|
//
|
|
// Initialize low real-mode code thunk
|
|
//
|
|
RealModeTemplate (&CodeStart, &CodeEnd, &ReverseThunkStart, IntThunk);
|
|
|
|
TempData = (UINTN) &(IntThunk->Code);
|
|
IntThunk->LowReverseThunkStart = ((UINT32) TempData + (UINT32) (ReverseThunkStart - CodeStart));
|
|
|
|
EsalSetSalDataArea (TempData, (UINTN) IntThunk);
|
|
CopyMem (IntThunk->Code, (VOID *) CodeStart, CodeEnd - CodeStart);
|
|
|
|
IntThunk->EfiToLegacy16InitTable.ReverseThunkCallSegment = EFI_SEGMENT (*((UINT32 *) &IntThunk->LowReverseThunkStart));
|
|
IntThunk->EfiToLegacy16InitTable.ReverseThunkCallOffset = EFI_OFFSET (*((UINT32 *) &IntThunk->LowReverseThunkStart));
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
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
|
|
exit.
|
|
|
|
@param This Protocol instance pointer.
|
|
@param BiosInt Processor interrupt vector to invoke
|
|
@param Regs Register contexted passed into (and returned) from
|
|
thunk to 16-bit mode
|
|
|
|
@retval FALSE Thunk completed, and there were no BIOS errors in the
|
|
target code. See Regs for status.
|
|
@retval TRUE There was a BIOS erro in the target code.
|
|
|
|
**/
|
|
BOOLEAN
|
|
EFIAPI
|
|
LegacyBiosInt86 (
|
|
IN EFI_LEGACY_BIOS_PROTOCOL *This,
|
|
IN UINT8 BiosInt,
|
|
IN EFI_IA32_REGISTER_SET *Regs
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
LEGACY_BIOS_INSTANCE *Private;
|
|
LOW_MEMORY_THUNK *IntThunk;
|
|
UINT16 *Stack16;
|
|
EFI_TPL OriginalTpl;
|
|
UINTN IaSegment;
|
|
UINTN IaOffset;
|
|
UINTN *Address;
|
|
UINTN TempData;
|
|
|
|
Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
|
|
IntThunk = Private->IntThunk;
|
|
|
|
//
|
|
// Get the current flat GDT, IDT, and SS and store them in Private->IntThunk.
|
|
//
|
|
Status = LegacyBiosGetFlatDescs (Private);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
Regs->X.Flags.Reserved1 = 1;
|
|
Regs->X.Flags.Reserved2 = 0;
|
|
Regs->X.Flags.Reserved3 = 0;
|
|
Regs->X.Flags.Reserved4 = 0;
|
|
Regs->X.Flags.IOPL = 3;
|
|
Regs->X.Flags.NT = 0;
|
|
Regs->X.Flags.IF = 1;
|
|
Regs->X.Flags.TF = 0;
|
|
Regs->X.Flags.CF = 0;
|
|
//
|
|
// Clear the error flag; thunk code may set it.
|
|
//
|
|
Stack16 = (UINT16 *) (IntThunk->Stack + LOW_STACK_SIZE);
|
|
|
|
//
|
|
// Copy regs to low memory stack
|
|
//
|
|
Stack16 -= sizeof (EFI_IA32_REGISTER_SET) / sizeof (UINT16);
|
|
CopyMem (Stack16, Regs, sizeof (EFI_IA32_REGISTER_SET));
|
|
|
|
//
|
|
// Provide low stack esp
|
|
//
|
|
TempData = ((UINTN) Stack16) - ((UINTN) IntThunk);
|
|
IntThunk->LowStack = *((UINT32 *) &TempData);
|
|
|
|
//
|
|
// Stack for reverse thunk flat mode.
|
|
// It must point to top of stack (end of stack space).
|
|
//
|
|
TempData = ((UINTN) IntThunk->RevThunkStack) + LOW_STACK_SIZE;
|
|
IntThunk->RevFlatStack = *((UINT32 *) &TempData);
|
|
|
|
//
|
|
// The call to Legacy16 is a critical section to EFI
|
|
//
|
|
OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
|
|
|
|
//
|
|
// Set Legacy16 state. 0x08, 0x70 is legacy 8259 vector bases.
|
|
//
|
|
Status = Private->Legacy8259->SetMode (Private->Legacy8259, Efi8259LegacyMode, NULL, NULL);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
//
|
|
// Call the real mode thunk code
|
|
//
|
|
TempData = BiosInt * 4;
|
|
Address = (UINTN *) TempData;
|
|
IaOffset = 0xFFFF & (*Address);
|
|
IaSegment = 0xFFFF & ((*Address) >> 16);
|
|
|
|
Status = BiosIntCall (
|
|
BiosInt,
|
|
(UINT16) IaSegment,
|
|
(UINT16) IaOffset,
|
|
(EFI_IA32_REGISTER_SET *) Stack16,
|
|
IntThunk,
|
|
IntThunk->LowStack
|
|
);
|
|
|
|
//
|
|
// Check for errors with the thunk
|
|
//
|
|
switch (Status) {
|
|
case THUNK_OK:
|
|
break;
|
|
|
|
case THUNK_ERR_A20_UNSUP:
|
|
case THUNK_ERR_A20_FAILED:
|
|
default:
|
|
//
|
|
// For all errors, set EFLAGS.CF (used by legacy BIOS to indicate error).
|
|
//
|
|
Regs->X.Flags.CF = 1;
|
|
break;
|
|
}
|
|
|
|
Status = Private->Legacy8259->SetMode (Private->Legacy8259, Efi8259ProtectedMode, NULL, NULL);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
//
|
|
// End critical section
|
|
//
|
|
gBS->RestoreTPL (OriginalTpl);
|
|
|
|
//
|
|
// Return the resulting registers
|
|
//
|
|
CopyMem (Regs, Stack16, sizeof (EFI_IA32_REGISTER_SET));
|
|
|
|
return (BOOLEAN) (Regs->X.Flags.CF != 0);
|
|
}
|
|
|
|
|
|
/**
|
|
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
|
|
the Stack argument
|
|
|
|
@param This Protocol instance pointer.
|
|
@param Segment Segemnt of 16-bit mode call
|
|
@param Offset Offset of 16-bit mdoe call
|
|
@param Regs Register contexted passed into (and returned) from
|
|
thunk to 16-bit mode
|
|
@param Stack Caller allocated stack used to pass arguments
|
|
@param StackSize Size of Stack in bytes
|
|
|
|
@retval FALSE Thunk completed, and there were no BIOS errors in the
|
|
target code. See Regs for status.
|
|
@retval TRUE There was a BIOS erro in the target code.
|
|
|
|
**/
|
|
BOOLEAN
|
|
EFIAPI
|
|
LegacyBiosFarCall86 (
|
|
IN EFI_LEGACY_BIOS_PROTOCOL *This,
|
|
IN UINT16 Segment,
|
|
IN UINT16 Offset,
|
|
IN EFI_IA32_REGISTER_SET *Regs,
|
|
IN VOID *Stack,
|
|
IN UINTN StackSize
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
LEGACY_BIOS_INSTANCE *Private;
|
|
LOW_MEMORY_THUNK *IntThunk;
|
|
UINT16 *Stack16;
|
|
EFI_TPL OriginalTpl;
|
|
UINTN IaSegment;
|
|
UINTN IaOffset;
|
|
UINTN TempData;
|
|
|
|
Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
|
|
IntThunk = Private->IntThunk;
|
|
IaSegment = Segment;
|
|
IaOffset = Offset;
|
|
|
|
//
|
|
// Get the current flat GDT and IDT and store them in Private->IntThunk.
|
|
//
|
|
Status = LegacyBiosGetFlatDescs (Private);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
Regs->X.Flags.Reserved1 = 1;
|
|
Regs->X.Flags.Reserved2 = 0;
|
|
Regs->X.Flags.Reserved3 = 0;
|
|
Regs->X.Flags.Reserved4 = 0;
|
|
Regs->X.Flags.IOPL = 3;
|
|
Regs->X.Flags.NT = 0;
|
|
Regs->X.Flags.IF = 1;
|
|
Regs->X.Flags.TF = 0;
|
|
Regs->X.Flags.CF = 0;
|
|
//
|
|
// Clear the error flag; thunk code may set it.
|
|
//
|
|
Stack16 = (UINT16 *) (IntThunk->Stack + LOW_STACK_SIZE);
|
|
if (Stack != NULL && StackSize != 0) {
|
|
//
|
|
// Copy Stack to low memory stack
|
|
//
|
|
Stack16 -= StackSize / sizeof (UINT16);
|
|
CopyMem (Stack16, Stack, StackSize);
|
|
}
|
|
//
|
|
// Copy regs to low memory stack
|
|
//
|
|
Stack16 -= sizeof (EFI_IA32_REGISTER_SET) / sizeof (UINT16);
|
|
CopyMem (Stack16, Regs, sizeof (EFI_IA32_REGISTER_SET));
|
|
|
|
//
|
|
// Provide low stack esp
|
|
//
|
|
TempData = ((UINTN) Stack16) - ((UINTN) IntThunk);
|
|
IntThunk->LowStack = *((UINT32 *) &TempData);
|
|
|
|
//
|
|
// The call to Legacy16 is a critical section to EFI
|
|
//
|
|
OriginalTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
|
|
|
|
//
|
|
// Set Legacy16 state. 0x08, 0x70 is legacy 8259 vector bases.
|
|
//
|
|
Status = Private->Legacy8259->SetMode (Private->Legacy8259, Efi8259LegacyMode, NULL, NULL);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
//
|
|
// Call the real mode thunk code
|
|
//
|
|
Status = BiosIntCall (
|
|
0x100,
|
|
(UINT16) IaSegment,
|
|
(UINT16) IaOffset,
|
|
(EFI_IA32_REGISTER_SET *) Stack16,
|
|
IntThunk,
|
|
IntThunk->LowStack
|
|
);
|
|
|
|
//
|
|
// Check for errors with the thunk
|
|
//
|
|
switch (Status) {
|
|
case THUNK_OK:
|
|
break;
|
|
|
|
case THUNK_ERR_A20_UNSUP:
|
|
case THUNK_ERR_A20_FAILED:
|
|
default:
|
|
//
|
|
// For all errors, set EFLAGS.CF (used by legacy BIOS to indicate error).
|
|
//
|
|
Regs->X.Flags.CF = 1;
|
|
break;
|
|
}
|
|
//
|
|
// Restore protected mode interrupt state
|
|
//
|
|
Status = Private->Legacy8259->SetMode (Private->Legacy8259, Efi8259ProtectedMode, NULL, NULL);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
//
|
|
// End critical section
|
|
//
|
|
gBS->RestoreTPL (OriginalTpl);
|
|
|
|
//
|
|
// Return the resulting registers
|
|
//
|
|
CopyMem (Regs, Stack16, sizeof (EFI_IA32_REGISTER_SET));
|
|
Stack16 += sizeof (EFI_IA32_REGISTER_SET) / sizeof (UINT16);
|
|
|
|
if (Stack != NULL && StackSize != 0) {
|
|
//
|
|
// Copy low memory stack to Stack
|
|
//
|
|
CopyMem (Stack, Stack16, StackSize);
|
|
Stack16 += StackSize / sizeof (UINT16);
|
|
}
|
|
|
|
return (BOOLEAN) (Regs->X.Flags.CF != 0);
|
|
}
|