Ring3: Added support for separate User address space.

This commit is contained in:
Mikhail Krichanov 2024-11-26 19:12:15 +03:00
parent 983649444e
commit 3b8cd407d3
18 changed files with 346 additions and 2 deletions

View File

@ -229,6 +229,7 @@ typedef struct {
VOID *HiiData;
BOOLEAN IsUserImage;
UINTN UserPageTable;
} LOADED_IMAGE_PRIVATE_DATA;
typedef struct {
@ -288,6 +289,10 @@ extern VOID *gCoreSysCallStackTop;
extern VOID *gRing3CallStackBase;
extern VOID *gRing3CallStackTop;
extern VOID *gRing3EntryPoint;
extern VOID *gUserPageTableTemplate;
extern UINTN gUserPageTableTemplateSize;
extern UINTN gUserPageTable;
extern UINTN gCorePageTable;
//
// Service Initialization Functions
@ -2777,4 +2782,10 @@ FreeProtocolsList (
VOID
);
VOID
EFIAPI
MakeUserPageTableTemplate (
VOID
);
#endif

View File

@ -150,6 +150,7 @@
gUefiImageLoaderImageContextGuid ## CONSUMES ## HOB
gEfiGlobalVariableGuid ## SOMETIMES_CONSUMES ## SysCall
gEarlyPL011BaseAddressGuid ## CONSUMES
gEfiHobPageTableInfoGuid ## CONSUMES ## HOB
[Ppis]
gEfiVectorHandoffInfoPpiGuid ## UNDEFINED # HOB

View File

@ -328,6 +328,8 @@ DxeMain (
ProtectUefiImage (&mCurrentImage->Info, UefiImageOriginFv, &ImageContext, mCurrentImage->IsUserImage);
MakeUserPageTableTemplate ();
//
// Call constructor for all libraries
//

View File

@ -1108,6 +1108,7 @@ CoreLoadImageCommon (
UEFI_IMAGE_LOADER_IMAGE_CONTEXT ImageContext;
UINT8 ImageOrigin;
EFI_FV_FILE_ATTRIBUTES FileAttributes;
VOID *UserPageTable;
SecurityStatus = EFI_SUCCESS;
@ -1348,6 +1349,14 @@ CoreLoadImageCommon (
Image->Info.ParentHandle = ParentImageHandle;
Image->IsUserImage = (FileAttributes & EFI_FV_FILE_ATTRIB_USER) != 0;
if ((gRing3Data != NULL) && Image->IsUserImage) {
UserPageTable = AllocatePages (EFI_SIZE_TO_PAGES (gUserPageTableTemplateSize));
CopyMem (UserPageTable, gUserPageTableTemplate, gUserPageTableTemplateSize);
Image->UserPageTable = (UINTN)UserPageTable;
gUserPageTable = Image->UserPageTable;
}
if (NumberOfPages != NULL) {
Image->NumberOfPages = *NumberOfPages;
} else {

View File

@ -82,6 +82,10 @@ SetUefiImageMemoryAttributes (
ASSERT (gCpu != NULL);
gCpu->SetMemoryAttributes (gCpu, BaseAddress, Length, FinalAttributes);
if ((Attributes & EFI_MEMORY_USER) != 0) {
gCpu->SetUserMemoryAttributes (gCpu, gUserPageTable, BaseAddress, Length, FinalAttributes);
}
}
/**

View File

@ -59,6 +59,12 @@ InitializeRing3 (
CopyMem ((VOID *)gRing3Data, (VOID *)Image->Info.SystemTable, sizeof (EFI_SYSTEM_TABLE));
SetUefiImageMemoryAttributes (
(UINTN)gRing3Data,
ALIGN_VALUE (sizeof (RING3_DATA), EFI_PAGE_SIZE),
EFI_MEMORY_XP | EFI_MEMORY_USER
);
if (PcdGetBool (PcdSerialUseMmio)) {
Status = CoreAllocatePages (
AllocateAnyPages,
@ -124,6 +130,12 @@ InitializeRing3 (
gRing3Interfaces = (VOID *)(UINTN)Physical;
SetUefiImageMemoryAttributes (
(UINTN)gRing3Interfaces,
EFI_PAGES_TO_SIZE (RING3_INTERFACES_PAGES),
EFI_MEMORY_XP | EFI_MEMORY_USER
);
SizeOfStack = EFI_SIZE_TO_PAGES (USER_STACK_SIZE) * EFI_PAGE_SIZE;
//
@ -141,6 +153,9 @@ InitializeRing3 (
gCoreSysCallStackTop = TopOfStack;
SetUefiImageMemoryAttributes ((UINTN)gCoreSysCallStackBase, SizeOfStack, EFI_MEMORY_XP);
//
// gCpu->SetUserMemoryAttributes (gCpu, gUserPageTable, (UINTN)gCoreSysCallStackBase, SizeOfStack, EFI_MEMORY_XP);
//
DEBUG ((DEBUG_ERROR, "Core: gCoreSysCallStackTop = %p\n", gCoreSysCallStackTop));
//

View File

@ -123,8 +123,13 @@ copy:
;
; (On User Stack) Argument 4, 5, ...
;------------------------------------------------------------------------------
ALIGN 4096
global ASM_PFX(CoreBootServices)
ASM_PFX(CoreBootServices):
mov rax, [ASM_PFX(gCorePageTable)]
mov cr3, rax
; Switch from User to Core data segment selectors.
mov ax, ss
mov ds, ax
@ -175,6 +180,8 @@ ASM_PFX(CoreBootServices):
pop rbp
pop rsp
mov rdx, [ASM_PFX(gUserPageTable)]
mov cr3, rdx
; SYSCALL saves RFLAGS into R11 and the RIP of the next instruction into RCX.
o64 sysret
; SYSRET copies the value in RCX into RIP and loads RFLAGS from R11.
@ -190,9 +197,9 @@ o64 sysret
;------------------------------------------------------------------------------
global ASM_PFX(CallRing3)
ASM_PFX(CallRing3):
cli
pushfq
pop r11
cli
; Save nonvolatile registers RBX, RBP, RDI, RSI, RSP, R12, R13, R14, and R15.
push rbx
push rbp
@ -221,6 +228,8 @@ ASM_PFX(CallRing3):
mov rsp, r8
mov rbp, rsp
mov r8, [ASM_PFX(gUserPageTable)]
mov cr3, r8
; Pass control to user image
o64 sysret
@ -248,5 +257,15 @@ ASM_PFX(ReturnToCore):
ret
SECTION .data
ALIGN 4096
global ASM_PFX(gCorePageTable)
ASM_PFX(gCorePageTable):
resq 1
global ASM_PFX(gUserPageTable)
ASM_PFX(gUserPageTable):
resq 1
ASM_PFX(CoreRsp):
resq 1

View File

@ -8,6 +8,202 @@
#include "DxeMain.h"
#include <Register/Intel/ArchitecturalMsr.h>
#include <IndustryStandard/PageTable.h>
VOID *gUserPageTableTemplate;
UINTN gUserPageTableTemplateSize;
VOID
EFIAPI
MakeUserPageTableTemplate (
VOID
)
{
EFI_HOB_GUID_TYPE *GuidHob;
EFI_PAGE_TABLE_INFO *PageTableInfo;
UINTN BigPageAddress;
EFI_PHYSICAL_ADDRESS PageAddress;
UINTN IndexOfPml5Entries;
UINTN IndexOfPml4Entries;
UINTN IndexOfPdpEntries;
UINTN IndexOfPageDirectoryEntries;
PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel5Entry;
PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry;
PAGE_MAP_AND_DIRECTORY_POINTER *PageMap;
PAGE_MAP_AND_DIRECTORY_POINTER *PageDirectoryPointerEntry;
PAGE_TABLE_ENTRY *PageDirectoryEntry;
PAGE_TABLE_1G_ENTRY *PageDirectory1GEntry;
GuidHob = GetFirstGuidHob (&gEfiHobPageTableInfoGuid);
if (GuidHob == NULL) {
DEBUG ((DEBUG_ERROR, "Core: Could not retrieve PageTableInfo HOB.\n"));
CpuDeadLoop ();
}
PageTableInfo = (EFI_PAGE_TABLE_INFO *)(GET_GUID_HOB_DATA (GuidHob));
BigPageAddress = (UINTN)AllocateAlignedPages (PageTableInfo->TotalPagesNum, PAGE_TABLE_POOL_ALIGNMENT);
if (BigPageAddress == 0) {
DEBUG ((DEBUG_ERROR, "Core: Could not allocate buffer for User page table.\n"));
CpuDeadLoop ();
}
//
// By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
//
PageMap = (VOID *)BigPageAddress;
if (PageTableInfo->Page5LevelEnabled) {
//
// By architecture only one PageMapLevel5 exists - so lets allocate storage for it.
//
PageMapLevel5Entry = PageMap;
BigPageAddress += SIZE_4KB;
}
PageAddress = 0;
for ( IndexOfPml5Entries = 0
; IndexOfPml5Entries < PageTableInfo->NumberOfPml5EntriesNeeded
; IndexOfPml5Entries++)
{
//
// Each PML5 entry points to a page of PML4 entires.
// So lets allocate space for them and fill them in in the IndexOfPml4Entries loop.
// When 5-Level Paging is disabled, below allocation happens only once.
//
PageMapLevel4Entry = (VOID *)BigPageAddress;
BigPageAddress += SIZE_4KB;
if (PageTableInfo->Page5LevelEnabled) {
//
// Make a PML5 Entry
//
PageMapLevel5Entry->Uint64 = (UINT64)(UINTN)PageMapLevel4Entry | PageTableInfo->AddressEncMask;
PageMapLevel5Entry->Bits.ReadWrite = 1;
PageMapLevel5Entry->Bits.UserSupervisor = 1;
PageMapLevel5Entry->Bits.Present = 1;
PageMapLevel5Entry++;
}
for ( IndexOfPml4Entries = 0
; IndexOfPml4Entries < (PageTableInfo->NumberOfPml5EntriesNeeded == 1 ? PageTableInfo->NumberOfPml4EntriesNeeded : 512)
; IndexOfPml4Entries++, PageMapLevel4Entry++)
{
//
// Each PML4 entry points to a page of Page Directory Pointer entires.
// So lets allocate space for them and fill them in in the IndexOfPdpEntries loop.
//
PageDirectoryPointerEntry = (VOID *)BigPageAddress;
BigPageAddress += SIZE_4KB;
//
// Make a PML4 Entry
//
PageMapLevel4Entry->Uint64 = (UINT64)(UINTN)PageDirectoryPointerEntry | PageTableInfo->AddressEncMask;
PageMapLevel4Entry->Bits.ReadWrite = 1;
PageMapLevel4Entry->Bits.UserSupervisor = 1;
PageMapLevel4Entry->Bits.Present = 1;
if (PageTableInfo->Page1GSupport) {
PageDirectory1GEntry = (VOID *)PageDirectoryPointerEntry;
for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += SIZE_1GB) {
//
// Fill in the Page Directory entries
//
PageDirectory1GEntry->Uint64 = (UINT64)PageAddress | PageTableInfo->AddressEncMask;
PageDirectory1GEntry->Bits.ReadWrite = 1;
PageDirectory1GEntry->Bits.Present = 1;
PageDirectory1GEntry->Bits.MustBe1 = 1;
}
} else {
for ( IndexOfPdpEntries = 0
; IndexOfPdpEntries < (PageTableInfo->NumberOfPml4EntriesNeeded == 1 ? PageTableInfo->NumberOfPdpEntriesNeeded : 512)
; IndexOfPdpEntries++, PageDirectoryPointerEntry++)
{
//
// Each Directory Pointer entries points to a page of Page Directory entires.
// So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
//
PageDirectoryEntry = (VOID *)BigPageAddress;
BigPageAddress += SIZE_4KB;
//
// Fill in a Page Directory Pointer Entries
//
PageDirectoryPointerEntry->Uint64 = (UINT64)(UINTN)PageDirectoryEntry | PageTableInfo->AddressEncMask;
PageDirectoryPointerEntry->Bits.ReadWrite = 1;
PageDirectoryPointerEntry->Bits.UserSupervisor = 1;
PageDirectoryPointerEntry->Bits.Present = 1;
for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += SIZE_2MB) {
//
// Fill in the Page Directory entries
//
PageDirectoryEntry->Uint64 = (UINT64)PageAddress | PageTableInfo->AddressEncMask;
PageDirectoryEntry->Bits.ReadWrite = 1;
PageDirectoryEntry->Bits.Present = 0;
PageDirectoryEntry->Bits.MustBe1 = 1;
}
}
//
// Fill with null entry for unused PDPTE
//
ZeroMem (PageDirectoryPointerEntry, (512 - IndexOfPdpEntries) * sizeof (PAGE_MAP_AND_DIRECTORY_POINTER));
}
}
//
// For the PML4 entries we are not using fill in a null entry.
//
ZeroMem (PageMapLevel4Entry, (512 - IndexOfPml4Entries) * sizeof (PAGE_MAP_AND_DIRECTORY_POINTER));
}
if (PageTableInfo->Page5LevelEnabled) {
//
// For the PML5 entries we are not using fill in a null entry.
//
ZeroMem (PageMapLevel5Entry, (512 - IndexOfPml5Entries) * sizeof (PAGE_MAP_AND_DIRECTORY_POINTER));
}
gUserPageTableTemplate = (VOID *)PageMap;
gUserPageTableTemplateSize = ALIGN_VALUE (EFI_PAGES_TO_SIZE (PageTableInfo->TotalPagesNum), PAGE_TABLE_POOL_ALIGNMENT);
gUserPageTable = (UINTN)gUserPageTableTemplate;
SetUefiImageMemoryAttributes ((UINT64)PageMap, gUserPageTableTemplateSize, EFI_MEMORY_XP);
//
// Map CoreBootServices
//
gCpu->SetUserMemoryAttributes (
gCpu,
(UINTN)PageMap,
(EFI_PHYSICAL_ADDRESS)(UINTN)CoreBootServices,
SIZE_4KB,
EFI_MEMORY_RO
);
gCpu->SetUserMemoryAttributes (
gCpu,
(UINTN)PageMap,
(EFI_PHYSICAL_ADDRESS)(UINTN)&gCorePageTable,
SIZE_4KB,
EFI_MEMORY_RO | EFI_MEMORY_XP
);
//
// Map ExceptionHandlerAsm: AsmIdtVectorBegin - AsmGetTemplateAddressMap
// mCorePageTable, gCoreSysCallStackTop
//
// gCpu->SetUserMemoryAttributes (gCpu, (UINTN)PageMap, BaseAddress, SIZE_4KB, EFI_MEMORY_RO);
gCpu->SetUserMemoryAttributes (
gCpu,
(UINTN)PageMap,
FixedPcdGet32 (PcdOvmfWorkAreaBase),
FixedPcdGet32 (PcdOvmfWorkAreaSize),
EFI_MEMORY_XP | EFI_MEMORY_USER
);
}
VOID
EFIAPI
@ -26,6 +222,9 @@ InitializeMsr (
Ebx = 0;
Edx = 0;
// The Intel-64 and IA-32 architectures also allow for global pages when the PGE flag (bit 7) is 1 in CR4.
// PGE must be zero.
//
// Forbid supervisor-mode accesses to any user-mode pages.
//
@ -76,4 +275,6 @@ InitializeMsr (
//
Msr = (UINT64)BIT9;
AsmWriteMsr64 (MSR_IA32_FMASK, Msr);
gCorePageTable = AsmReadCr3 ();
}

View File

@ -90,6 +90,7 @@
## SOMETIMES_PRODUCES ## HOB
gEfiMemoryTypeInformationGuid
gUefiImageLoaderImageContextGuid
gEfiHobPageTableInfoGuid
[FeaturePcd.IA32]
gEfiMdeModulePkgTokenSpaceGuid.PcdDxeIplSwitchToLongMode ## CONSUMES

View File

@ -705,6 +705,7 @@ CreateIdentityMappingPageTables (
PAGE_TABLE_1G_ENTRY *PageDirectory1GEntry;
UINT64 AddressEncMask;
IA32_CR4 Cr4;
EFI_PAGE_TABLE_INFO PageTableInfo;
//
// Set PageMapLevel5Entry to suppress incorrect compiler/analyzer warnings
@ -828,6 +829,16 @@ CreateIdentityMappingPageTables (
(UINT64)TotalPagesNum
));
PageTableInfo.NumberOfPml5EntriesNeeded = NumberOfPml5EntriesNeeded;
PageTableInfo.NumberOfPml4EntriesNeeded = NumberOfPml4EntriesNeeded;
PageTableInfo.NumberOfPdpEntriesNeeded = NumberOfPdpEntriesNeeded;
PageTableInfo.TotalPagesNum = TotalPagesNum;
PageTableInfo.Page5LevelEnabled = Page5LevelEnabled;
PageTableInfo.Page1GSupport = Page1GSupport;
PageTableInfo.AddressEncMask = AddressEncMask;
BuildGuidDataHob (&gEfiHobPageTableInfoGuid, &PageTableInfo, sizeof (EFI_PAGE_TABLE_INFO));
BigPageAddress = (UINTN)AllocatePageTableMemory (TotalPagesNum);
ASSERT (BigPageAddress != 0);

View File

@ -21,8 +21,12 @@
#define EFI_HOB_MEMORY_ALLOC_MODULE_GUID \
{0xf8e21975, 0x899, 0x4f58, {0xa4, 0xbe, 0x55, 0x25, 0xa9, 0xc6, 0xd7, 0x7a} }
#define EFI_HOB_PAGE_TABLE_INFO_GUID \
{0xac992fe9, 0xb0cc, 0x45df, {0xae, 0xcf, 0x51, 0x5a, 0xd2, 0xc4, 0xe8, 0xb3} }
extern EFI_GUID gEfiHobMemoryAllocBspStoreGuid;
extern EFI_GUID gEfiHobMemoryAllocStackGuid;
extern EFI_GUID gEfiHobMemoryAllocModuleGuid;
extern EFI_GUID gEfiHobPageTableInfoGuid;
#endif

View File

@ -14,6 +14,16 @@ SPDX-License-Identifier: BSD-2-Clause-Patent
#ifndef __MEMORY_ALLOCATION_LIB_H__
#define __MEMORY_ALLOCATION_LIB_H__
typedef struct {
UINT32 NumberOfPml5EntriesNeeded;
UINT32 NumberOfPml4EntriesNeeded;
UINT32 NumberOfPdpEntriesNeeded;
UINTN TotalPagesNum;
BOOLEAN Page5LevelEnabled;
BOOLEAN Page1GSupport;
UINT64 AddressEncMask;
} EFI_PAGE_TABLE_INFO;
/**
Allocates one or more 4KB pages of type EfiBootServicesData.

View File

@ -258,6 +258,16 @@ EFI_STATUS
OUT UINT64 *Attributes
);
typedef
EFI_STATUS
(EFIAPI *EFI_CPU_SET_USER_MEMORY_ATTRIBUTES)(
IN EFI_CPU_ARCH_PROTOCOL *This,
IN UINTN UserPageTable,
IN EFI_PHYSICAL_ADDRESS BaseAddress,
IN UINT64 Length,
IN UINT64 Attributes
);
///
/// The EFI_CPU_ARCH_PROTOCOL is used to abstract processor-specific functions from the DXE
/// Foundation. This includes flushing caches, enabling and disabling interrupts, hooking interrupt
@ -288,6 +298,7 @@ struct _EFI_CPU_ARCH_PROTOCOL {
///
UINT32 DmaBufferAlignment;
EFI_CPU_GET_MEMORY_ATTRIBUTES GetMemoryAttributes;
EFI_CPU_SET_USER_MEMORY_ATTRIBUTES SetUserMemoryAttributes;
};
extern EFI_GUID gEfiCpuArchProtocolGuid;

View File

@ -781,6 +781,9 @@
## Include/Guid/MemoryAllocationHob.h
gEfiHobMemoryAllocBspStoreGuid = { 0x564B33CD, 0xC92A, 0x4593, { 0x90, 0xBF, 0x24, 0x73, 0xE4, 0x3C, 0x63, 0x22 }}
## Include/Guid/MemoryAllocationHob.h
gEfiHobPageTableInfoGuid = { 0xAC992FE9, 0xB0CC, 0x45DF, { 0xAE, 0xCF, 0x51, 0x5A, 0xD2, 0xC4, 0xE8, 0xB3 }}
## Include/Guid/EventLegacyBios.h
gEfiEventLegacyBootGuid = { 0x2A571201, 0x4966, 0x47F6, { 0x8B, 0x86, 0xF3, 0x1E, 0x41, 0xF3, 0x2F, 0x10 }}

View File

@ -158,4 +158,6 @@ typedef union {
#define PTE_OFFSET(x) ( (x >> 12) & PAGETABLE_ENTRY_MASK)
#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull
#define PAGE_TABLE_POOL_ALIGNMENT BASE_2MB
#endif

View File

@ -32,7 +32,8 @@ EFI_CPU_ARCH_PROTOCOL gCpuImpl = {
CpuSetMemoryAttributes,
1, // NumberOfTimers
4, // DmaBufferAlignment
CpuGetMemoryAttributes
CpuGetMemoryAttributes,
CpuSetUserMemoryAttributes
};
EFI_HOB_PLATFORM_INFO *mPlatformInfoHob2 = NULL;

View File

@ -229,6 +229,16 @@ CpuGetMemoryAttributes (
OUT UINT64 *Attributes
);
EFI_STATUS
EFIAPI
CpuSetUserMemoryAttributes (
IN EFI_CPU_ARCH_PROTOCOL *This,
IN UINTN UserPageTable,
IN EFI_PHYSICAL_ADDRESS BaseAddress,
IN UINT64 Length,
IN UINT64 Attributes
);
/**
Initialize Global Descriptor Table.

View File

@ -429,6 +429,35 @@ CpuGetMemoryAttributes (
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
CpuSetUserMemoryAttributes (
IN EFI_CPU_ARCH_PROTOCOL *This,
IN UINTN UserPageTable,
IN EFI_PHYSICAL_ADDRESS BaseAddress,
IN UINT64 Length,
IN UINT64 Attributes
)
{
UINT64 MemoryAttributes;
PAGE_TABLE_LIB_PAGING_CONTEXT PagingContext;
MemoryAttributes = Attributes & EFI_MEMORY_ATTRIBUTE_MASK;
GetCurrentPagingContext (&PagingContext);
if (PagingContext.MachineType == IMAGE_FILE_MACHINE_I386) {
PagingContext.ContextData.Ia32.PageTableBase = (UINT32)UserPageTable;
} else {
PagingContext.ContextData.X64.PageTableBase = (UINT64)UserPageTable;
}
//
// Set memory attribute by page table
//
return AssignMemoryPageAttributes (&PagingContext, BaseAddress, Length, MemoryAttributes, AllocatePages);
}
/**
Modify memory attributes of page entry.