Thpertic wrote:Code: Select all
#define PAGE_TABLE_INDEX(x) (((x) >> 12) & 0x3ff)
Code: Select all
for (i = KERNEL_VIRTUAL_BASE + 0x100000, addr = 0x100000; (uint32_t)i <= _end_addr; i += PAGE_SIZE, addr += PAGE_SIZE)
kernelPT[PAGE_TABLE_INDEX(i)] = addr | 0x000000003;
Looks about right.
Thpertic wrote:Then, I also changed something in the switch assembly, now it just invalidate the TLB and load the new page directory:
Code: Select all
pusha
; Invalidate the TLB
mov ecx, cr3
invlpg [ecx]
; Load PDBR (CR3), it must contains the physical address of the Page Directory
mov ecx, [_cur_pageDirectory]
mov cr3, ecx
popa
ret
What did I just say? Don't mix up physical and virtual addresses. CR3 contains a physical address. "invlpg" requires a virtual one.
This isn't necessary anyway, since loading CR3 already causes a TLB flush (except in global pages, but that is a feature you aren't using yet). The pusha and popa aren't necessary either if you'd just follow the ABI. In your case, you only clobber ecx, which is volatile, so you don't need to save it.
Also, CR3 contains the physical address of the page directory. And the page directory must contain the physical addresses of the page tables. So, what you need to do is:
Code: Select all
pageDirectory[0] = v2p(first4MBpt) | 3;
pageDirectory[PAGE_DIRECTORY_INDEX(KERNEL_VIRTUAL_BASE)] = v2p(kernelPT) | 0x000000003;
loadPageDir(v2p(pageDirectory));
And then think how you get from virtual to physical address. I don't know where your kmalloc() is allocating from. Maybe something like
Code: Select all
uintptr_t v2p(void* addr) {
uintptr_t x = (uintptr_t)addr;
if (x - KERNEL_VIRTUAL_BASE < (uintptr_t)_end_linmap - KERNEL_VIRTUAL_BASE)
return x - KERNEL_VIRTUAL_BASE;
uint32_t pde = _cur_pageDirectory[PAGE_DIRECTORY_INDEX(x)];
assert(pde & 1);
if (pde & 128)
return (pde & 0xffc00000) + (x & 0x003fffff);
uint32_t *pt = p2v(pde & 0xfffff000);
assert(pt);
uint32_t pte = pt[PAGE_TABLE_INDEX(x)];
assert(pte & 1);
return (pte & 0xfffff000) + (x & 0x00000fff);
}
void *p2v(uint32_t paddr) {
if (paddr < _end_linmap - KERNEL_VIRTUAL_BASE)
return (void*)(paddr + KERNEL_VIRTUAL_BASE);
return 0; //XXX: Or panic instead? Are the page tables always mapped?
}
This depends on assertions. I suggest you panic if an assertion fails (stop executing, write a crashlog to console, stop all other cores).
This also depends on a new global variable, _end_linmap, which determines the current end of the linear mapping from 1MB to 3GB. Basically, this is where the cat chases its own tail: You need virtual and physical addresses of the page tables always to hand, and can't look it up in the page tables. If you only have a physical address, you need a simple way to find the corresponding virtual address.
This is usually accomplished by putting the paging structures inside the area that is linearly mapped. Then there is always a very simple relationship between physical and virtual address for the page tables. Alternatively, another linear area can be set aside. Problem is that it constrains the possible locations of the page tables in physical address space. Thus, you might run into the problem that you are out of space for such things, even if there'd be room for other stuff (linear mapping area is full and can't be expanded since either the physical or virtual memory following it is already in use).
On AMD64 you don't have this problem, since all of physical address space can be mapped at once.
Oh, and also:
Code: Select all
uint32_t loadPageDir(uint32_t); /*loads CR3 with argument and returns old CR3. */
Code: Select all
loadPageDir:
mov ecx, [esp + 4]
mov eax, cr3
mov cr3, ecx
ret
HTH.