From c702152cca4456b597374585271aceb0eaf27c08 Mon Sep 17 00:00:00 2001 From: Aiken Harris Date: Fri, 12 Jun 2026 13:16:48 +0200 Subject: [PATCH] Refactor real-mode memory allocation and identity mapping for AP trampoline --- xtoskrnl/hl/x86/cpu.cc | 41 ++++--- xtoskrnl/includes/mm/hlpool.hh | 15 ++- xtoskrnl/mm/data.cc | 6 + xtoskrnl/mm/hlpool.cc | 210 ++++++++++++++++++++++++++++++--- 4 files changed, 236 insertions(+), 36 deletions(-) diff --git a/xtoskrnl/hl/x86/cpu.cc b/xtoskrnl/hl/x86/cpu.cc index d6014d3f0..f833a051d 100644 --- a/xtoskrnl/hl/x86/cpu.cc +++ b/xtoskrnl/hl/x86/cpu.cc @@ -85,9 +85,9 @@ XTAPI XTSTATUS HL::Cpu::StartAllProcessors(VOID) { - ULONG CpuNumber, Index, MaxCpus, SipiVector, Timeout, TrampolineCodeSize, TrampolinePages; - PVOID CpuStructures, TrampolineAddress, TrampolineCode; - ULONG_PTR AllocationSize; + ULONG ApPages, CpuNumber, Index, MaxCpus, SipiVector, Timeout, TrampolineCodeSize; + PVOID ApVirtualAddress, CpuStructures, TrampolineCode; + PHYSICAL_ADDRESS ApPhysicalAddress; PPROCESSOR_START_BLOCK StartBlock; PKPROCESSOR_BLOCK ProcessorBlock; PACPI_SYSTEM_INFO SysInfo; @@ -136,27 +136,34 @@ HL::Cpu::StartAllProcessors(VOID) return STATUS_UNSUCCESSFUL; } - /* Compute trampoline memory allocation size (trampoline + processor start block + temporary stack) */ - AllocationSize = TrampolineCodeSize + sizeof(PROCESSOR_START_BLOCK) + 512; - TrampolinePages = (ULONG)(ROUND_UP(AllocationSize, MM_PAGE_SIZE) / MM_PAGE_SIZE); - - /* Allocate real mode memory for AP trampoline */ - Status = MM::HardwarePool::AllocateRealModeMemory(TrampolinePages, &TrampolineAddress); + /* Allocate low memory for AP trampoline code */ + Status = MM::HardwarePool::AllocateLowMemory(&ApPhysicalAddress, &ApVirtualAddress); if(Status != STATUS_SUCCESS) { /* Failed to allocate memory, print error message and return error */ - DebugPrint(L"Failed to allocate %lu pages for AP Trampoline!\n", TrampolinePages); + DebugPrint(L"Failed to allocate low memory for AP Trampoline!\n"); return Status; } /* Copy trampoline code to low memory */ - RTL::Memory::CopyMemory(TrampolineAddress, TrampolineCode, TrampolineCodeSize); + RTL::Memory::CopyMemory(ApVirtualAddress, TrampolineCode, TrampolineCodeSize); + + /* Compute number of pages for trampoline */ + ApPages = MM::HardwarePool::CalculateRealModeAllocationPages(TrampolineCodeSize); + + /* Temporarily identity map trampoline address */ + Status = MM::HardwarePool::MapRealModeMemory(ApPhysicalAddress, ApPages); + if(Status != STATUS_SUCCESS) + { + /* Failed to map memory, return error */ + return Status; + } /* Get start block address relative to trampoline address */ - StartBlock = (PPROCESSOR_START_BLOCK)((PUCHAR)TrampolineAddress + TrampolineCodeSize); + StartBlock = (PPROCESSOR_START_BLOCK)((PUCHAR)ApVirtualAddress + TrampolineCodeSize); /* Get SIPI vector */ - SipiVector = (ULONG)((ULONG_PTR)TrampolineAddress >> 12); + SipiVector = (ULONG)(ApPhysicalAddress.QuadPart >> 12); /* Loop over all CPUs */ CpuNumber = 0; @@ -183,8 +190,8 @@ HL::Cpu::StartAllProcessors(VOID) Status = MM::KernelPool::AllocateProcessorStructures(&CpuStructures); if(Status != STATUS_SUCCESS) { - /* Failed to allocate memory, unmap memory and return error */ - MM::HardwarePool::UnmapHardwareMemory(TrampolineAddress, TrampolinePages, TRUE); + /* Failed to allocate memory, unmap temporary identity mapping and return error */ + MM::HardwarePool::UnmapRealModeMemory(ApPhysicalAddress, ApPages); return Status; } @@ -242,7 +249,7 @@ HL::Cpu::StartAllProcessors(VOID) } } - /* Unmap trampoline memory and return success */ - MM::HardwarePool::UnmapHardwareMemory(TrampolineAddress, TrampolinePages, TRUE); + /* Unmap temporary identity mapping and return success */ + MM::HardwarePool::UnmapRealModeMemory(ApPhysicalAddress, ApPages); return STATUS_SUCCESS; } diff --git a/xtoskrnl/includes/mm/hlpool.hh b/xtoskrnl/includes/mm/hlpool.hh index 41daac4cd..a4bb43dfb 100644 --- a/xtoskrnl/includes/mm/hlpool.hh +++ b/xtoskrnl/includes/mm/hlpool.hh @@ -20,6 +20,8 @@ namespace MM private: STATIC LOADER_MEMORY_DESCRIPTOR HardwareAllocationDescriptors[MM_HARDWARE_ALLOCATION_DESCRIPTORS]; STATIC PVOID HardwareHeapStart; + STATIC PHYSICAL_ADDRESS LowMemoryPhysicalAddress; + STATIC PVOID LowMemoryVirtualAddress; STATIC ULONG UsedHardwareAllocationDescriptors; public: @@ -27,12 +29,19 @@ namespace MM IN BOOLEAN Aligned, IN ULONGLONG MaximumAddress, OUT PPHYSICAL_ADDRESS Buffer); - STATIC XTAPI XTSTATUS AllocateRealModeMemory(IN PFN_NUMBER PageCount, - OUT PVOID *MemoryAddress); + STATIC XTAPI XTSTATUS AllocateLowMemory(OUT PPHYSICAL_ADDRESS PhysicalAddress, + OUT PVOID *VirtualAddress); + STATIC XTAPI ULONG CalculateRealModeAllocationPages(IN ULONG TrampolineCodeSize); + STATIC XTAPI XTSTATUS FreeHardwareMemory(IN PHYSICAL_ADDRESS PhysicalAddress, + IN PFN_NUMBER PageCount); + STATIC XTAPI XTSTATUS FreeRealModeMemory(IN PVOID VirtualAddress, + IN PFN_NUMBER PageCount); STATIC XTAPI XTSTATUS MapHardwareMemory(IN PHYSICAL_ADDRESS PhysicalAddress, IN PFN_NUMBER PageCount, IN BOOLEAN FlushTlb, OUT PVOID *VirtualAddress); + STATIC XTAPI XTSTATUS MapRealModeMemory(IN PHYSICAL_ADDRESS PhysicalAddress, + IN ULONG Size); STATIC XTAPI VOID MarkHardwareMemoryWriteThrough(IN PVOID VirtualAddress, IN PFN_NUMBER PageCount); STATIC XTAPI VOID RemapHardwareMemory(IN PVOID VirtualAddress, @@ -41,6 +50,8 @@ namespace MM STATIC XTAPI XTSTATUS UnmapHardwareMemory(IN PVOID VirtualAddress, IN PFN_NUMBER PageCount, IN BOOLEAN FlushTlb); + STATIC XTAPI VOID UnmapRealModeMemory(IN PHYSICAL_ADDRESS PhysicalAddress, + IN ULONG Size); }; } diff --git a/xtoskrnl/mm/data.cc b/xtoskrnl/mm/data.cc index 350580703..a89d69ee2 100644 --- a/xtoskrnl/mm/data.cc +++ b/xtoskrnl/mm/data.cc @@ -60,6 +60,12 @@ LOADER_MEMORY_DESCRIPTOR MM::HardwarePool::HardwareAllocationDescriptors[MM_HARD /* Live address of kernel's hardware heap */ PVOID MM::HardwarePool::HardwareHeapStart = MM_HARDWARE_HEAP_START_ADDRESS; +/* Physical address of kernel's low memory region */ +PHYSICAL_ADDRESS MM::HardwarePool::LowMemoryPhysicalAddress; + +/* Virtual address of kernel's low memory region */ +PVOID MM::HardwarePool::LowMemoryVirtualAddress; + /* Number of used hardware allocation descriptors */ ULONG MM::HardwarePool::UsedHardwareAllocationDescriptors = 0; diff --git a/xtoskrnl/mm/hlpool.cc b/xtoskrnl/mm/hlpool.cc index c57ef3f93..d9589136d 100644 --- a/xtoskrnl/mm/hlpool.cc +++ b/xtoskrnl/mm/hlpool.cc @@ -150,8 +150,11 @@ MM::HardwarePool::AllocateHardwareMemory(IN PFN_NUMBER PageCount, /** * Allocates a physical page in low memory (addressable in real-mode) and maps it into the virtual address space. * - * @param MemoryAddress - * Supplies a pointer to a variable that receives the identity-mapped virtual address of the allocated memory. + * @param PhysicalAddress + * Supplies a pointer to a variable that receives the physical address of the allocated memory. + * + * @param VirtualAddress + * Supplies a pointer to a variable that receives the virtual address of the allocated memory. * * @return This routine returns a status code indicating the success or failure of the operation. * @@ -159,39 +162,141 @@ MM::HardwarePool::AllocateHardwareMemory(IN PFN_NUMBER PageCount, */ XTAPI XTSTATUS -MM::HardwarePool::AllocateRealModeMemory(IN PFN_NUMBER PageCount, - OUT PVOID *MemoryAddress) +MM::HardwarePool::AllocateLowMemory(OUT PPHYSICAL_ADDRESS PhysicalAddress, + OUT PVOID *VirtualAddress) { - PHYSICAL_ADDRESS PhysicalAddress; - PFN_NUMBER PageFrameNumber; - PVOID VirtualAddress; + ULONG AllocationPages, TrampolineCodeSize; + PVOID TrampolineCode; XTSTATUS Status; + /* Check if low memory is already allocated and mapped */ + if(LowMemoryVirtualAddress && LowMemoryPhysicalAddress.QuadPart != 0) + { + /* Check if the caller requested the allocated addresses */ + if(PhysicalAddress != NULLPTR && VirtualAddress != NULLPTR) + { + /* Set the trampoline physical and virtual address and return success */ + *PhysicalAddress = LowMemoryPhysicalAddress; + *VirtualAddress = LowMemoryVirtualAddress; + } + + /* Return success */ + return STATUS_SUCCESS; + } + + /* Get trampoline information */ + AR::ProcessorSupport::GetTrampolineInformation(TrampolineApStartup, &TrampolineCode, &TrampolineCodeSize); + + /* Verify trampoline information */ + if(TrampolineCode == NULLPTR || TrampolineCodeSize == 0) + { + /* Failed to get trampoline information, return error */ + return STATUS_UNSUCCESSFUL; + } + + /* Compute number of pages for real-mode memory allocation (trampoline + processor start block + temporary stack) */ + AllocationPages = CalculateRealModeAllocationPages(TrampolineCodeSize); + /* Allocate physical memory in first 1MB */ - Status = AllocateHardwareMemory(PageCount, TRUE, 0x100000, &PhysicalAddress); + Status = AllocateHardwareMemory(AllocationPages, TRUE, 0x100000, &LowMemoryPhysicalAddress); if(Status != STATUS_SUCCESS) { /* Failed to allocate memory, return error */ return Status; } - /* Calculate virtual address and page frame number */ - VirtualAddress = (PVOID)(ULONG_PTR)PhysicalAddress.QuadPart; - PageFrameNumber = PhysicalAddress.QuadPart >> MM_PAGE_SHIFT; - - /* Identity map the memory to the virtual address */ - Status = MM::Paging::MapVirtualAddress(VirtualAddress, PageFrameNumber, MM_PTE_EXECUTE_READWRITE); + /* Map the memory to the virtual address */ + Status = MapHardwareMemory(LowMemoryPhysicalAddress, AllocationPages, FALSE, &LowMemoryVirtualAddress); if(Status != STATUS_SUCCESS) { - /* Failed to map memory, return error */ + /* Failed to map memory, free memory and return error */ + FreeHardwareMemory(LowMemoryPhysicalAddress, AllocationPages); return Status; } - /* Set the trampoline virtual address and return success */ - *MemoryAddress = VirtualAddress; + /* Check if the caller requested the allocated addresses */ + if(PhysicalAddress != NULLPTR && VirtualAddress != NULLPTR) + { + /* Set the trampoline physical and virtual address and return success */ + *PhysicalAddress = LowMemoryPhysicalAddress; + *VirtualAddress = LowMemoryVirtualAddress; + } + + /* Return success */ return STATUS_SUCCESS; } +/** + * Computes the total memory allocation size required for the real-mode trampoline execution environment. + * + * @param TrampolineCodeSize + * Supplies the size of the trampoline code in bytes. + * + * @return This routine returns the computed allocation size in pages. + * + * @since XT 1.0 + */ +XTAPI +ULONG +MM::HardwarePool::CalculateRealModeAllocationPages(IN ULONG TrampolineCodeSize) +{ + ULONG Size; + + /* Compute real-mode memory allocation size (trampoline + processor start block + temporary stack) */ + Size = TrampolineCodeSize + sizeof(PROCESSOR_START_BLOCK) + 512; + + /* Calculate and return number of pages */ + return (ULONG)(ROUND_UP(Size, MM_PAGE_SIZE) / MM_PAGE_SIZE); +} + +/** + * Releases physical memory allocated for kernel hardware layer. + * + * @param PhysicalAddress + * Supplies the physical address of the memory block to be freed. + * + * @param PageCount + * Supplies the number of pages associated with the allocation descriptor. + * + * @return This routine returns a status code indicating the success or failure of the operation. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +MM::HardwarePool::FreeHardwareMemory(IN PHYSICAL_ADDRESS PhysicalAddress, + IN PFN_NUMBER PageCount) +{ + PLOADER_MEMORY_DESCRIPTOR Descriptor; + PFN_NUMBER BasePage; + ULONG Index; + + /* Calculate base page from physical address */ + BasePage = PhysicalAddress.QuadPart >> MM_PAGE_SHIFT; + + /* Iterate through recorded hardware descriptors to find the matching allocation */ + for(Index = 0; Index < UsedHardwareAllocationDescriptors; Index++) + { + /* Get current hardware allocation descriptor */ + Descriptor = &HardwareAllocationDescriptors[Index]; + + /* Verify descriptor properties */ + if(Descriptor->MemoryType == LoaderHardwareCachedMemory && + Descriptor->BasePage == BasePage && + Descriptor->PageCount == PageCount) + { + /* Make descriptor available again */ + Descriptor->MemoryType = LoaderFree; + + /* Return success */ + return STATUS_SUCCESS; + } + } + + /* Descriptors not found, return error */ + return STATUS_INVALID_PARAMETER; +} + /** * Maps physical address to the virtual memory area used by kernel hardware layer. * @@ -291,6 +396,46 @@ MM::HardwarePool::MapHardwareMemory(IN PHYSICAL_ADDRESS PhysicalAddress, return STATUS_SUCCESS; } +/** + * Establishes a temporary identity mapping for a specified physical memory range to facilitate real-mode execution. + * + * @param PhysicalAddress + * Supplies the physical address of the trampoline memory. + * + * @param Size + * Supplies the size of the trampoline memory allocation. + * + * @return This routine returns a status code indicating the success or failure of the operation. + * + * @since XT 1.0 + */ +XTAPI +XTSTATUS +MM::HardwarePool::MapRealModeMemory(IN PHYSICAL_ADDRESS PhysicalAddress, + IN ULONG Pages) +{ + PFN_NUMBER Index; + XTSTATUS Status; + + /* Identity map each page of the real-mode memory allocation */ + for(Index = 0; Index < Pages; Index++) + { + /* Map the current physical page */ + Status = MM::Paging::MapVirtualAddress((PVOID)(PhysicalAddress.QuadPart + (Index * MM_PAGE_SIZE)), + (PhysicalAddress.QuadPart >> MM_PAGE_SHIFT) + Index, + MM_PTE_EXECUTE_READWRITE); + if(Status != STATUS_SUCCESS) + { + /* Failed to map the page, unmap previously mapped pages and return the error code */ + UnmapRealModeMemory(PhysicalAddress, Index * MM_PAGE_SIZE); + return Status; + } + } + + /* Return success */ + return STATUS_SUCCESS; +} + /** * Marks existing mapping as CD/WT to avoid delays in write-back cache. * @@ -425,3 +570,34 @@ MM::HardwarePool::UnmapHardwareMemory(IN PVOID VirtualAddress, /* Return success */ return STATUS_SUCCESS; } + +/** + * Removes the temporary identity mapping for a real-mode memory region. + * + * @param PhysicalAddress + * Supplies the physical address of the trampoline memory. + * + * @param Size + * Supplies the size of the trampoline memory allocation. + * + * @return This routine does not return any value. + * + * @since XT 1.0 + */ +XTAPI +VOID +MM::HardwarePool::UnmapRealModeMemory(IN PHYSICAL_ADDRESS PhysicalAddress, + IN ULONG Size) +{ + PFN_NUMBER AllocationPages, Index; + + /* Calculate number of pages to unmap */ + AllocationPages = (ULONG)(ROUND_UP(Size, MM_PAGE_SIZE) / MM_PAGE_SIZE); + + /* Iterate over the allocation pages to remove the identity mapping */ + for(Index = 0; Index < AllocationPages; Index++) + { + /* Clear the page table entry for the current virtual address */ + MM::Paging::ClearPte(MM::Paging::GetPteAddress((PVOID)(PhysicalAddress.QuadPart + (Index * MM_PAGE_SIZE)))); + } +}