diff options
Diffstat (limited to 'MdePkg/Library/BaseLib/x86Thunk.c')
-rw-r--r-- | MdePkg/Library/BaseLib/x86Thunk.c | 153 |
1 files changed, 133 insertions, 20 deletions
diff --git a/MdePkg/Library/BaseLib/x86Thunk.c b/MdePkg/Library/BaseLib/x86Thunk.c index 8cb5f4f..7045924 100644 --- a/MdePkg/Library/BaseLib/x86Thunk.c +++ b/MdePkg/Library/BaseLib/x86Thunk.c @@ -14,6 +14,35 @@ **/
+//
+// Byte packed structure for a segment descriptor in a GDT/LDT
+//
+typedef union {
+ struct {
+ UINT32 LimitLow:16;
+ UINT32 BaseLow:16;
+ UINT32 BaseMid:8;
+ UINT32 Type:4;
+ UINT32 S:1;
+ UINT32 DPL:2;
+ UINT32 P:1;
+ UINT32 LimitHigh:4;
+ UINT32 AVL:1;
+ UINT32 L:1;
+ UINT32 DB:1;
+ UINT32 G:1;
+ UINT32 BaseHigh:8;
+ } Bits;
+ UINT64 Uint64;
+} IA32_SEGMENT_DESCRIPTOR;
+
+extern CONST UINT8 m16Start;
+extern CONST UINT16 m16Size;
+extern CONST UINT16 mThunk16Attr;
+extern CONST UINT16 m16Gdt;
+extern CONST UINT16 m16GdtrBase;
+extern CONST UINT16 mTransition;
+
/**
Invokes 16-bit code in big real mode and returns the updated register set.
@@ -22,7 +51,7 @@ on the real mode stack and the starting address of the save area is returned.
@param RegisterSet Values of registers before invocation of 16-bit code.
- @param Patch Pointer to the area following the 16-bit code.
+ @param Transition Pointer to the transition code under 1MB.
@return The pointer to a IA32_REGISTER_SET structure containing the updated
register values.
@@ -31,10 +60,45 @@ IA32_REGISTER_SET *
InternalAsmThunk16 (
IN IA32_REGISTER_SET *RegisterSet,
- IN OUT VOID *Patch
+ IN OUT VOID *Transition
);
/**
+ Retrieves the properties for 16-bit thunk functions.
+
+ Computes the size of the buffer and stack below 1MB required to use the
+ AsmPrepareThunk16(), AsmThunk16() and AsmPrepareAndThunk16() functions. This
+ buffer size is returned in RealModeBufferSize, and the stack size is returned
+ in ExtraStackSize. If parameters are passed to the 16-bit real mode code,
+ then the actual minimum stack size is ExtraStackSize plus the maximum number
+ of bytes that need to be passed to the 16-bit real mode code.
+
+ If RealModeBufferSize is NULL, then ASSERT().
+ If ExtraStackSize is NULL, then ASSERT().
+
+ @param RealModeBufferSize A pointer to the size of the buffer below 1MB
+ required to use the 16-bit thunk functions.
+ @param ExtraStackSize A pointer to the extra size of stack below 1MB
+ that the 16-bit thunk functions require for
+ temporary storage in the transition to and from
+ 16-bit real mode.
+
+**/
+VOID
+EFIAPI
+AsmGetThunk16Properties (
+ OUT UINT32 *RealModeBufferSize,
+ OUT UINT32 *ExtraStackSize
+ )
+{
+ ASSERT (RealModeBufferSize != NULL);
+ ASSERT (ExtraStackSize != NULL);
+
+ *RealModeBufferSize = m16Size;
+ *ExtraStackSize = sizeof (IA32_DWORD_REGS) + 8;
+}
+
+/**
Prepares all structures a code required to use AsmThunk16().
Prepares all structures and code required to use AsmThunk16().
@@ -51,7 +115,64 @@ AsmPrepareThunk16 ( OUT THUNK_CONTEXT *ThunkContext
)
{
+ IA32_SEGMENT_DESCRIPTOR *RealModeGdt;
+
ASSERT (ThunkContext != NULL);
+ ASSERT ((UINTN)ThunkContext->RealModeBuffer < 0x100000);
+ ASSERT (ThunkContext->RealModeBufferSize >= m16Size);
+ ASSERT ((UINTN)ThunkContext->RealModeBuffer + m16Size <= 0x100000);
+ ASSERT (((UINTN)ThunkContext->RealModeBuffer & 0x0f) == 0);
+
+ CopyMem (ThunkContext->RealModeBuffer, &m16Start, m16Size);
+
+ //
+ // Point RealModeGdt to the GDT to be used in transition
+ //
+ // RealModeGdt[0]: Reserved as NULL descriptor
+ // RealModeGdt[1]: Code Segment
+ // RealModeGdt[2]: Data Segment
+ // RealModeGdt[3]: Call Gate
+ //
+ RealModeGdt = (IA32_SEGMENT_DESCRIPTOR*)(
+ (UINTN)ThunkContext->RealModeBuffer + m16Gdt);
+
+ //
+ // Update Code & Data Segment Descriptor
+ //
+ RealModeGdt[1].Bits.BaseLow =
+ (UINT32)(UINTN)ThunkContext->RealModeBuffer & ~0xf;
+ RealModeGdt[1].Bits.BaseMid =
+ (UINT32)(UINTN)ThunkContext->RealModeBuffer >> 16;
+
+ //
+ // Update transition code entry point offset
+ //
+ *(UINT32*)((UINTN)ThunkContext->RealModeBuffer + mTransition) +=
+ (UINT32)(UINTN)ThunkContext->RealModeBuffer & 0xf;
+
+ //
+ // Update Segment Limits for both Code and Data Segment Descriptors
+ //
+ if ((ThunkContext->ThunkAttributes & THUNK_ATTRIBUTE_BIG_REAL_MODE) == 0) {
+ //
+ // Set segment limits to 64KB
+ //
+ RealModeGdt[1].Bits.LimitHigh = 0;
+ RealModeGdt[1].Bits.G = 0;
+ RealModeGdt[2].Bits.LimitHigh = 0;
+ RealModeGdt[2].Bits.G = 0;
+ }
+
+ //
+ // Update GDTBASE for this thunk context
+ //
+ *(VOID**)((UINTN)ThunkContext->RealModeBuffer + m16GdtrBase) = RealModeGdt;
+
+ //
+ // Update Thunk Attributes
+ //
+ *(UINT32*)((UINTN)ThunkContext->RealModeBuffer + mThunk16Attr) =
+ ThunkContext->ThunkAttributes;
}
/**
@@ -74,28 +195,20 @@ AsmThunk16 ( IN OUT THUNK_CONTEXT *ThunkContext
)
{
- UINT16 *Patch;
+ IA32_REGISTER_SET *UpdatedRegs;
ASSERT (ThunkContext != NULL);
+ ASSERT ((UINTN)ThunkContext->RealModeBuffer < 0x100000);
+ ASSERT (ThunkContext->RealModeBufferSize >= m16Size);
+ ASSERT ((UINTN)ThunkContext->RealModeBuffer + m16Size <= 0x100000);
+ ASSERT (((UINTN)ThunkContext->RealModeBuffer & 0x0f) == 0);
- Patch = (UINT16*)(
- (UINTN)ThunkContext->RealModeCode +
- ThunkContext->RealModeCodeSize
- );
+ UpdatedRegs = InternalAsmThunk16 (
+ ThunkContext->RealModeState,
+ ThunkContext->RealModeBuffer
+ );
- //
- // 0x9a66 is the OpCode of far call with an operand size override.
- //
- *Patch = 0x9a66;
-
- //
- // CopyMem() here copies the updated register values back to RealModeState
- //
- CopyMem (
- &ThunkContext->RealModeState,
- InternalAsmThunk16 (&ThunkContext->RealModeState, Patch + 1),
- sizeof (ThunkContext->RealModeState)
- );
+ CopyMem (ThunkContext->RealModeState, UpdatedRegs, sizeof (*UpdatedRegs));
}
/**
|