How can I resolve 4 Level Paging issue in x86 architecture based 64 bit OS?
Posted: Sat Jan 18, 2025 7:40 am
Hello!
I am trying to develop a x86 architecture based 64 bit Operating system. The limine bootloader is using to load kernel.
Limine initially enable 4 level Paging. So I have defined following structures to use previously enabled paging system.
Following macros are using to get pml4, pdpt, pd, pt indexes from virtual address,
I have written following below get_page function to get page_t pointer from virtual address ,
I am also using 64 bit bitmap to store information about used or unused 4KB sized Physical Frame .Those Physical frame address allocated to page_t structure so I have written below alloc_frame which set page.present bit and store free physical frame address into corresponding page.frame. So alloc_frame set present bit but also I can set the page.present bit by manually by using page1->present = 1, But unfortunetly it is not set in below test_paging function
But it is showing page is not present as it's present bit never bin set by alloc_frame even trying to manually.
The mysterious thing when I am using 1024 MB Memory the above test run successfully.
The full code is in https://github.com/baponkar/KeblaOS
I am trying to develop a x86 architecture based 64 bit Operating system. The limine bootloader is using to load kernel.
Limine initially enable 4 level Paging. So I have defined following structures to use previously enabled paging system.
Code: Select all
typedef struct dir_entry { // 64 bit
uint64_t present : 1; // always 1
uint64_t rw : 1; // 0 for read-only, 1 for read-write
uint64_t user : 1; // 0 for kernel, 1 for user
uint64_t pwt : 1;
uint64_t pcd : 1;
uint64_t accessed : 1;
uint64_t reserved_1 : 3; // all zeros
uint64_t available_1 : 3; // zero
uint64_t base_addr : 40; // Table base address
// uint64_t reserved_2 : 12; // Reserved must be zero
uint64_t available_2 : 11; // zero
uint64_t xd : 1;
} __attribute__((packed)) dir_entry_t;
typedef struct page { // 64 bit
uint64_t present : 1;
uint64_t rw : 1;
uint64_t user : 1;
uint64_t pwt : 1;
uint64_t pcd : 1;
uint64_t accessed : 1;
uint64_t dirty : 1;
uint64_t pat : 1;
uint64_t global : 1;
uint64_t ignored : 3;
uint64_t frame : 40;
uint64_t reserved : 11;
uint64_t nx : 1;
} __attribute__((packed)) page_t;
typedef struct pt { // page table structure is containing 512 page entries
page_t pages[512];
} __attribute__((aligned(PAGE_SIZE))) pt_t;
typedef struct pd { // page directory structure is containg 512 page table entries
dir_entry_t entry_t[512]; // Each entry have Physical addresses of PTs
} __attribute__((aligned(PAGE_SIZE))) pd_t;
typedef struct pdpt { // pdpt structure is containing 512 page directory entries
dir_entry_t entry_t[512]; // Each entry have Physical addresses of PDs
} __attribute__((aligned(PAGE_SIZE))) pdpt_t;
typedef struct pml4 { // pml4 structure is containing 512 pdpt directory entries
dir_entry_t entry_t[512]; // Each entry have Physical addresses of PDPTs
} __attribute__((aligned(PAGE_SIZE))) pml4_t;
Code: Select all
#define PML4_INDEX(va) (((va) >> 39) & 0x1FF) // Bits 39-47
#define PDPT_INDEX(va) (((va) >> 30) & 0x1FF) // Bits 30-38
#define PD_INDEX(va) (((va) >> 21) & 0x1FF) // Bits 21-29
#define PT_INDEX(va) (((va) >> 12) & 0x1FF) // Bits 12-20
#define PAGE_OFFSET(va) ((va) & 0xFFF) // Bits 0-11
Code: Select all
page_t* get_page(uint64_t va, int make, pml4_t* pml4) {
uint64_t pml4_index = PML4_INDEX(va);
uint64_t pdpt_index = PDPT_INDEX(va);
uint64_t pd_index = PD_INDEX(va);
uint64_t pt_index = PT_INDEX(va);
uint64_t page_offset = PAGE_OFFSET(va);
// Get the PML4 entry from pml4_index which is found from va
dir_entry_t* pml4_entry = &pml4->entry_t[pml4_index];
// the below if block will create pdpt if not present and make = 1
if (!pml4_entry->present) {
if (!make) {
return NULL; // Page table does not exist and we are not allowed to create it
}
// Allocate a new PDPT
pdpt_t* pdpt = alloc_pdpt(); // Creating a new pdpt
if (!pdpt) {
return NULL; // Allocation failed
}
// Set up the PML4 entry
pml4_entry->present = 1;
pml4_entry->rw = 1; // Read/write
pml4_entry->user = 0; // Kernel mode
pml4_entry->base_addr = (uint64_t) pdpt >> 12; // Base address of PDPT
}
// Get the PDPT entry
pdpt_t* pdpt = (pdpt_t*)(pml4_entry->base_addr << 12); // Converting base address into pdpt pointer
dir_entry_t* pdpt_entry = &pdpt->entry_t[pdpt_index]; //
// the below if block will create pd if not present and make = 1
if (!pdpt_entry->present) {
if (!make) {
return NULL; // Page directory does not exist and we are not allowed to create it
}
// Allocate a new PD
pd_t* pd = alloc_pd();
if (!pd) {
return NULL; // Allocation failed
}
// Set up the PDPT entry
pdpt_entry->present = 1;
pdpt_entry->rw = 1; // Read/write
pdpt_entry->user = 0; // Kernel mode
pdpt_entry->base_addr = (uint64_t)pd >> 12; // Base address of PD
}
// Get the PD entry
pd_t* pd = (pd_t*)(pdpt_entry->base_addr << 12);
dir_entry_t* pd_entry = &pd->entry_t[pd_index];
// the below if block will create pt if not present and make = 1
if (!pd_entry->present) {
if (!make) {
return NULL; // Page table does not exist and we are not allowed to create it
}
// Allocate a new PT
pt_t* pt = alloc_pt();
if (!pt) {
return NULL; // Allocation failed
}
// Set up the PD entry
pd_entry->present = 1;
pd_entry->rw = 1; // Read/write
pd_entry->user = 0; // Kernel mode
pd_entry->base_addr = (uint64_t)pt >> 12; // Base address of PT
}
// Get the PT entry from pd_entry->base_addr
pt_t* pt = (pt_t*)(pd_entry->base_addr << 12);
page_t *page = (page_t *) &pt->pages[pt_index];
// return page pointer
return page;
}
Code: Select all
void test_paging() {
print("\nTest of Paging\n");
// test following address
uint64_t va1 = PAGE_ALIGN(KMEM_LOW_BASE) + PHYSICAL_TO_VIRTUAL_OFFSET + 40*PAGE_SIZE;
// uint64_t va2 = PAGE_ALIGN(KMEM_LOW_BASE) + PHYSICAL_TO_VIRTUAL_OFFSET + PAGE_SIZE;
// Virtual Pointer on based higher half
uint64_t *v_ptr1 = (uint64_t *) va1;
// uint64_t *v_ptr2 = (uint64_t *) va2;
// Get Page pointer from above virtual pointer address
page_t *page1 = (page_t *) get_page((uint64_t)v_ptr1, 1, current_pml4);
// page_t *page2 = get_page((uint64_t)v_ptr2, 1, current_pml4);
assert(((uint64_t)page1 % sizeof(page_t)) == 0);
page1->present = 1;
page1->rw = 1;
debug_page(page1);
// debug_page(page2);
// allocate a physical frame address in above page with kernel level and writable
alloc_frame(page1, 1, 1);
// alloc_frame(page2, 1, 1);
debug_page(page1);
// debug_page(page2);
// Store a value at the virtual pointer
*v_ptr1 = 0x567;
// *v_ptr2 = 0xABC50;
print("Content of v_ptr1: ");
print_hex(*v_ptr1);
print("\n");
// print("Content of v_ptr2: ");
// print_hex(*v_ptr2);
// print("\n");
print("Finish Paging Test\n");
}
The mysterious thing when I am using 1024 MB Memory the above test run successfully.
The full code is in https://github.com/baponkar/KeblaOS