From 1112ad7822cb51057b001fcd46a7f012b5efafd7 Mon Sep 17 00:00:00 2001 From: Mikhail Krichanov Date: Tue, 16 Jan 2024 17:06:50 +0300 Subject: [PATCH] Ring3: Added EnterUserImage(). --- MdeModulePkg/Core/Dxe/DxeMain.h | 16 ++ MdeModulePkg/Core/Dxe/Image/Image.c | 30 ++- MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c | 1 + MdePkg/Include/Library/BaseLib.h | 157 ++++++++++++++++ MdePkg/Library/BaseLib/BaseLibInternals.h | 11 ++ MdePkg/Library/BaseLib/SwitchStack.c | 22 +++ MdePkg/Library/BaseLib/X64/SwitchStack.nasm | 48 +++++ UefiCpuPkg/Library/CpuArchLib/CpuGdt.c | 6 +- UefiCpuPkg/Library/CpuArchLib/CpuGdt.h | 172 +----------------- 9 files changed, 296 insertions(+), 167 deletions(-) diff --git a/MdeModulePkg/Core/Dxe/DxeMain.h b/MdeModulePkg/Core/Dxe/DxeMain.h index da6a744e88..eb6be0d22a 100644 --- a/MdeModulePkg/Core/Dxe/DxeMain.h +++ b/MdeModulePkg/Core/Dxe/DxeMain.h @@ -113,6 +113,8 @@ SPDX-License-Identifier: BSD-2-Clause-Patent /// #define DEPEX_STACK_SIZE_INCREMENT 0x1000 +#define USER_STACK_SIZE 0x20000 + typedef struct { EFI_GUID *ProtocolGuid; VOID **Protocol; @@ -2800,4 +2802,18 @@ MergeMemoryMap ( IN UINTN DescriptorSize ); +/** + Set UEFI image memory attributes. + + @param[in] BaseAddress Specified start address + @param[in] Length Specified length + @param[in] Attributes Specified attributes +**/ +VOID +SetUefiImageMemoryAttributes ( + IN UINT64 BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ); + #endif diff --git a/MdeModulePkg/Core/Dxe/Image/Image.c b/MdeModulePkg/Core/Dxe/Image/Image.c index 7a58d7b23b..2b45e3b30f 100644 --- a/MdeModulePkg/Core/Dxe/Image/Image.c +++ b/MdeModulePkg/Core/Dxe/Image/Image.c @@ -1596,6 +1596,9 @@ CoreStartImage ( UINTN SetJumpFlag; EFI_HANDLE Handle; UINT64 Attributes; + VOID *BaseOfStack; + VOID *TopOfStack; + UINTN SizeOfStack; Handle = ImageHandle; @@ -1694,7 +1697,32 @@ CoreStartImage ( gCpu->GetMemoryAttributes (gCpu, (EFI_PHYSICAL_ADDRESS)Image->EntryPoint, &Attributes); ASSERT ((Attributes & EFI_MEMORY_USER) != 0); - Image->Status = Image->EntryPoint (ImageHandle, Image->Info.SystemTable); + // + // Allocate 128KB for the User Stack. + // + BaseOfStack = AllocatePages (EFI_SIZE_TO_PAGES (USER_STACK_SIZE)); + ASSERT (BaseOfStack != NULL); + + SizeOfStack = EFI_SIZE_TO_PAGES (USER_STACK_SIZE) * EFI_PAGE_SIZE; + + SetUefiImageMemoryAttributes ((UINTN)BaseOfStack, SizeOfStack, EFI_MEMORY_XP | EFI_MEMORY_USER); + + // + // Compute the top of the allocated stack. Pre-allocate a UINTN for safety. + // + TopOfStack = (VOID *)((UINTN)BaseOfStack + SizeOfStack - CPU_STACK_ALIGNMENT); + TopOfStack = ALIGN_POINTER (TopOfStack, CPU_STACK_ALIGNMENT); + // DEBUG ((DEBUG_ERROR, "RING3_CODE64_SEL = 0x%x RING3_DATA64_SEL = 0x%x\n", (UINT16)RING3_CODE64_SEL, (UINT16)RING3_DATA64_SEL)); + + EnterUserImage ( + (SWITCH_STACK_ENTRY_POINT)(UINTN)Image->EntryPoint, + ImageHandle, + Image->Info.SystemTable, + TopOfStack, + (UINT16)RING3_CODE64_SEL, + (UINT16)RING3_DATA64_SEL + ); + Image->Status = EFI_SUCCESS; } // diff --git a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c index 910958370a..76ab3fbbd6 100644 --- a/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c +++ b/MdeModulePkg/Core/Dxe/Misc/MemoryProtection.c @@ -91,6 +91,7 @@ SetUefiImageMemoryAttributes ( Set UEFI image protection attributes. @param[in] ImageRecord A UEFI image record + @param[in] IsUser Whether UEFI image record is User Image. **/ VOID SetUefiImageProtectionAttributes ( diff --git a/MdePkg/Include/Library/BaseLib.h b/MdePkg/Include/Library/BaseLib.h index 0f4542e8ef..4890ce62e4 100644 --- a/MdePkg/Include/Library/BaseLib.h +++ b/MdePkg/Include/Library/BaseLib.h @@ -5114,6 +5114,17 @@ SwitchStack ( ... ); +VOID +EFIAPI +EnterUserImage ( + IN SWITCH_STACK_ENTRY_POINT EntryPoint, + IN VOID *Context1 OPTIONAL, + IN VOID *Context2 OPTIONAL, + IN VOID *NewStack, + IN UINT16 CodeSelector, + IN UINT16 DataSelector + ); + /** Generates a breakpoint on the CPU. @@ -5430,6 +5441,152 @@ typedef union { UINTN UintN; } IA32_CR4; +#pragma pack (1) + +// +// Global Descriptor Entry structures +// + +typedef struct { + UINT16 SegmentLimit_15_0; + UINT16 BaseAddress_15_0; + UINT8 BaseAddress_23_16; + UINT8 Type : 4; + UINT8 S : 1; + UINT8 DPL : 2; + UINT8 P : 1; + UINT8 SegmentLimit_19_16 : 4; + UINT8 AVL : 1; + UINT8 L : 1; + UINT8 D_B : 1; + UINT8 G : 1; + UINT8 BaseAddress_31_24; +} SEGMENT_DESCRIPTOR; + +typedef struct { + UINT16 SegmentLimit_15_0; + UINT16 BaseAddress_15_0; + UINT8 BaseAddress_23_16; + // + // Type + // + UINT8 Accessed : 1; + UINT8 Writable : 1; + UINT8 ExpansionDirection : 1; + UINT8 IsCode : 1; + UINT8 IsNotSystemSegment : 1; + UINT8 DescriptorPrivilegeLevel : 2; + UINT8 SegmentPresent : 1; + + UINT8 SegmentLimit_19_16 : 4; + UINT8 Available : 1; + UINT8 Reserved : 1; + UINT8 UpperBound : 1; + UINT8 Granularity : 1; + UINT8 BaseAddress_31_24; +} DATA_SEGMENT_32; + +typedef struct { + UINT16 SegmentLimit_15_0; + UINT16 BaseAddress_15_0; + UINT8 BaseAddress_23_16; + // + // Type + // + UINT8 Accessed : 1; + UINT8 Readable : 1; + UINT8 Conforming : 1; + UINT8 IsCode : 1; + UINT8 IsNotSystemSegment : 1; + UINT8 DescriptorPrivilegeLevel : 2; + UINT8 SegmentPresent : 1; + + UINT8 SegmentLimit_19_16 : 4; + UINT8 Available : 1; + UINT8 Reserved : 1; + UINT8 Is32Bit : 1; + UINT8 Granularity : 1; + UINT8 BaseAddress_31_24; +} CODE_SEGMENT_32; + +typedef struct { + UINT32 Reserved1; + UINT8 Reserved2; + // + // Type + // + UINT8 Accessed : 1; + UINT8 Readable : 1; + UINT8 Conforming : 1; + UINT8 IsCode : 1; + UINT8 IsNotSystemSegment : 1; + UINT8 DescriptorPrivilegeLevel : 2; + UINT8 SegmentPresent : 1; + + UINT8 Reserved3 : 4; + UINT8 Available : 1; + UINT8 LongMode : 1; + UINT8 Is32Bit : 1; + UINT8 Granularity : 1; + UINT8 Reserved4; +} CODE_SEGMENT_64; + +typedef struct { + UINT16 SegmentLimit_15_0; + UINT16 BaseAddress_15_0; + UINT8 BaseAddress_23_16; + + UINT8 Type : 4; + UINT8 IsNotSystemSegment : 1; + UINT8 DescriptorPrivilegeLevel : 2; + UINT8 SegmentPresent : 1; + + UINT8 SegmentLimit_19_16 : 4; + UINT8 Reserved : 3; + UINT8 Granularity : 1; + UINT8 BaseAddress_31_24; +} SYSTEM_SEGMENT; + +typedef struct { + UINT16 OffsetInSegment_15_0; + UINT16 SegmentSelector; + + UINT8 ParameterCount : 5; + UINT8 Reserved : 3; + + UINT8 Type : 4; + UINT8 IsNotSystemSegment : 1; + UINT8 DescriptorPrivilegeLevel : 2; + UINT8 SegmentPresent : 1; + UINT16 OffsetInSegment_31_16; +} CALL_GATE_32; + +typedef struct { + CALL_GATE_32 Common; + UINT32 OffsetInSegment_63_31; + UINT32 Reserved; +} CALL_GATE_64; + +typedef struct { + SEGMENT_DESCRIPTOR Null; + DATA_SEGMENT_32 Linear; + CODE_SEGMENT_32 LinearCode; + DATA_SEGMENT_32 SysData; + CODE_SEGMENT_32 SysCode; + CODE_SEGMENT_32 SysCode16; + DATA_SEGMENT_32 LinearData64; + CODE_SEGMENT_64 LinearCode64; + SEGMENT_DESCRIPTOR Spare5; + DATA_SEGMENT_32 Ring3Data64; + CODE_SEGMENT_64 Ring3Code64; + // CALL_GATE_64 FromRing3ToRing0; +} GDT; + +#pragma pack () + +#define RING3_DATA64_SEL OFFSET_OF (GDT, Ring3Data64) +#define RING3_CODE64_SEL OFFSET_OF (GDT, Ring3Code64) + /// /// Byte packed structure for a segment descriptor in a GDT/LDT. /// diff --git a/MdePkg/Library/BaseLib/BaseLibInternals.h b/MdePkg/Library/BaseLib/BaseLibInternals.h index d61e604385..340ad96284 100644 --- a/MdePkg/Library/BaseLib/BaseLibInternals.h +++ b/MdePkg/Library/BaseLib/BaseLibInternals.h @@ -332,6 +332,17 @@ InternalSwitchStack ( IN VA_LIST Marker ); +VOID +EFIAPI +InternalEnterUserImage ( + IN SWITCH_STACK_ENTRY_POINT EntryPoint, + IN VOID *Context1 OPTIONAL, + IN VOID *Context2 OPTIONAL, + IN VOID *NewStack, + IN UINT16 CodeSelector, + IN UINT16 DataSelector + ); + /** Worker function that returns a bit field from Operand. diff --git a/MdePkg/Library/BaseLib/SwitchStack.c b/MdePkg/Library/BaseLib/SwitchStack.c index 954e863fc1..14ad7cd4bf 100644 --- a/MdePkg/Library/BaseLib/SwitchStack.c +++ b/MdePkg/Library/BaseLib/SwitchStack.c @@ -68,3 +68,25 @@ SwitchStack ( // ASSERT (FALSE); } + +VOID +EFIAPI +EnterUserImage ( + IN SWITCH_STACK_ENTRY_POINT EntryPoint, + IN VOID *Context1 OPTIONAL, + IN VOID *Context2 OPTIONAL, + IN VOID *NewStack, + IN UINT16 CodeSelector, + IN UINT16 DataSelector + ) +{ + ASSERT (EntryPoint != NULL); + ASSERT (NewStack != NULL); + + // + // New stack must be aligned with CPU_STACK_ALIGNMENT + // + ASSERT (((UINTN)NewStack & (CPU_STACK_ALIGNMENT - 1)) == 0); + + InternalEnterUserImage (EntryPoint, Context1, Context2, NewStack, CodeSelector, DataSelector); +} diff --git a/MdePkg/Library/BaseLib/X64/SwitchStack.nasm b/MdePkg/Library/BaseLib/X64/SwitchStack.nasm index 7be666dbd3..0f8aa12014 100644 --- a/MdePkg/Library/BaseLib/X64/SwitchStack.nasm +++ b/MdePkg/Library/BaseLib/X64/SwitchStack.nasm @@ -1,4 +1,6 @@ ;------------------------------------------------------------------------------ +; Copyright (c) 2024, Mikhail Krichanov. All rights reserved. +; SPDX-License-Identifier: BSD-3-Clause ; ; Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.
; SPDX-License-Identifier: BSD-2-Clause-Patent @@ -43,3 +45,49 @@ ASM_PFX(InternalSwitchStack): lea rsp, [r9 - 0x20] call rax +;------------------------------------------------------------------------------ +; Routine Description: +; +; Routine for transfering control to user image with 2 parameters +; +; Arguments: +; +; (rcx) EntryPoint - Entry point with new stack. +; (rdx) Context1 - Parameter1 for entry point. +; (r8) Context2 - Parameter2 for entry point. +; (r9) NewStack - The pointer to new stack. +;On stack CodeSelector - Segment selector for code. +;On stack DataSelector - Segment selector for data. +; +; Returns: +; +; None +; +;------------------------------------------------------------------------------ +global ASM_PFX(InternalEnterUserImage) +ASM_PFX(InternalEnterUserImage): + ; Set Data selectors + mov rax, [rsp + 8*6] + or rax, 3H ; RPL = 3 + +o16 mov ds, ax +o16 mov es, ax +o16 mov fs, ax +o16 mov gs, ax + + ; Save Code selector + mov r10, [rsp + 8*5] + or r10, 3H ; RPL = 3 + + ; Prepare stack before swithcing + push rax ; ss + push r9 ; rsp + push r10 ; cs + push rcx ; rip + + ; Save 2 parameters + mov rcx, rdx + mov rdx, r8 + + ; Pass control to user image + retfq diff --git a/UefiCpuPkg/Library/CpuArchLib/CpuGdt.c b/UefiCpuPkg/Library/CpuArchLib/CpuGdt.c index d569d3be53..e8047b1207 100644 --- a/UefiCpuPkg/Library/CpuArchLib/CpuGdt.c +++ b/UefiCpuPkg/Library/CpuArchLib/CpuGdt.c @@ -13,7 +13,7 @@ // // Global descriptor table (GDT) Template // -STATIC GDT_ENTRIES mGdtTemplate = { +STATIC GDT mGdtTemplate = { .Null = { .SegmentLimit_15_0 = 0x0, .BaseAddress_15_0 = 0x0, @@ -249,7 +249,7 @@ InitGlobalDescriptorTable ( ) { EFI_STATUS Status; - GDT_ENTRIES *Gdt; + GDT *Gdt; IA32_DESCRIPTOR Gdtr; EFI_PHYSICAL_ADDRESS Memory; @@ -267,7 +267,7 @@ InitGlobalDescriptorTable ( ); ASSERT_EFI_ERROR (Status); ASSERT ((Memory != 0) && (Memory < SIZE_4GB)); - Gdt = (GDT_ENTRIES *)(UINTN)Memory; + Gdt = (GDT *)(UINTN)Memory; // // Initialize all GDT entries diff --git a/UefiCpuPkg/Library/CpuArchLib/CpuGdt.h b/UefiCpuPkg/Library/CpuArchLib/CpuGdt.h index d1659293cb..21ad6df184 100644 --- a/UefiCpuPkg/Library/CpuArchLib/CpuGdt.h +++ b/UefiCpuPkg/Library/CpuArchLib/CpuGdt.h @@ -14,169 +14,15 @@ // Local structure definitions // -#pragma pack (1) - -// -// Global Descriptor Entry structures -// - -typedef struct { - UINT16 SegmentLimit_15_0; - UINT16 BaseAddress_15_0; - UINT8 BaseAddress_23_16; - UINT8 Type : 4; - UINT8 S : 1; - UINT8 DPL : 2; - UINT8 P : 1; - UINT8 SegmentLimit_19_16 : 4; - UINT8 AVL : 1; - UINT8 L : 1; - UINT8 D_B : 1; - UINT8 G : 1; - UINT8 BaseAddress_31_24; -} SEGMENT_DESCRIPTOR; - -typedef struct { - UINT16 SegmentLimit_15_0; - UINT16 BaseAddress_15_0; - UINT8 BaseAddress_23_16; - // - // Type - // - UINT8 Accessed : 1; - UINT8 Writable : 1; - UINT8 ExpansionDirection : 1; - UINT8 IsCode : 1; - UINT8 IsNotSystemSegment : 1; - UINT8 DescriptorPrivilegeLevel : 2; - UINT8 SegmentPresent : 1; - - UINT8 SegmentLimit_19_16 : 4; - UINT8 Available : 1; - UINT8 Reserved : 1; - UINT8 UpperBound : 1; - UINT8 Granularity : 1; - UINT8 BaseAddress_31_24; -} DATA_SEGMENT_32; - -typedef struct { - UINT16 SegmentLimit_15_0; - UINT16 BaseAddress_15_0; - UINT8 BaseAddress_23_16; - // - // Type - // - UINT8 Accessed : 1; - UINT8 Readable : 1; - UINT8 Conforming : 1; - UINT8 IsCode : 1; - UINT8 IsNotSystemSegment : 1; - UINT8 DescriptorPrivilegeLevel : 2; - UINT8 SegmentPresent : 1; - - UINT8 SegmentLimit_19_16 : 4; - UINT8 Available : 1; - UINT8 Reserved : 1; - UINT8 Is32Bit : 1; - UINT8 Granularity : 1; - UINT8 BaseAddress_31_24; -} CODE_SEGMENT_32; - -typedef struct { - UINT32 Reserved1; - UINT8 Reserved2; - // - // Type - // - UINT8 Accessed : 1; - UINT8 Readable : 1; - UINT8 Conforming : 1; - UINT8 IsCode : 1; - UINT8 IsNotSystemSegment : 1; - UINT8 DescriptorPrivilegeLevel : 2; - UINT8 SegmentPresent : 1; - - UINT8 Reserved3 : 4; - UINT8 Available : 1; - UINT8 LongMode : 1; - UINT8 Is32Bit : 1; - UINT8 Granularity : 1; - UINT8 Reserved4; -} CODE_SEGMENT_64; - -typedef struct { - UINT16 SegmentLimit_15_0; - UINT16 BaseAddress_15_0; - UINT8 BaseAddress_23_16; - - UINT8 Type : 4; - UINT8 IsNotSystemSegment : 1; - UINT8 DescriptorPrivilegeLevel : 2; - UINT8 SegmentPresent : 1; - - UINT8 SegmentLimit_19_16 : 4; - UINT8 Reserved : 3; - UINT8 Granularity : 1; - UINT8 BaseAddress_31_24; -} SYSTEM_SEGMENT; - -typedef struct { - UINT16 OffsetInSegment_15_0; - UINT16 SegmentSelector; - - UINT8 ParameterCount : 5; - UINT8 Reserved : 3; - - UINT8 Type : 4; - UINT8 IsNotSystemSegment : 1; - UINT8 DescriptorPrivilegeLevel : 2; - UINT8 SegmentPresent : 1; - UINT16 OffsetInSegment_31_16; -} CALL_GATE_32; - -typedef struct { - CALL_GATE_32 Common; - UINT32 OffsetInSegment_63_31; - UINT32 Reserved; -} CALL_GATE_64; - -typedef union { - DATA_SEGMENT_32 DataSegment32; - CODE_SEGMENT_32 CodeSegment32; - CODE_SEGMENT_64 CodeSegment64; - CALL_GATE_32 CallGate32; - SYSTEM_SEGMENT SystemSegment; - SEGMENT_DESCRIPTOR SegmentDescriptor; -} GDT_ENTRY; - -typedef struct { - SEGMENT_DESCRIPTOR Null; - DATA_SEGMENT_32 Linear; - CODE_SEGMENT_32 LinearCode; - DATA_SEGMENT_32 SysData; - CODE_SEGMENT_32 SysCode; - CODE_SEGMENT_32 SysCode16; - DATA_SEGMENT_32 LinearData64; - CODE_SEGMENT_64 LinearCode64; - SEGMENT_DESCRIPTOR Spare5; - DATA_SEGMENT_32 Ring3Data64; - CODE_SEGMENT_64 Ring3Code64; - // CALL_GATE_64 FromRing3ToRing0; -} GDT_ENTRIES; - -#pragma pack () - -#define NULL_SEL OFFSET_OF (GDT_ENTRIES, Null) -#define LINEAR_SEL OFFSET_OF (GDT_ENTRIES, Linear) -#define LINEAR_CODE_SEL OFFSET_OF (GDT_ENTRIES, LinearCode) -#define SYS_DATA_SEL OFFSET_OF (GDT_ENTRIES, SysData) -#define SYS_CODE_SEL OFFSET_OF (GDT_ENTRIES, SysCode) -#define SYS_CODE16_SEL OFFSET_OF (GDT_ENTRIES, SysCode16) -#define LINEAR_DATA64_SEL OFFSET_OF (GDT_ENTRIES, LinearData64) -#define LINEAR_CODE64_SEL OFFSET_OF (GDT_ENTRIES, LinearCode64) -#define SPARE5_SEL OFFSET_OF (GDT_ENTRIES, Spare5) -#define RING3_DATA64_SEL OFFSET_OF (GDT_ENTRIES, Ring3Data64) -#define RING3_CODE64_SEL OFFSET_OF (GDT_ENTRIES, Ring3Code64) +#define NULL_SEL OFFSET_OF (GDT, Null) +#define LINEAR_SEL OFFSET_OF (GDT, Linear) +#define LINEAR_CODE_SEL OFFSET_OF (GDT, LinearCode) +#define SYS_DATA_SEL OFFSET_OF (GDT, SysData) +#define SYS_CODE_SEL OFFSET_OF (GDT, SysCode) +#define SYS_CODE16_SEL OFFSET_OF (GDT, SysCode16) +#define LINEAR_DATA64_SEL OFFSET_OF (GDT, LinearData64) +#define LINEAR_CODE64_SEL OFFSET_OF (GDT, LinearCode64) +#define SPARE5_SEL OFFSET_OF (GDT, Spare5) #if defined (MDE_CPU_IA32) #define CPU_CODE_SEL LINEAR_CODE_SEL