My page directories and tables (pml4, pdpe, pde, pte) are (if necessary) dynamically expanded, when a page needs to be allocated. If a required directory or table isn't created yet, I allocate some space using kmalloc, and create a new directory or table and add it to my existing structure.
When I need to find a specific page, I walk though this path "PML4 -> PDPE -> PDE -> PTE" using the pointer to the next level, e.g. a PML4 entry points to a PDPE which points to a PDE. The pointers is of cause the physical address to the next level, and this isn't a problem as long as the memory is identity mapped.
The first directories and tables are stored in an identity mapped area, right after the kernel, which is fine because then I can walk though the structure to find my requested page. The problem occurs when I enable paging and the heap. When the heap is enabled, and I need to malloc some space in order to expand my paging structure, it returns a virtual address in the heap, and optionally the physical address. And here is the problem:
When I expand my paging structure, I need to use the physical address to point to the next entry in the structure. But in order to walk though the stucture and modify it, I need the virtual address. This means that I either (a) need a method to convert the stored physical address to a virtual address or (b) have to store a lookup table, so I can lookup what virtual address that matched the physical, which will require even more memory.
I'm using a dymanic structure, because I don't want to support a fixed size of memory. Currently my memory manager is designed to handle 36-bit which equals 64 GB (this can easily be changed to more or less bits). A quick calculation tells me that it will require about 256 MB of memory to store the structures needed to map 64 GB of RAM.
Are there anyone who can suggest a solution to my problem? Should I implement this in another way?
Please ask if there's anything that doesn't make sense, I had a hard time explaing this!
It's the first time I've written code for handling paging and the heap myself, so I'm still learning
I have attached my function for dynamically expand the paging structure, when a page is requested. In this code, kmalloc doesn't return the physical address, because the first directories and tables are stored in identity mapped memory. But I can get both the physical and the virtual address by using kmalloc_ap instead.
Code: Select all
pte_t *get_page(uint64_t addr, int make, pde_t *dir)
{
pde_t *ptr;
pte_t *page;
uint32_t pml4 = ((addr >> 39) & 0x1FF);
uint32_t pdpe = ((addr >> 30) & 0x1FF);
uint32_t pde = ((addr >> 21) & 0x1FF);
uint32_t pte = ((addr >> 12) & 0x1FF);
if(dir[pml4].next_base)
{
ptr = (pde_t*)((uint64_t)dir[pml4].next_base << 12); // Pointer to pdpe
}
else if(make)
{
pde_t *tmp = create_pde();
dir[pml4].next_base = (((uint64_t)tmp >> 12) & BIT_MASK);
ptr = tmp;
}
else
{
return 0;
}
if(ptr[pdpe].next_base)
{
ptr = (pde_t*)((uint64_t)ptr[pdpe].next_base << 12); // Pointer to pde
}
else if(make)
{
pde_t *tmp = create_pde();
ptr[pdpe].next_base = (((uint64_t)tmp >> 12) & BIT_MASK);
ptr = tmp;
}
else
{
return 0;
}
if(ptr[pde].next_base)
{
page = (pte_t*)((uint64_t)ptr[pde].next_base << 12); // Pointer to pte
}
else if(make)
{
uint64_t tmp = kmalloc_a(sizeof(pte_t)*512);
memset((void*)tmp, 0, 4096);
ptr[pde].next_base = ((tmp >> 12) & BIT_MASK);
page = (pte_t*)tmp;
}
else
{
return 0;
}
return &page[pte];
}
pde_t *create_pde()
{
pde_t *pde = (pde_t*)kmalloc_a(sizeof(pde_t)*512);
int i = 0;
for(i=0;i<512;i++)
{
pde[i].present = 1;
pde[i].write = 1;
pde[i].mode = KERNEL;
pde[i].pwt = 0;
pde[i].pcd = 0;
pde[i].accessed = 0;
pde[i].ignored = 0;
pde[i].next_base = 0;
pde[i].unused = 0;
pde[i].nx = 0;
}
return pde;
}