- takes a physical address, a virtual address, a memory block size, and a set of flags,
- computes which page directory and page table entries corresponding to the beginning and end of the virtual memory block,
- populates these corresponding page directory entries and page table entries with the physical addresses (auto-incremented by 4KiB for each table entry) and the set of flags.
Code: Select all
#define PD_ENTRY_COUNT 0x0400
#define PT_ENTRY_COUNT 0x0400
#define PT_ENTRY_TOTAL_COUNT (PD_ENTRY_COUNT * PT_ENTRY_COUNT)
#define PAGE_SPAN 0x1000
#define PD_ENTRY_SPAN (PT_ENTRY_COUNT * PAGE_SPAN)
#define PD_SIZE (PD_ENTRY_COUNT * sizeof(union Page_Directory_Entry))
#define PT_SIZE (PT_ENTRY_TOTAL_COUNT * sizeof(union Page_Table_Entry))
Code: Select all
/* set a block of page directory and page table entries matching a block of memory */
void set_page_block(uint32_t phys_address,
uint32_t virt_address,
uint32_t block_size,
bool page_size, bool rw,
bool user, bool write_thru,
bool no_caching)
{
// determine the page directory entry and page table entry
// corresponding to the virtual address
uint32_t pd_start = virt_address / PD_ENTRY_SPAN;
uint32_t directory_offset = virt_address - (pd_start * PD_ENTRY_SPAN);
uint32_t pe_start = directory_offset / PAGE_SPAN;
// determine the page directory entry and page table entry
// corresponding to the end of the block of memory
uint32_t block_end = virt_address + block_size - 1;
uint32_t pd_end = block_end / PD_ENTRY_SPAN;
uint32_t trailing_block_size = block_size % PD_ENTRY_SPAN;
uint32_t pe_end = pe_start + (trailing_block_size / PAGE_SPAN) - 1;
size_t addr = phys_address;
for (uint32_t pd_e = pd_start; pd_e < pd_end; pd_e++)
{
set_page_directory_entry(pd_e,
addr,
page_size, rw,
user, write_thru,
no_caching);
for (uint32_t pt_e = pe_start; pt_e < PT_ENTRY_COUNT; pt_e++, addr += PAGE_SPAN)
{
set_page_table_entry(pd_e,
pt_e,
addr,
page_size, rw,
user, write_thru,
no_caching);
}
}
}
I believe I have figured out how to compute the start and end indices correctly, as well as the index for the monoblock page table. At this time I have instrumented the code to print out the results of these calculations, and they appear to be correct for the test values I am using, though I probably need to test various edge cases to be sure. I can use to have some eyes on this code to be sure, if anyone is willing to take a look.
I also have a driver function which calls this function with arguments which are intended to match those in my bootloader's paging code. While I am not pleased to have to manually repeat these values, the bigger issue is that I am having some trouble getting them to line up.
Code: Select all
void reset_default_paging(uint32_t map_size, struct memory_map_entry mt[KDATA_MAX_MEMTABLE_SIZE])
{
// first, set all of the page directory entries to a default state
memset(page_directory, 0, PD_SIZE);
// do the same for all of the page table entries
memset(page_tables, 0, PT_SIZE);
// next, identity map the first 1MiB
set_page_block(0, 0, 0x00100000, false, true, false, false, false);
// map in the kernel region
set_page_block(0x00100000, KERNEL_BASE, 0x00100000, false, true, false, false, false);
// map in the various tables
set_page_block(0x00400000, (size_t) tables_base, 0x01000000, false, true, false, false, false);
// map in the stack
set_page_block(0x01400000, (size_t) &kernel_stack_base, 0x4000, false, true, false, false, false);
// reset the paging address control register
// to point to the new page directory
/* __asm__ __volatile__ (
" mov %0, %%cr3"
:
: "a" (page_directory)
);
// confirm that the paging bit is set
// in the main control register
__asm__ __volatile__ (
" mov %cr0, %eax;"
" or $0x80000000, %eax;"
" mov %eax, %cr0;"
); */
}
Code: Select all
%macro populate_pte 4
; set up the page table
mov ebx, dword %1 ; location to save the table entry
mov ecx, dword %2 ; number of entries to fill
mov eax, dword %3 ; the physical address to map
memset32 0, 0x1000, ebx ; clear the table entries
%%pt_fill:
mov edx, eax
and edx, PTE_Page_Index_Mask ; clear the flags before setting them
or edx, %4 ; flags
mov [ebx], dword edx
add eax, 0x1000
add ebx, 4
loop %%pt_fill
%endmacro
%macro populate_pde 3
mov ebx, page_directory
mov eax, %1 ; directory entry address
add ebx, %2 * 4 ; directory entry index
or eax, %3 ; flags
mov [ebx], eax
%endmacro
Code: Select all
bits 32
page_directory equ 0x0040000
page_table_0x0000 equ page_directory + 0x1000
page_table_0x0300 equ page_table_0x0000 + 0x1000
page_table_0x0301 equ page_table_0x0300 + 0x1000
page_table_0x0302 equ page_table_0x0301 + 0x1000
page_table_0x0303 equ page_table_0x0302 + 0x1000
page_table_0x0304 equ page_table_0x0303 + 0x1000
page_table_0x0305 equ page_table_0x0304 + 0x1000
init_page_directory:
mov ebx, dword page_directory
memset32 0, 0x1000, ebx ; clear the page dir table
populate_pte page_table_0x0000, 0x0100, 0x00000000, PTE_Present
populate_pte page_table_0x0300, 0x1000, 0x00100000, PTE_Present
populate_pte page_table_0x0301, 0x1000, 0x00400000, PTE_Present
populate_pte page_table_0x0302, 0x1000, 0x00800000, PTE_Present
populate_pte page_table_0x0303, 0x1000, 0x00C00000, PTE_Present
populate_pte page_table_0x0304, 0x1000, 0x01000000, PTE_Present
populate_pte page_table_0x0305, 0x1000, 0x01400000, PTE_Present
.setup_directory:
populate_pde page_table_0x0000, 0x0000, PDE_Present
populate_pde page_table_0x0300, 0x0300, PDE_Present
populate_pde page_table_0x0301, 0x0301, PDE_Present
populate_pde page_table_0x0302, 0x0302, PDE_Present
populate_pde page_table_0x0303, 0x0303, PDE_Present
populate_pde page_table_0x0304, 0x0304, PDE_Present
populate_pde page_table_0x0305, 0x0305, PDE_Present
; set the page directory
mov eax, page_directory
mov cr3, eax
ret