exectos/xtoskrnl/mm/hlpool.c
Rafal Kupiec 6176ca38a8
Some checks failed
Builds / ExectOS (amd64) (push) Successful in 32s
Builds / ExectOS (i686) (push) Failing after 32s
Cleanup hardware allocation memory pool related code
2024-06-02 17:29:31 +02:00

384 lines
11 KiB
C

/**
* PROJECT: ExectOS
* COPYRIGHT: See COPYING.md in the top level directory
* FILE: xtoskrnl/mm/hlpool.c
* DESCRIPTION: Hardware layer pool memory management
* DEVELOPERS: Rafal Kupiec <belliash@codingworkshop.eu.org>
*/
#include <xtos.h>
/**
* Allocates physical memory for kernel hardware layer before memory manager gets initialized.
*
* @param PageCount
* Supplies the number of pages to be allocated.
*
* @param Aligned
* Specifies whether allocated memory should be aligned to 64k boundary or not.
*
* @param Buffer
* Supplies a buffer that receives the physical address.
*
* @return This routine returns a status code.
*
* @since XT 1.0
*/
XTAPI
XTSTATUS
MmAllocateHardwareMemory(IN PFN_NUMBER PageCount,
IN BOOLEAN Aligned,
OUT PULONG_PTR Buffer)
{
PLOADER_MEMORY_DESCRIPTOR Descriptor, ExtraDescriptor, HardwareDescriptor;
PFN_NUMBER Alignment, MaxPage;
ULONGLONG PhysicalAddress;
PLIST_ENTRY ListEntry;
/* Assume failure */
*Buffer = 0;
/* Calculate maximum page address */
MaxPage = MM_MAXIMUM_PHYSICAL_ADDRESS >> MM_PAGE_SHIFT;
/* Make sure there are at least 2 descriptors available */
if((MmpUsedHardwareAllocationDescriptors + 2) > MM_HARDWARE_ALLOCATION_DESCRIPTORS)
{
/* Not enough descriptors, return error */
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Scan memory descriptors provided by the boot loader */
ListEntry = KeInitializationBlock->MemoryDescriptorListHead.Flink;
while(ListEntry != &KeInitializationBlock->MemoryDescriptorListHead)
{
Descriptor = CONTAIN_RECORD(ListEntry, LOADER_MEMORY_DESCRIPTOR, ListEntry);
/* Align memory to 64KB if needed */
Alignment = Aligned ? (((Descriptor->BasePage + 0x0F) & ~0x0F) - Descriptor->BasePage) : 0;
/* Ensure that memory type is free for this descriptor */
if(Descriptor->MemoryType == LoaderFree)
{
/* Check if descriptor is big enough and if it fits under the maximum physical address */
if(Descriptor->BasePage &&
((Descriptor->BasePage + PageCount + Alignment) < MaxPage) &&
(Descriptor->PageCount >= (PageCount + Alignment)))
{
/* Set physical address */
PhysicalAddress = (Descriptor->BasePage + Alignment) << MM_PAGE_SHIFT;
break;
}
}
/* Move to next descriptor */
ListEntry = ListEntry->Flink;
}
/* Make sure we found a descriptor */
if(ListEntry == &KeInitializationBlock->MemoryDescriptorListHead)
{
/* Descriptor not found, return error */
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Allocate new descriptor */
HardwareDescriptor = &MmpHardwareAllocationDescriptors[MmpUsedHardwareAllocationDescriptors];
HardwareDescriptor->BasePage = Descriptor->BasePage + Alignment;
HardwareDescriptor->MemoryType = LoaderHardwareCachedMemory;
HardwareDescriptor->PageCount = PageCount;
/* Update hardware allocation descriptors count */
MmpUsedHardwareAllocationDescriptors++;
/* Check if alignment was done */
if(Alignment)
{
/* Check if extra descriptor is needed to describe the allocation */
if(Descriptor->PageCount > (PageCount + Alignment))
{
/* Initialize extra descriptor */
ExtraDescriptor = &MmpHardwareAllocationDescriptors[MmpUsedHardwareAllocationDescriptors];
ExtraDescriptor->BasePage = Descriptor->BasePage + Alignment + (ULONG)PageCount;
ExtraDescriptor->MemoryType = LoaderFree;
ExtraDescriptor->PageCount = Descriptor->PageCount - (Alignment + (ULONG)PageCount);
/* Update hardware allocation descriptors count */
MmpUsedHardwareAllocationDescriptors++;
/* Insert extra descriptor in the list */
RtlInsertHeadList(&Descriptor->ListEntry, &ExtraDescriptor->ListEntry);
}
/* Trim source descriptor to the alignment */
Descriptor->PageCount = Alignment;
/* Insert new descriptor in the list */
RtlInsertHeadList(&Descriptor->ListEntry, &HardwareDescriptor->ListEntry);
}
else
{
/* Consume pages from the source descriptor */
Descriptor->BasePage += (ULONG)PageCount;
Descriptor->PageCount -= (ULONG)PageCount;
/* Insert new descriptor in the list */
RtlInsertTailList(&Descriptor->ListEntry, &HardwareDescriptor->ListEntry);
/* Check if source descriptor is fully consumed */
if(Descriptor->PageCount == 0)
{
/* Remove descriptor from the list */
RtlRemoveEntryList(&Descriptor->ListEntry);
}
}
/* Return physical address */
*Buffer = PhysicalAddress;
return STATUS_SUCCESS;
}
/**
* Maps physical address to the virtual memory area used by kernel hardware layer.
*
* @param PhysicalAddress
* Supplies the physical address to map.
*
* @param PageCount
* Supplies the number of pages to be mapped.
*
* @param FlushTlb
* Specifies whether to flush the TLB or not.
*
* @param VirtualAddress
* Supplies a buffer that receives the virtual address of the mapped pages.
*
* @return This routine returns a status code.
*
* @since XT 1.0
*/
XTAPI
XTSTATUS
MmMapHardwareMemory(IN PHYSICAL_ADDRESS PhysicalAddress,
IN PFN_NUMBER PageCount,
IN BOOLEAN FlushTlb,
OUT PVOID *VirtualAddress)
{
PVOID BaseAddress, ReturnAddress;
PFN_NUMBER MappedPages;
PHARDWARE_PTE PtePointer;
/* Initialize variables */
BaseAddress = MmpHardwareHeapStart;
MappedPages = 0;
ReturnAddress = BaseAddress;
*VirtualAddress = NULL;
/* Iterate through all pages */
while(MappedPages < PageCount)
{
/* Check if address overflows */
if(BaseAddress == NULL)
{
/* Not enough free pages, return error */
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Get PTE pointer and advance to next page */
PtePointer = (PHARDWARE_PTE)MmpGetPteAddress(ReturnAddress);
ReturnAddress = (PVOID)(ULONG_PTR)ReturnAddress + MM_PAGE_SIZE;
/* Check if PTE is valid */
if(PtePointer->Valid)
{
/* PTE is not available, go to the next one */
BaseAddress = ReturnAddress;
MappedPages = 0;
continue;
}
/* Increase number of mapped pages */
MappedPages++;
}
/* Take the actual base address with an offset */
ReturnAddress = (PVOID)(ULONG_PTR)(BaseAddress + PAGE_OFFSET(PhysicalAddress.LowPart));
/* Check if base address starts at the beginning of the heap */
if(BaseAddress == MmpHardwareHeapStart)
{
/* Move heap beyond base address */
MmpHardwareHeapStart = (PVOID)((ULONG_PTR)BaseAddress + ((ULONG_PTR)PageCount << MM_PAGE_SHIFT));
}
/* Iterate through mapped pages */
while(MappedPages--)
{
/* Get PTE pointer */
PtePointer = (PHARDWARE_PTE)MmpGetPteAddress(BaseAddress);
/* Fill the PTE */
PtePointer->PageFrameNumber = (PFN_NUMBER)(PhysicalAddress.QuadPart >> MM_PAGE_SHIFT);
PtePointer->Valid = 1;
PtePointer->Writable = 1;
/* Advance to the next address */
PhysicalAddress.QuadPart += MM_PAGE_SIZE;
BaseAddress = (PVOID)((ULONG_PTR)BaseAddress + MM_PAGE_SIZE);
}
/* Check if TLB needs to be flushed */
if(FlushTlb)
{
/* Flush the TLB */
MmFlushTlb();
}
/* Return virtual address */
*VirtualAddress = ReturnAddress;
return STATUS_SUCCESS;
}
/**
* Marks existing mapping as CD/WT to avoid delays in write-back cache.
*
* @param VirtualAddress
* Supplies the virtual address region to mark as CD/WT.
*
* @param PageCount
* Supplies the number of mapped pages.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTAPI
VOID
MmMarkHardwareMemoryWriteThrough(IN PVOID VirtualAddress,
IN PFN_NUMBER PageCount)
{
PHARDWARE_PTE PtePointer;
PFN_NUMBER Page;
/* Get PTE address from virtual address */
PtePointer = (PHARDWARE_PTE)MmpGetPteAddress(VirtualAddress);
/* Iterate through mapped pages */
for(Page = 0; Page < PageCount; Page++)
{
/* Mark pages as CD/WT */
PtePointer->CacheDisable = 1;
PtePointer->WriteThrough = 1;
PtePointer++;
}
}
/**
* Remaps the PTE to new physical address.
*
* @param VirtualAddress
* Supplies the virtual address to remap.
*
* @param PhysicalAddress
* Supplies a new physical address.
*
* @param FlushTlb
* Specifies whether to flush the TLB or not.
*
* @return This routine does not return any value.
*
* @since XT 1.0
*/
XTAPI
VOID
MmRemapHardwareMemory(IN PVOID VirtualAddress,
IN PHYSICAL_ADDRESS PhysicalAddress,
IN BOOLEAN FlushTlb)
{
PHARDWARE_PTE PtePointer;
/* Get PTE address from virtual address */
PtePointer = (PHARDWARE_PTE)MmpGetPteAddress(VirtualAddress);
/* Remap the PTE */
PtePointer->PageFrameNumber = (PFN_NUMBER)(PhysicalAddress.QuadPart >> MM_PAGE_SHIFT);
PtePointer->Valid = 1;
PtePointer->Writable = 1;
/* Check if TLB needs to be flushed */
if(FlushTlb)
{
/* Flush the TLB */
MmFlushTlb();
}
}
/**
* Unmaps a Page Table Entry corresponding to the given virtual address.
*
* @param VirtualAddress
* Supplies the virtual address to unmap.
*
* @param PageCount
* Supplies the number of mapped pages.
*
* @param FlushTlb
* Specifies whether to flush the TLB or not.
*
* @return This routine returns a status code.
*
* @since XT 1.0
*/
XTAPI
XTSTATUS
MmUnmapHardwareMemory(IN PVOID VirtualAddress,
IN PFN_NUMBER PageCount,
IN BOOLEAN FlushTlb)
{
PHARDWARE_PTE PtePointer;
PFN_NUMBER Page;
/* Check if address is valid hardware memory */
if(VirtualAddress < (PVOID)MM_HARDWARE_VA_START)
{
/* Invalid address, return error */
return STATUS_INVALID_PARAMETER;
}
/* Align virtual address down to page boundary */
VirtualAddress = (PVOID)((ULONG_PTR)VirtualAddress & ~(MM_PAGE_SIZE - 1));
/* Get PTE address from virtual address */
PtePointer = (PHARDWARE_PTE)MmpGetPteAddress(VirtualAddress);
/* Iterate through mapped pages */
for(Page = 0; Page < PageCount; Page++)
{
/* Unmap the PTE and get the next one */
PtePointer->CacheDisable = 0;
PtePointer->Valid = 0;
PtePointer->Writable = 0;
PtePointer->WriteThrough = 0;
PtePointer->PageFrameNumber = 0;
PtePointer++;
}
/* Check if TLB needs to be flushed */
if(FlushTlb)
{
/* Flush the TLB */
MmFlushTlb();
}
/* Check if heap can be reused */
if(MmpHardwareHeapStart > VirtualAddress)
{
/* Free VA space */
MmpHardwareHeapStart = VirtualAddress;
}
/* Return success */
return STATUS_SUCCESS;
}