Recursive page mapping causes PDE movement
Posted: Sun Apr 11, 2010 7:30 am
I've decided to scrap my old kernel (monolithic, horrible design) and try my hand at creating a microkernel. So far, I've been doing fairly well - I can bootstrap multitasking, and do a jump into ring 3; I've learned a lot. However, I've noticed a few 'phantom' page mappings in my page directory. I've traced it down to the allocation of kernel heap page directory entries (so that it doesn't lose state on context switches.)
I use a recursive page mapping, which puts the page directory at 0xFFBFF000, and the page tables in one sequential block starting at 0xFFC00000. I have a higher-half kernel, so also have a mapping at 0xFFF00000 to 0x01070000 (the physical address of my higher-half mapping page table). Apart from that, my page directory is zeroed.
This all works out relatively simple, until I allocate page directory entries for my kernel heap. The code I use to do so is below:
(properKernel is just the virtual address of my page directory table, 0xFFBFF000)
This should create page directory entries between 0xD0000000 and 0xEFFFF000, the range of my kernel heap. It does this, but it also creates page directory entries which have the correct physical addresses, but have virtual addresses between 0xFFF40000 and 0xFFFBFFFF. I'm aware of the cause of this - the recursive page mapping means that my page directory entries are also viewed as page tables, putting them in that range. But I don't have any idea of a solution.
I've verified that the page directory entries are actually present, and they are. By adding a page table entry to them, I make them show up in Bochs' paging structure viewer. My main concern is the massive loss of virtual address space that I'm seeing.
I would appreciate any help on how to make my recursive page mappings 'right', and how to make my page directory allocations stay where I tell them to.
For reference, I created my page directory layout in assembly; the code I used to do so is listed below.
When I boot the kernel, I map it to 0xC0100000 and erase the identity mapping of the first 4 MiB in this way:
I use a recursive page mapping, which puts the page directory at 0xFFBFF000, and the page tables in one sequential block starting at 0xFFC00000. I have a higher-half kernel, so also have a mapping at 0xFFF00000 to 0x01070000 (the physical address of my higher-half mapping page table). Apart from that, my page directory is zeroed.
This all works out relatively simple, until I allocate page directory entries for my kernel heap. The code I use to do so is below:
Code: Select all
for(unsigned int i = KernelHeapStart >> 22; i <= (KernelHeapEnd >> 22); i++)
{
if((properKernel[i] & x86::PageDirectoryFlags::Present) == 0)
properKernel[i] = (unsigned int)Physical::PageAllocator::AllocatePage(false) |
x86::PageDirectoryFlags::Present | x86::PageDirectoryFlags::ReadWrite;
else
asm volatile ("cli;hlt" : : "a"(0xBAD));
}
This should create page directory entries between 0xD0000000 and 0xEFFFF000, the range of my kernel heap. It does this, but it also creates page directory entries which have the correct physical addresses, but have virtual addresses between 0xFFF40000 and 0xFFFBFFFF. I'm aware of the cause of this - the recursive page mapping means that my page directory entries are also viewed as page tables, putting them in that range. But I don't have any idea of a solution.
I've verified that the page directory entries are actually present, and they are. By adding a page table entry to them, I make them show up in Bochs' paging structure viewer. My main concern is the massive loss of virtual address space that I'm seeing.
I would appreciate any help on how to make my recursive page mappings 'right', and how to make my page directory allocations stay where I tell them to.
For reference, I created my page directory layout in assembly; the code I used to do so is listed below.
Code: Select all
section .data
align 0x1000
BootPageTable: ; Contains the initial identity mapping of the first 4 MiB
%assign i 0
%rep 1024
dd i | 111b
%assign i i+4096
%endrep
align 0x1000
; This page table is the same as the one above, but gets placed at a different page directory index
KernelPageTable: ; Contains the main kernel page table, mapping the first 4 MiB to 0xC000000
%assign i 0
%rep 1024
dd i | 111b ; Note: at present this is set to be user-accessible. I don't want this when I load a program
%assign i i+4096
%endrep
align 0x1000
ReverseMappingTable:
%assign i 0
%rep 1023 ; The last entry points to the page directory
dd 0
%assign i i+1
%endrep
dd (PageDirectory - VirtualAddressBase + 11b)
align 0x1000
PageDirectory:
dd (BootPageTable - VirtualAddressBase + 11b)
times (KernelPageNumber - 1) dd 0
dd (KernelPageTable - VirtualAddressBase + 111b)
; There's a -2 because the reverse mappings go at the end
times (1024 - KernelPageNumber - 1 - 2) dd 0
dd (ReverseMappingTable - VirtualAddressBase + 11b)
dd (PageDirectory - VirtualAddressBase + 11b)
Code: Select all
mov dword [PageDirectory], 0
invlpg [0]