Mapping memory out of kmalloc() to the kernel page directory.
Posted: Sat Oct 19, 2024 12:30 am
Hello all, I've been having this problem with my hobby OS and I'm at my wits end. I was hoping someone could help me out.
There are two functions involved, one is my memory manager. I'm using a buddy allocator with a minimum block size of 0x1000. I've pseudo coded them so you can get an idea of how they work. My heap starts at 0x40000000 and ends at 0xC0000000
Pseudocode:
The second function is map_page, which maps a virtual address to a physical one. Right now all I'm doing is identity mapping from the start of memory to the start of the heap.
Pseudocode:
The problem is that I want to alter kmalloc() so that it goes ahead and maps whatever memory you get from it to the kernel page directory. This is proving to be much more difficult than I thought.
My first thought was to do this:
But what would happen is:
1. identity map 0x0 to 0x0
2. Since there is no page table present for 0x0, we malloc one
3. Inside of malloc, we get address 0x40000000 and try to map it
4. Since there is no page table present for 0x40000000, we malloc a page table.
5. Malloc gives us 0x40001000 and tries to map it
6. Since there is no page table present for 0x40001000, we malloc a page table.
7. Steps 3-6 continue forever but the malloc address keeps increasing
My next change was so have map_page mark a page table as present before allocating it and unmarking it if malloc fails. So we have this:
Pseudocode:
But then I encountered another problem.
1. We map 0x0 to kernel pd
2. No PT for 0x0, malloc
3. malloc gets and maps 0x40000000 to kernel pd
4. No PT for 0x40000000, we set the new pt pointer to whatever malloc returns
5. malloc gets and maps 0x40001000 to kernel pd
6. This succeeds and malloc returns 0x40001000
7. map_page initializes the memory at 0x40001000 to zero, unmapping 0x40001000 from the kernel PD
8. Trying to allocate more memory will cause kmalloc to map another block, so it will try to write to the page table at 0x40001000, which was initialized to zero, so we get a non-present page fault.
Another thought was to have kmalloc initialize all returned memory to zero and remove the initialization to zero from map_page
(map_page is the same but we don't initialize the new page table to zero)
and though this seems like it should fix all of my issues, it is giving me the exact same page fault at 0x40001000 as last time. It seems 0x40001000 is still somehow being overwritten, though it seemingly shouldn't be possible this way.
My final thought was to immediately allocate all needed page tables at the start. That way map_page never needs to call kmalloc at all. However, when I eventually try to implement multitasking this will make every process 4MB in size immediately as I will need to allocate their entire page tables as well. I want to avoid this solution if possible.
Hopefully I've explained this well enough, any help will be greatly appreciated.
There are two functions involved, one is my memory manager. I'm using a buddy allocator with a minimum block size of 0x1000. I've pseudo coded them so you can get an idea of how they work. My heap starts at 0x40000000 and ends at 0xC0000000
Pseudocode:
Code: Select all
kmalloc(size)
get block of size 0x1000 from the heap
return address of block
Pseudocode:
Code: Select all
map_page(vaddr, paddr, page_directory)
if (free_frame_at(paddr)
{
if (page_directory[page_directory_entry_of(vaddr)] not present)
{
addr = kmalloc(size of page table)
initialize page table to 0 and set to present
page_directory[page_directory_entry_of(vaddr)].address = addr
}
page_table = page_directory[page_directory_entry_of(vaddr)].address
page = page_table[page_table_entry_of(vaddr)]
page.flags = present
page.addr = paddr
mark_frame_as_used(paddr)
}
My first thought was to do this:
Code: Select all
kmalloc(size)
addr = get block of size 0x1000 from the heap
map_page(addr, addr, kernel_page_directory)
return address of block
1. identity map 0x0 to 0x0
2. Since there is no page table present for 0x0, we malloc one
3. Inside of malloc, we get address 0x40000000 and try to map it
4. Since there is no page table present for 0x40000000, we malloc a page table.
5. Malloc gives us 0x40001000 and tries to map it
6. Since there is no page table present for 0x40001000, we malloc a page table.
7. Steps 3-6 continue forever but the malloc address keeps increasing
My next change was so have map_page mark a page table as present before allocating it and unmarking it if malloc fails. So we have this:
Pseudocode:
Code: Select all
map_page(vaddr, paddr, page_directory)
if (free_frame_at(paddr)
{
if (page_directory[page_directory_entry_of(vaddr)] not present)
{
set page table to present
addr = kmalloc(size of page table)
if (kmalloc didnt work)
unmark page table and return
initialize page table to 0
page_directory[page_directory_entry_of(vaddr)].address = addr
}
page_table = page_directory[page_directory_entry_of(vaddr)].address
page = page_table[page_table_entry_of(vaddr)]
page.flags = present
page.addr = paddr
mark_frame_as_used(paddr)
}
1. We map 0x0 to kernel pd
2. No PT for 0x0, malloc
3. malloc gets and maps 0x40000000 to kernel pd
4. No PT for 0x40000000, we set the new pt pointer to whatever malloc returns
5. malloc gets and maps 0x40001000 to kernel pd
6. This succeeds and malloc returns 0x40001000
7. map_page initializes the memory at 0x40001000 to zero, unmapping 0x40001000 from the kernel PD
8. Trying to allocate more memory will cause kmalloc to map another block, so it will try to write to the page table at 0x40001000, which was initialized to zero, so we get a non-present page fault.
Another thought was to have kmalloc initialize all returned memory to zero and remove the initialization to zero from map_page
Code: Select all
kmalloc(size)
addr = get block of size 0x1000 from the heap
memset(addr, addr+0x1000, 0)
map_page(addr, addr, kernel_page_directory)
return address of block
and though this seems like it should fix all of my issues, it is giving me the exact same page fault at 0x40001000 as last time. It seems 0x40001000 is still somehow being overwritten, though it seemingly shouldn't be possible this way.
My final thought was to immediately allocate all needed page tables at the start. That way map_page never needs to call kmalloc at all. However, when I eventually try to implement multitasking this will make every process 4MB in size immediately as I will need to allocate their entire page tables as well. I want to avoid this solution if possible.
Hopefully I've explained this well enough, any help will be greatly appreciated.