exectos/xtldr/i686/memory.c
belliash 0572b208f1
All checks were successful
ci/woodpecker/push/build Pipeline was successful
Add full paging support for AMD64 and i686 architectures, including PAE support
2022-12-11 20:14:04 +01:00

412 lines
14 KiB
C

/**
* PROJECT: ExectOS
* COPYRIGHT: See COPYING.md in the top level directory
* FILE: xtldr/i686/memory.c
* DESCRIPTION: EFI memory management for i686 target
* DEVELOPERS: Rafal Kupiec <belliash@codingworkshop.eu.org>
*/
#include <xtbl.h>
VOID
BlCreateStack(IN PVOID *StackPtr,
IN ULONG StackSize,
IN PVOID Callback)
{
EFI_PHYSICAL_ADDRESS Address;
PVOID StackEnd;
/* Allocate pages for new stack and calculate its end */
BlEfiMemoryAllocatePages(StackSize, &Address);
*StackPtr = (PVOID)(UINT_PTR)Address;
StackEnd = (PUINT8)*StackPtr + (StackSize * EFI_PAGE_SIZE) - EFI_PAGE_SIZE;
/* Create new stack and switch to it immediatelly by calling callback function */
asm volatile("mov %1, %%eax\n"
"mov %%esp, %%ebx\n"
"mov %0, %%esp\n"
"push %%ebp\n"
"mov %%esp, %%ebp\n"
"push %%ebx\n"
"sub $32, %%esp\n"
"call *%%eax\n"
:
: "m" (StackEnd), "m" (Callback)
: "eax", "ebx");
}
/**
* Builds the actual memory mapping page table and enables paging. This routine exits EFI boot services as well.
*
* @param MemoryMappings
* Supplies a pointer to linked list containing all memory mappings.
*
* @param VirtualAddress
* Supplies a pointer to the next valid, free and available virtual address.
*
* @param ImageProtocol
* A pointer to the EFI loaded image protocol with information about where in memory the loader code was placed.
*
* @param PtePointer
* Supplies a pointer to memory area containing a Page Table Entries (PTE).
*
* @return This routine returns a status code.
*
* @since XT 1.0
*/
EFI_STATUS
BlEnablePaging(IN PLIST_ENTRY MemoryMappings,
IN PVOID VirtualAddress,
IN PEFI_LOADED_IMAGE_PROTOCOL ImageProtocol,
IN PVOID *PtePointer)
{
UINT_PTR PhysicalAddress, MapKey, DescriptorSize, DescriptorCount;
EFI_PHYSICAL_ADDRESS Address, PDPTAddress = 0;
PEFI_MEMORY_DESCRIPTOR MemoryMap = NULL;
PLOADER_MEMORY_MAPPING Mapping;
PCPUID_REGISTERS CpuRegisters;
PLIST_ENTRY ListEntry;
BOOLEAN PaeExtension;
EFI_STATUS Status;
PVOID Stack;
UINT Index;
/* Prepare CPUID registers */
CpuRegisters->Leaf = CPUID_GET_CPU_FEATURES;
CpuRegisters->SubLeaf = 0;
CpuRegisters->Eax = 0;
CpuRegisters->Ebx = 0;
CpuRegisters->Ecx = 0;
CpuRegisters->Edx = 0;
/* Get CPUID */
HlCpuId(CpuRegisters);
/* Store PAE status from the CPUID results */
PaeExtension = CpuRegisters->Edx & CPUID_FEATURES_EDX_PAE;
/* Get EFI memory map */
Status = BlGetMemoryMap(&MemoryMap, &MapKey, &DescriptorSize, &DescriptorCount);
if(Status != STATUS_EFI_SUCCESS)
{
/* Unable to get memory map */
return Status;
}
/* Check if PAE supported by the underlying hardware */
if(PaeExtension)
{
/* Print debug message */
BlDbgPrint(L"Physical Address Extension (PAE) available\n");
/* Calculate physical address based on KSEG0 base */
PhysicalAddress = (UINT_PTR)VirtualAddress - XTOS_VIRTUAL_MEMORY_AREA;
/* Iterate over all descriptors from memory map to find satisfying address for PDPT */
for(Index = 0; Index < DescriptorCount; Index++)
{
/* Check descriptor if it can be used to store PDPT */
if((MemoryMap->PhysicalStart + ((MemoryMap->NumberOfPages - 1) * EFI_PAGE_SIZE) >= PhysicalAddress) &&
(MemoryMap->Type == EfiConventionalMemory))
{
/* Use highest address possible */
if(PhysicalAddress >= MemoryMap->PhysicalStart)
{
/* Use physical address */
PDPTAddress = PhysicalAddress;
}
else
{
/* Use descriptor physical start as PDPT address */
PDPTAddress = MemoryMap->PhysicalStart;
}
/* Allocate pages for the PDPT address */
Status = BlEfiMemoryAllocatePages(1, &PDPTAddress);
if(Status != STATUS_EFI_SUCCESS) {
return Status;
}
break;
}
/* Get next descriptor */
MemoryMap = (EFI_MEMORY_DESCRIPTOR*)((UINT8*)MemoryMap + DescriptorSize);
}
/* Make sure PDPT address found */
if(PDPTAddress == 0)
{
/* No suitable area for PDPT found in EFI memory map */
return STATUS_EFI_NOT_FOUND;
}
/* Set virtual address based on new PDPT address mapped to KSEG0 base */
VirtualAddress = (void*)(UINT_PTR)(PDPTAddress + EFI_PAGE_SIZE + XTOS_VIRTUAL_MEMORY_AREA);
/* Set base page frame number */
Address = 0x100000; // MEM_TOP_DOWN ?
/* Allocate pages for the PFN */
Status = BlEfiMemoryAllocatePages(4, &Address);
if(Status != STATUS_EFI_SUCCESS)
{
/* Memory allocation failure */
return Status;
}
/* Set and zero memory used by page mappings and CR3 */
*PtePointer = (PVOID)(UINT_PTR)PDPTAddress;
RtlZeroMemory(*PtePointer, EFI_PAGE_SIZE);
RtlZeroMemory((PVOID)Address, EFI_PAGE_SIZE * 4);
/* Set the page directory into the PDPT and mark it present */
for(Index = 0; Index < 4; Index++)
{
/* Set paging entry settings */
((PHARDWARE_PTE_PAE)*PtePointer)[Index].PageFrameNumber = Address / EFI_PAGE_SIZE;
((PHARDWARE_PTE_PAE)*PtePointer)[Index].Valid = 1;
/* Next valid PFN address */
Address += EFI_PAGE_SIZE;
}
}
else
{
/* Print debug message */
BlDbgPrint(L"Physical Address Extension (PAE) NOT available\n");
/* Allocate pages for Page Directory */
Status = BlEfiMemoryAllocatePages(1, &Address);
if(Status != STATUS_EFI_SUCCESS)
{
/* Memory allocation failure */
return Status;
}
/* Set and zero memory used by Page Directory */
*PtePointer = (PVOID)(UINT_PTR)Address;
RtlZeroMemory(*PtePointer, EFI_PAGE_SIZE);
}
/* Map the stack */
BlGetStackPointer(&Stack);
Status = BlAddVirtualMemoryMapping(MemoryMappings, Stack, Stack, XTOS_KERNEL_STACK_SIZE,
LoaderOsloaderStack);
if(Status != STATUS_EFI_SUCCESS)
{
/* Mapping the stack failed */
return Status;
}
/* Map XTLDR code */
Status = BlAddVirtualMemoryMapping(MemoryMappings, ImageProtocol->ImageBase, ImageProtocol->ImageBase,
EFI_SIZE_TO_PAGES(ImageProtocol->ImageSize),
LoaderFirmwareTemporary);
if(Status != STATUS_EFI_SUCCESS)
{
/* Mapping the boot loader code failed */
return Status;
}
/* Add page mapping itself to memory mapping */
Status = BlAddVirtualMemoryMapping(MemoryMappings, NULL, *PtePointer, 1, LoaderMemoryData);
if(Status != STATUS_EFI_SUCCESS)
{
/* Mapping PD failed */
return Status;
}
/* Iterate through all mappings */
ListEntry = MemoryMappings->Flink;
while(ListEntry != MemoryMappings)
{
/* Take mapping from the list */
Mapping = CONTAIN_RECORD(ListEntry, LOADER_MEMORY_MAPPING, ListEntry);
/* Check if virtual address is set */
if(Mapping->VirtualAddress)
{
/* Map memory */
Status = BlMapVirtualMemory(MemoryMappings, (UINT_PTR)Mapping->VirtualAddress,
(UINT_PTR)Mapping->PhysicalAddress, Mapping->NumberOfPages,
PaeExtension, PtePointer);
if(Status != STATUS_EFI_SUCCESS)
{
/* Memory mapping failed */
return Status;
}
}
/* Take next element */
ListEntry = ListEntry->Flink;
}
/* Exit EFI Boot Services */
BlDbgPrint(L"Exiting EFI boot services\n");
EfiSystemTable->BootServices->ExitBootServices(EfiImageHandle, MapKey);
/* Enable PAE if supported by CPU */
if(PaeExtension)
{
/* Enable Physical Address Extension (PAE) */
HlWriteCR4(HlReadCR4() | 0x00000020);
}
/* Write page mappings to CR3 */
HlWriteCR3((UINT_PTR)*PtePointer);
/* Enable paging */
HlWriteCR0(HlReadCR0() | 0x80000000);
/* Return success */
return STATUS_EFI_SUCCESS;
}
/**
* This routine does the actual virtual memory mapping.
*
* @param MemoryMappings
* Supplies a pointer to linked list containing all memory mappings.
*
* @param VirtualAddress
* Supplies a virtual address of the mapping.
*
* @param PhysicalAddress
* Supplies a physical address of the mapping.
*
* @param NumberOfPages
* Supplies a number of the pages of the mapping.
*
* @param PaeExtension
* Specifies whether Physical Address Extension (PAE) is supported by the hardware.
*
* @param PtePointer
* Supplies a pointer to an array of pointers to page table entries.
*
* @return This routine returns a status code.
*
* @since XT 1.0
*/
EFI_STATUS
BlMapVirtualMemory(IN PLIST_ENTRY MemoryMappings,
IN UINT_PTR VirtualAddress,
IN UINT_PTR PhysicalAddress,
IN UINT NumberOfPages,
IN BOOLEAN PaeExtension,
IN OUT PVOID *PtePointer)
{
EFI_PHYSICAL_ADDRESS Address;
UINT_PTR PageFrameNumber;
PHARDWARE_PTE_PAE PaePageTable, PageDirectory;
PHARDWARE_PTE PageTable;
EFI_STATUS Status;
unsigned int PdIndex, PtIndex;
/* Set the PFN */
PageFrameNumber = PhysicalAddress >> EFI_PAGE_SHIFT;
/* Check if PAE supported by the hardware */
if(PaeExtension)
{
/* PAE supported, do the recursive mapping */
while(NumberOfPages > 0)
{
/* Find Page Directory and calculate indices from a virtual address */
PageDirectory = (HARDWARE_PTE_PAE*)(((PHARDWARE_PTE_PAE)(*PtePointer))[VirtualAddress >> 30].PageFrameNumber * EFI_PAGE_SIZE);
PdIndex = (VirtualAddress >> 21) & 0x1ff;
PtIndex = (VirtualAddress & 0x1ff000) >> 12;
/* Validate Page Directory */
if(!PageDirectory[PdIndex].Valid) {
/* Allocate pages for new page table */
Status = BlEfiMemoryAllocatePages(1, &Address);
if(Status != STATUS_EFI_SUCCESS) {
/* Memory allocation failure */
return Status;
}
/* Fill allocated memory with zeros */
RtlZeroMemory((PVOID)(UINT_PTR)Address, EFI_PAGE_SIZE);
/* Set paging entry settings */
PageDirectory[PdIndex].PageFrameNumber = Address / EFI_PAGE_SIZE;
PageDirectory[PdIndex].Valid = 1;
PageDirectory[PdIndex].Write = 1;
/* Set page table */
PaePageTable = (HARDWARE_PTE_PAE*)(UINT_PTR)Address;
}
else
{
/* Set page table */
PaePageTable = (HARDWARE_PTE_PAE*)(PageDirectory[PdIndex].PageFrameNumber * EFI_PAGE_SIZE);
}
/* Set page table settings */
PaePageTable[PtIndex].PageFrameNumber = PageFrameNumber;
PaePageTable[PtIndex].Valid = 1;
PaePageTable[PtIndex].Write = 1;
/* Take next virtual address and PFN */
VirtualAddress += EFI_PAGE_SIZE;
PageFrameNumber++;
/* Decrease number of pages left */
NumberOfPages--;
}
}
else
{
/* PAE not supported, do the recursive mapping */
while (NumberOfPages > 0)
{
/* Calculate indices from a virtual address */
PdIndex = VirtualAddress >> 22;
PtIndex = (VirtualAddress & 0x3ff000) >> 12;
/* Validate Page Table */
if(!((PHARDWARE_PTE)(*PtePointer))[PdIndex].Valid)
{
/* Allocate pages for new page table */
Status = BlEfiMemoryAllocatePages(1, &Address);
if (Status != STATUS_EFI_SUCCESS) {
/* Memory allocation failure */
return Status;
}
/* Fill allocated memory with zeros */
RtlZeroMemory((void*)(UINT_PTR)Address, EFI_PAGE_SIZE);
/* Set paging entry settings */
((PHARDWARE_PTE)(*PtePointer))[PdIndex].PageFrameNumber = Address / EFI_PAGE_SIZE;
((PHARDWARE_PTE)(*PtePointer))[PdIndex].Valid = 1;
((PHARDWARE_PTE)(*PtePointer))[PdIndex].Write = 1;
/* Set page table */
PageTable = (HARDWARE_PTE*)(UINT_PTR)Address;
}
else
{
/* Set page table */
PageTable = (HARDWARE_PTE*)(((PHARDWARE_PTE)(*PtePointer))[PdIndex].PageFrameNumber * EFI_PAGE_SIZE);
}
/* Set page table settings */
PageTable[PtIndex].PageFrameNumber = PageFrameNumber;
PageTable[PtIndex].Valid = 1;
PageTable[PtIndex].Write = 1;
/* Take next virtual address and PFN */
VirtualAddress += EFI_PAGE_SIZE;
PageFrameNumber++;
/* Decrease number of pages left */
NumberOfPages--;
}
}
/* Return success */
return STATUS_EFI_SUCCESS;
}