[SOLVED] Problems with the Memory Manager
Posted: Thu Jun 20, 2013 12:31 pm
Recently, I've decided to rewrite my memory manager. The first thing I did was add support for higher half paging, which is set up at the beginning of the kernel. The kernel is linked to 2 GB virtual, but GRUB loads the kernel to 1 MB physical. The kernel then creates page tables to map the kernel to 2 GB. After this is done, it jumps to the newly mapped virtual start of the kernel. Once the virtual memory manager is run in the kernel, the lower half mapping is discarded. So far so good.
Next, I also decided to rewrite the kernel heap code that I was using (which was mostly from JamesM's tutorial). However, doing this also revealed several problems in my entire memory manager. It contained several circular dependencies, such as the PMM and VMM calling the heap to allocate their structures (once again, a result of basing my code on JamesM's tutorial). So I decided to rewrite those as well, first of all, I wrote code to allocate the bitmap used by the PMM not using the heap, but by reserving free pages from the BIOS memory. Once again, this worked fine.
After the PMM was rewritten, I started rewriting the VMM. In order to access my page tables, I used a recursive page directory when higher half was first enabled:
The page directory was mapped in as the last page table, and the second-to-last page table was reserved for when another address space had to be modified. I modified the VMM to use recursive page directory mapping, but when it attempts to create the new page directory and map it as the recursive page directory, the recursive page directory does not end up mapped as it should (only relevant functions are posted):
The fault occurs in get_page(), which gets called when map_page() is called for the first time in init_vmm(). Notice how the fault is in the first else instruction in get_page(), which logically shouldn't be called if the page directory was mapped correctly.
I've done some testing, and the page directory is mapped correctly until flush_tlb() gets called in init_vmm(), meaning that when I set the value of the last page table to the value of the second-to-last page table, that messed something up. Both recursive page directories are no longer mapped.
Also, here's the information that my page fault handler provides:
Next, I also decided to rewrite the kernel heap code that I was using (which was mostly from JamesM's tutorial). However, doing this also revealed several problems in my entire memory manager. It contained several circular dependencies, such as the PMM and VMM calling the heap to allocate their structures (once again, a result of basing my code on JamesM's tutorial). So I decided to rewrite those as well, first of all, I wrote code to allocate the bitmap used by the PMM not using the heap, but by reserving free pages from the BIOS memory. Once again, this worked fine.
After the PMM was rewritten, I started rewriting the VMM. In order to access my page tables, I used a recursive page directory when higher half was first enabled:
Code: Select all
/* Add the page tables into the page directory */
pd[0] = (unsigned int) pt_lower | PAGE_KERNEL;
pd[512] = (unsigned int) pt_higher | PAGE_KERNEL | PAGE_FLAG_GLOBAL;
pd[575] = (unsigned int) pt_bitmap | PAGE_KERNEL | PAGE_FLAG_GLOBAL;
pd[1022] = 0;
pd[1023] = (unsigned int) pd | PAGE_KERNEL;
Code: Select all
/* Get a page */
page_t *get_page(unsigned int dir, unsigned int virtual_address, bool make, bool present, bool rw, bool user, bool global)
{
/* Construct the page flags */
unsigned int flags = 0;
if (present)
{
flags |= PAGE_FLAG_PRESENT;
}
if (rw)
{
flags |= PAGE_FLAG_RW;
}
if (user)
{
flags |= PAGE_FLAG_USER;
}
if (global)
{
flags |= PAGE_FLAG_GLOBAL;
}
/* Find out which page the virtual address in in */
unsigned int page = virtual_address >> 12;
/* Now use that page index to find out the index of the page table */
unsigned int table_index = page >> 10;
/* Get the address of the recursive page directory and recursive page table */
page_directory_t *directory = &((page_directory_t*) PAGE_STRUCTURES_START)[1023];
page_table_t *table = &((page_table_t*) PAGE_STRUCTURES_START)[table_index];
/* The page directory is mapped as the recursive page directory */
if (PAGE_FRAME(directory->tables[1023]) == dir)
{
}
/* The page directory is mapped as the secondary recursive page directory */
else if (PAGE_FRAME(directory->tables[1022]) == dir)
{
/* Choose the secondary recursive page directory */
directory = &((page_directory_t*) PAGE_STRUCTURES_START)[1022];
}
/* The page directory is not mapped in */
else
{
/* Map it in as the secondary recursive page directory */
directory->tables[1022] = dir | PAGE_KERNEL; // Page fault here
/* Flush the entire TLB */
flush_tlb();
/* Choose the secondary recursive page directory */
directory = &((page_directory_t*) PAGE_STRUCTURES_START)[1022];
}
/* If the page table already exists, return the page */
if (directory->tables[table_index])
{
return &table->pages[page % 1024];
}
/* If the table does not already exist and we want to make the page, create and return it */
else if (make)
{
/* Create a new page table */
directory->tables[table_index] = pmm_alloc_page() | PAGE_KERNEL;
flush_tlb();
memset(table, 0, 0x1000);
/* Return the page */
return &table->pages[page % 1024];
}
/* Otherwise, return 0 */
else
{
return 0;
}
}
/* Map a virtual address to a physical address */
void map_page(unsigned int dir, unsigned int virtual_address, unsigned int physical_address, bool present, bool rw, bool user, bool global)
{
/* Construct the page flags */
unsigned int flags = 0;
if (present)
{
flags |= PAGE_FLAG_PRESENT;
}
if (rw)
{
flags |= PAGE_FLAG_RW;
}
if (user)
{
flags |= PAGE_FLAG_USER;
}
if (global)
{
flags |= PAGE_FLAG_GLOBAL;
}
/* Return the page that corresponds to the virtual address, creating it if it doesn't already exist */
page_t *page = get_page(dir, virtual_address, true, present, rw, user, global);
/* Map the page in the table to the physical address */
*((unsigned int*) page) = physical_address | flags; // Page fault here
/* Invalidate the TLB entry */
asm volatile ("invlpg (%0)" :: "a" (virtual_address));
}
/* Create a new blank page directory */
unsigned int create_page_directory()
{
/* Allocate a page directory */
unsigned int dir = pmm_alloc_page();
/* Get the address of the recursive page directory */
page_directory_t *directory = &((page_directory_t*) PAGE_STRUCTURES_START)[1023];
/* Map the page directory in as the secondary recursive page directory */
directory->tables[1022] = dir | PAGE_KERNEL;
/* Flush the entire TLB */
flush_tlb();
/* Choose the secondary recursive page directory */
directory = &((page_directory_t*) PAGE_STRUCTURES_START)[1022];
/* Clear the page directory */
memset(directory, 0, sizeof(page_directory_t));
/* Return the physical address of the page directory */
return dir;
}
/* Initialize paging */
void init_vmm()
{
/* Set the address of the current directory */
asm volatile ("mov %%cr3, %0" : "=r"(current_directory));
/* Create the kernel directory */
kernel_directory = create_page_directory();
page_directory_t *directory = &((page_directory_t*) PAGE_STRUCTURES_START)[1023];
directory->tables[1023] = directory->tables[1022];
/* Flush the entire TLB */
flush_tlb();
unsigned int i;
/* Identity map the first 1 MB of the address space, so that the VGA framebuffer and VM86 tasks will work */
for (i = 0; i < 0x100000; i += 0x1000)
{
map_page(kernel_directory, i, i, true, true, false, true);
}
/* Map our kernel into the kernel directory */
for (i = 0; i < KERNEL_PHYSICAL_SIZE; i += 0x1000)
{
map_page(kernel_directory, KERNEL_VIRTUAL_START + i, KERNEL_PHYSICAL_START + i, true, true, false, true);
}
/* Switch to the the kernel directory */
switch_page_directory(kernel_directory);
/* Now, enable paging! */
unsigned int cr0;
asm volatile("mov %%cr0, %0" : "=r"(cr0));
cr0 |= 0x80000000;
asm volatile("mov %0, %%cr0" :: "r"(cr0));
}
I've done some testing, and the page directory is mapped correctly until flush_tlb() gets called in init_vmm(), meaning that when I set the value of the last page table to the value of the second-to-last page table, that messed something up. Both recursive page directories are no longer mapped.
Also, here's the information that my page fault handler provides:
Code: Select all
Unhandled Page Fault Exception at 0xfffffff8, error code 0x00000002
Present: no, Access: write, Mode: supervisor
Register Dump
EAX: 00121003 EBX: 00000000 ECX: 00000001 EDX: 00121000
ESI: fffff000 EDI: ffc00000 ESP: 00000000 EBP: 00000000
CS: 00000008 DS: 00000010 ES: 00000010
FS: 00000010 GS: 00000010 SS: 00000000
EIP: 80002936 EFLAGS: 00010006
CR0: e0000011 CR2: fffffff8 CR3: 0011a000 CR4: 00000080