Freeldr内存管理模块的代码大多在freeldr/freeldr/mm文件夹中。我们从初始化部分开始看起。
freeldr/freeldr/mm/Meminit.c
- BOOLEAN MmInitializeMemoryManager(VOID)
- {
- // 获得内存总页数
- TotalPagesInLookupTable = MmGetAddressablePageCountIncludingHoles();
- // 为LookupTable生成空间
- PageLookupTableAddress = MmFindLocationForPageLookupTable(TotalPagesInLookupTable);
- LastFreePageHint = TotalPagesInLookupTable;
- if (PageLookupTableAddress ==
0) - {
- printf("Error initializing memory manager!/n");
- return FALSE;
- }
- // 初始化LookupTable
- MmInitPageLookupTable(PageLookupTableAddress, TotalPagesInLookupTable);
- MmUpdateLastFreePageHint(PageLookupTableAddress, TotalPagesInLookupTable);
- // 获得LookupTable中的空闲页数量
- FreePagesInLookupTable = MmCountFreePagesInLookupTable(PageLookupTableAddress, TotalPagesInLookupTable);
- // 初始化堆
- MmInitializeHeap(PageLookupTableAddress);
- return TRUE;
- }
首先使用MmGetAddressablePageCountIncludingHoles获得一共有多少内存页(4kb),这其中包括了BIOS AREA等不能被系统使用的内存。
MmInitializeMemoryManager ->
MmGetAddressablePageCountIncludingHoles
freeldr/freeldr/mm/mm.c
- ULONG MmGetAddressablePageCountIncludingHoles(VOID)
- {
- MEMORY_DESCRIPTOR* MemoryDescriptor = NULL;
- ULONG EndPage =
0; - /* 使用int 0x15等调用获得一个内存描述符, 这个描述符的格式和int 0x15返回的相同同 */
- while ((MemoryDescriptor = ArcGetMemoryDescriptor(MemoryDescriptor)) != NULL)
- {
- /* 找到最高的页号 */
- if (MemoryDescriptor->BasePage + MemoryDescriptor->PageCount > EndPage)
- {
- EndPage = MemoryDescriptor->BasePage + MemoryDescriptor->PageCount;
- }
- }
- return EndPage;
- }
他使用int 0x15中断获得每个内存描述符,找到最大的内存页并返回。
获得了内存页数后,调用
MmFindLocationForPageLookupTable从空闲内存页(Memroy_Free)的最高地址找到可以容纳LookupTable的空闲。
LookupTable是一个PAGE_LOOIUP_TABLE_ITEM数组,每个数组元素描述了一个页的类型(LoaderFree等), 当PageAllocated为已分配时(除LoaderFree外), PageAllocationLength为分配的块数,如果该页不是分配时的第一个页这个值为0。
所以内存有多少页LookupTable数组就有多少个元素。
- typedef struct
- {
- TYPE_OF_MEMORY PageAllocated; // Type of allocated memory (LoaderFree if this memory is free)
- ULONG PageAllocationLength; // Number of pages allocated (or zero if this isn't the first page in the chain)
- } PAGE_LOOKUP_TABLE_ITEM, *PPAGE_LOOKUP_TABLE_ITEM;
MmInitializeMemoryManager -> MmFindLocationForPageLookupTable
freeldr/freeldr/mm/meminit.c
- PVOID MmFindLocationForPageLookupTable(ULONG TotalPageCount)
- {
- MEMORY_DESCRIPTOR* MemoryDescriptor = NULL;
- ULONG PageLookupTableSize;
- ULONG PageLookupTablePages;
- ULONG PageLookupTableStartPage =
0; - PVOID PageLookupTableMemAddress = NULL;
- /* 首先计算生成LoopupTable需要多少页 */
- PageLookupTableSize = TotalPageCount * sizeof(PAGE_LOOKUP_TABLE_ITEM);
- PageLookupTablePages = PageLookupTableSize / MM_PAGE_SIZE;
- /* 找到可以容纳LoopupTable的最高内存地址 */
- while ((MemoryDescriptor = ArcGetMemoryDescriptor(MemoryDescriptor)) != NULL)
- {
- if (MemoryDescriptor->MemoryType != MemoryFree)
- { /* 该内存块被占用 */
- continue;
- }
- if (MemoryDescriptor->PageCount < PageLookupTablePages)
- { /* 这个内存块不够大 */
- continue;
- }
- if (MemoryDescriptor->BasePage < PageLookupTableStartPage)
- { /* 该内存块的地址比以找到的地 */
- continue;
- }
- /* 这里获得的是容纳LoopupTable的最高内存地址 */
- PageLookupTableStartPage = MemoryDescriptor->BasePage;
- PageLookupTableMemAddress = (PVOID)((ULONG_PTR)
- (MemoryDescriptor->BasePage + MemoryDescriptor->PageCount) * MM_PAGE_SIZE
- - PageLookupTableSize);
- }
- return PageLookupTableMemAddress;
- }
这个函数搜索计算机中的所有内存块,找到可以容纳PageLookupTable的最高地址的内存。返回给调用者。
现在找到了放置PageLookupTalbe的空间,调用
MmInitPageLookupTable对他进行初始化。这个函数有两个参数,PageLookupTable的地址和它的元素个数(也就是我们页的个数)
MmInitializeMemoryManager ->
MmInitPageLookupTable
freeldr/freeldr/mm/meminit.c
- VOID MmInitPageLookupTable(PVOID PageLookupTable, ULONG TotalPageCount)
- {
- MEMORY_DESCRIPTOR* MemoryDescriptor = NULL;
- TYPE_OF_MEMORY MemoryMapPageAllocated;
- ULONG PageLookupTableStartPage;
- ULONG PageLookupTablePageCount;
- // 首先把所有的页标注为LoaderFirmwarePermanent
- MmMarkPagesInLookupTable(PageLookupTable,
0, TotalPageCount, LoaderFirmwarePermanent); - while ((MemoryDescriptor = ArcGetMemoryDescriptor(MemoryDescriptor)) != NULL)
- {
- // 把int 0x15返回的内存类型转换为Loader中对应的
- switch (MemoryDescriptor->MemoryType)
- {
- case MemoryFree:
- { // 空闲内存
- MemoryMapPageAllocated = LoaderFree;
- break;
- }
- case MemoryFirmwarePermanent:
- { // BIOS中断表等硬件使用的内存
- MemoryMapPageAllocated = LoaderFirmwarePermanent;
- break;
- }
- case MemoryFirmwareTemporary:
- { // 堆栈、缓冲区等
- MemoryMapPageAllocated = LoaderFirmwareTemporary;
- break;
- }
- case MemoryLoadedProgram:
- { // freeldr代码本身
- MemoryMapPageAllocated = LoaderLoadedProgram;
- break;
- }
- case MemorySpecialMemory:
- { // 其它类型, 包括Prot下的堆栈等
- MemoryMapPageAllocated = LoaderSpecialMemory;
- break;
- }
- default:
- {
- MemoryMapPageAllocated = LoaderSpecialMemory;
- break;
- }
- }
- // 在PageLoopupTable中标记该段内存
- MmMarkPagesInLookupTable(PageLookupTable, MemoryDescriptor->BasePage, MemoryDescriptor->PageCount, MemoryMapPageAllocated);
- }
- // 将PageLookupTable本身标记为LoaderFirmwareTemporary
- PageLookupTableStartPage = MmGetPageNumberFromAddress(PageLookupTable);
- PageLookupTablePageCount = MmGetPageNumberFromAddress((PVOID)((ULONG_PTR)PageLookupTable + ROUND_UP(TotalPageCount * sizeof(PAGE_LOOKUP_TABLE_ITEM), MM_PAGE_SIZE))) - PageLookupTableStartPage;
- MmMarkPagesInLookupTable(PageLookupTable, PageLookupTableStartPage, PageLookupTablePageCount, LoaderFirmwareTemporary);
- }
这个函数把ArcGetMemoryDescriptor返回的内存类型转换为Loader中的对应类型,并调用MmMarkPagesInLookupTable标记对应内存。最后PageLookupTable本身占用的空间被标记为LoaderFirmwareTemporary。
这个函数返回后PageLookupTable初始化完毕, 每个页在其中都有对应的表项。
最后MmInitializeMemoryManager使用MmInitializeHeap初始化堆。
- VOID MmInitializeHeap(PVOID PageLookupTable)
- {
- ULONG PagesNeeded;
- ULONG HeapStart;
- MEMORY_TYPE Type;
- PPAGE_LOOKUP_TABLE_ITEM RealPageLookupTable = (PPAGE_LOOKUP_TABLE_ITEM)PageLookupTable;
- // 首先保留1mb -- 2mb这段空间, 这段空间之后用作内核, 所以不能使用
- Type = RealPageLookupTable[
0x100].PageAllocated; - MmMarkPagesInLookupTable(PageLookupTableAddress,
0x100,0xFF, LoaderSystemCode); - // 从PageLookupTable中寻找足够大的空间
- PagesNeeded = HEAP_PAGES + STACK_PAGES;
- HeapStart = MmFindAvailablePages(PageLookupTable, TotalPagesInLookupTable, PagesNeeded, FALSE);
- MmMarkPagesInLookupTable(PageLookupTableAddress,
0x100,0xFF, Type); - if (HeapStart ==
0) - return;
- // 初始化堆, 这是开源的bget堆管理库
- bpool(HeapStart << MM_PAGE_SHIFT, PagesNeeded << MM_PAGE_SHIFT);
- // 把对占用的页标记为LoaderOsloaderHeap
- MmMarkPagesInLookupTable(PageLookupTableAddress, HeapStart, PagesNeeded, LoaderOsloaderHeap);
- }
这个函数很简单,找到合适的内存后调用bpool初始化堆,把堆占用的内存标记为LoaderOsLoaderHeap。
到这里内存管理部分初始化就完毕了。
最后来看看怎么分配内存。
freeldr分配内存主要使用两个函数MmHeapAlloc从堆中分配、MmAllocateMemoryWithType直接从LookupTable中分配。
MmHeapAlloc主要用在分配小于1个页的内存时使用, 它就是不bget函数简单的包装。
- PVOID MmHeapAlloc(ULONG MemorySize)
- {
- PVOID Result;
- // 调用bget从堆中分配内存
- Result = bget(MemorySize);
- return Result;
- }
当分配大于一个页的内存时, 一般使用MmAllocateMemoryWithType函数, 这个函数直接动LookupTable中搜索适当的内存块。
- PVOID MmAllocateMemoryWithType(ULONG MemorySize, TYPE_OF_MEMORY MemoryType)
- {
- ULONG PagesNeeded;
- ULONG FirstFreePageFromEnd;
- PVOID MemPointer;
- if (MemorySize ==
0) - {
- return NULL;
- }
- // 大小按照页对齐
- MemorySize = ROUND_UP(MemorySize,
4); - PagesNeeded = ROUND_UP(MemorySize, MM_PAGE_SIZE) / MM_PAGE_SIZE;
- // 空间不够
- if (FreePagesInLookupTable < PagesNeeded)
- {
- return NULL;
- }
- // 从LookupTable中找到适当的空间
- FirstFreePageFromEnd = MmFindAvailablePages(PageLookupTableAddress, TotalPagesInLookupTable, PagesNeeded, FALSE);
- if (FirstFreePageFromEnd ==
0) - {
- return NULL;
- }
- // 分配这段空间并把这段空间标记为MemoryType
- MmAllocatePagesInLookupTable(PageLookupTableAddress, FirstFreePageFromEnd, PagesNeeded, MemoryType);
- FreePagesInLookupTable -= PagesNeeded;
- MemPointer = (PVOID)((ULONG_PTR)FirstFreePageFromEnd * MM_PAGE_SIZE);
- // LoaderPagesSpanned记录了freeldr和其使用的内存占用的空间
- if ((((ULONG_PTR)MemPointer + MemorySize + PAGE_SIZE -
1) >> PAGE_SHIFT) > LoaderPagesSpanned) - LoaderPagesSpanned = (((ULONG_PTR)MemPointer + MemorySize + PAGE_SIZE -
1) >> PAGE_SHIFT); - return MemPointer;
- }
这一这个函数最后更新了LoaderPagesSpanned全局变量, 这个变量记录了freeldr占用的空间大小,最后freeldr打开分页时需要用到这个值。