Page 1 of 1

My own 64-bit Paging is not working

Posted: Mon Jul 15, 2024 12:12 am
by osdev199
Hello, I am trying to enable paging in my boot loader code so that I can print a single red-colored pixel at the top-left corner of my screen. I am new to paging and I'm doing this small experiment to get started myself with paging before writing a memory manager for my kernel. I'm enabling page after exiting the UEFI boot services by calling the `setup_and_enable_paging` function. The red pixel is being printed if I do not enable my own paging but on enabling it, the pixel does not get printed.

The frame buffer base address is 0x4000000000 and a 32-bit write to the base address includes the addresses 0x4000000000-0x4000000003.

The linear addresses give the following information:

Code: Select all

pml4e = 0
pdpte = 256
pde = 0
pte = 0
offset = 0 to 3
`sys_var_ptr` in the code provided below is a pointer variable holding the base address of 2MiB memory region for system variables. It is allocated using the boot services 'AllocatePool' function and the memory type is `EfiRuntimeServicesData`. The region is also set to 0.


Below is the code for setting up the paging structures and enabling paging:

paging.h

Code: Select all

#include <stdint.h>

// using M = 50

typedef struct _pml4_table {
	uint64_t p :1;
	uint64_t rw :1;
	uint64_t us :1;
	uint64_t pwt :1;
	uint64_t pcd :1;
	uint64_t a :1;
	uint64_t avl_1 :1;
	uint64_t reserved_zero_1 :1;
	uint64_t avl_2:4;
	uint64_t pdpt_phy_addr :38;
	uint64_t reserved_zero_2 :2;
	uint64_t avl_3 :11;
	uint64_t xd :1;
}__attribute__((packed)) pml4_table_t;

typedef struct _pae_page_directory_pointer_table{
	uint64_t p :1;
	uint64_t rw :1;
	uint64_t us :1;
	uint64_t pwt :1;
	uint64_t pcd :1;
	uint64_t a :1;
	uint64_t avl_1 :1;
	uint64_t ps_zero :1;
	uint64_t avl_2 :4;
	uint64_t pd_phy_addr :38;
	uint64_t reserved_zero_1 :2;
	uint64_t avl_3 :11;
	uint64_t xd :1;
}__attribute__((packed)) pae_page_directory_pointer_table_t;

typedef struct _pae_page_directory_table {
	uint64_t p :1;
	uint64_t rw :1;
	uint64_t us :1;
	uint64_t pwt :1;
	uint64_t pcd :1;
	uint64_t a :1;
	uint64_t avl_1 :1;
	uint64_t ps_zero :1;
	uint64_t avl_2 :4;
	uint64_t pt_phy_addr :38;
	uint64_t reserved_zero_1 :2;
	uint64_t avl_3 :11;
	uint64_t xd :1;
}__attribute__((packed)) pae_page_directory_table_t;

typedef struct _pae_page_table {
	uint64_t p :1;
	uint64_t rw :1;
	uint64_t us :1;
	uint64_t pwt :1;
	uint64_t pcd :1;
	uint64_t a :1;
	uint64_t d :1;
	uint64_t pat :1;
	uint64_t g :1;
	uint64_t avl_1 :3;
	uint64_t page_4k_phy_addr :38;
	uint64_t reserved_zero_1 :2;
	uint64_t avl_2 :7;
	uint64_t pk :4;
	uint64_t xd :1;
}__attribute__((packed)) pae_page_table_t;

boot.c

Code: Select all

void setup_and_enable_paging(void)
{
	volatile char *addr_1 = (void *) find_first_4096_byte_aligned_address(sys_var_ptr);

	volatile pml4_table_t *pml4e = (pml4_table_t *)addr_1;

	volatile pae_page_directory_pointer_table_t *pdpt_base = (pae_page_directory_pointer_table_t*) ((char *) pml4e + 0x1000);

	volatile pae_page_directory_pointer_table_t *pdpte = (pae_page_directory_pointer_table_t*) ((char *) pdpt_base + (256 * 8));

	volatile pae_page_directory_table_t *pde = (pae_page_directory_table_t *) ((char *) pdpt_base + 0x1000);
	
	volatile pae_page_table_t *pte = (pae_page_table_t *) ((char *) pde + 0x1000);

	unsigned long addr_2;
	uint32_t cpu_edx, cpu_eax;

	pml4e->p = 1;
	pml4e->rw = 1;
	pml4e->us = 1;
	addr_2 = (unsigned long) pdpt_base;
	pml4e->pdpt_phy_addr = (addr_2 >> 12) & 0x3FFFFFFFFF;
	
	pdpte->p = 1;
	pdpte->rw = 1;
	pdpte->us = 1;
	unsigned long addr_3;
	addr_3 = (unsigned long) pde;
	pdpte->pd_phy_addr = (addr_3 >> 12) & 0x3FFFFFFFFF;

	pde->p = 1;
	pde->rw = 1;
	pde->us = 1;
	// unsigned long addr_4 = (unsigned long) pt_base;
	unsigned long addr_4 = (unsigned long) pte;
	pde->pt_phy_addr = (addr_4 >> 12) & 0x3FFFFFFFFF;
	

	pte->p = 1;
	pte->rw = 1;
	pte->us = 1;
	unsigned long addr_5 = (unsigned long) 0x4000000000;
	pte->page_4k_phy_addr = (addr_5 >> 12) & 0x3FFFFFFFFF;


	__asm__ __volatile__("cli");

	/* enable paging */
	
	__asm__("mov r8, %0\n\t"
		"call enable_paging"
		::"m" (pml4e):"r8");
	
	__asm__ __volatile__("sti");
}

volatile uint64_t *find_first_4096_byte_aligned_address(char *sys_var_ptr)
{
	volatile char *addr = sys_var_ptr;

	while((uint64_t) addr % 4096 != 0) {
		addr++;
	}

	return (volatile uint64_t *) addr;
}

paging.asm

Code: Select all

; written in FASM assembly

format elf64

public enable_paging

section '.text' executable

; the first parameter pml4e is in r8 register
enable_paging:
	; firstly, deactivate paging while in long mode
	mov rbx, cr0
	and ebx, 01111111111111111111111111111111b	; clear the PG bit (bit #31)
	mov cr0, rbx

	; flush the TLB
	mov rcx, 0x0
	mov cr3, rcx
	
	; enable PAE
	mov rdx, cr4
	or  edx, 100000b
	mov cr4, rdx

	; Set LME (long mode enable)
	mov ecx, 0xC0000080
	rdmsr
	or  eax, 100000000b
	wrmsr


	; point the CR3 register to the base address of pml4 table
	mov cr3, r8
	
	; enable paging
	or ebx, 10000000000000000000000000000000b
	mov cr0, rbx
        ret

tty_io.c

Code: Select all

void put_red_pixel(void)
{
	volatile uint32_t* addr = frame_buffer.frame_buffer_base;

	*addr = 0xff0000;
}

Edits:

1. Updated the paging structures in `paging.h` according to osdev wiki's. Earlier I was using the structures from the Intel manual but I think I will use osdev wiki instructions for easiness.

Re: My own 64-bit Paging is not working

Posted: Mon Jul 15, 2024 9:46 am
by Octocontrabass
osdev199 wrote: Mon Jul 15, 2024 12:12 amIt is allocated using the boot services 'AllocatePool' function and the memory type is `EfiRuntimeServicesData`.
Is there any particular reason why you chose EfiRuntimeServicesData instead of EfiLoaderData?
osdev199 wrote: Mon Jul 15, 2024 12:12 amBelow is the code for setting up the paging structures and enabling paging:
Paging is already enabled.
osdev199 wrote: Mon Jul 15, 2024 12:12 am

Code: Select all

	; firstly, deactivate paging while in long mode
You can't disable paging in 64-bit mode. It looks like you copied this code from an example that shows how to switch from protected mode to long mode, but the CPU is already in long mode. You only need to set CR3 to switch to your own page tables. You could delete this entire function and rewrite your inline asm like this:

Code: Select all

__asm__("mov %0, %%cr3"::"r" (pml4e));
Or, if you're using "-masm=intel" to use Intel syntax for inline asm, like this:

Code: Select all

__asm__("mov cr3, %0"::"r" (pml4e));
(It's possible to write inline assembly that works with both AT&T and Intel syntax, but I'm assuming you don't need that.)

You probably also don't want to enable interrupts until after your kernel has set up its own IDT.

Re: My own 64-bit Paging is not working

Posted: Tue Jul 16, 2024 4:39 am
by osdev199
Octocontrabass wrote: Mon Jul 15, 2024 9:46 am Is there any particular reason why you chose EfiRuntimeServicesData instead of EfiLoaderData?
No. I am just using it as my XHCI USB driver also uses the `sys_var_ptr` variable.
Octocontrabass wrote: Mon Jul 15, 2024 9:46 am Or, if you're using "-masm=intel" to use Intel syntax for inline asm, like this:

Code: Select all

__asm__("mov cr3, %0"::"r" (pml4e));

You probably also don't want to enable interrupts until after your kernel has set up its own IDT.
I'm enabling my own paging in the code after setting up my own IDT.

I tried your suggestion of using the inline assembly and I also used EfiLoaderData in memory allocation code but it didn't work.

Re: My own 64-bit Paging is not working

Posted: Tue Jul 16, 2024 8:49 pm
by Octocontrabass
That means it's time for more debugging. Which virtual machine are you using?

Re: My own 64-bit Paging is not working

Posted: Tue Jul 16, 2024 8:59 pm
by osdev199
It's a real machine. HP Pavilion 15 Laptop.

Re: My own 64-bit Paging is not working

Posted: Tue Jul 16, 2024 9:23 pm
by Octocontrabass
Does your code work in a virtual machine?

Re: My own 64-bit Paging is not working

Posted: Tue Jul 16, 2024 10:49 pm
by osdev199
On qemu, my code doesn't work. Even without enabling my own paging, it doesn't print the red pixel. But the later works on my real machine.

Re: My own 64-bit Paging is not working

Posted: Tue Jul 16, 2024 11:00 pm
by Octocontrabass
A single pixel is easy to miss. Try printing more than one pixel.

If that's not working, you can debug your code by attaching GDB to QEMU. This might also work for debugging your paging code, but I've found that adding "-d int" to the QEMU command line and examining the resulting log tends to be more useful for figuring out that kind of problem.

Re: My own 64-bit Paging is not working

Posted: Thu Jul 18, 2024 6:30 pm
by StudlyCaps
osdev199 wrote: Tue Jul 16, 2024 10:49 pm On qemu, my code doesn't work. Even without enabling my own paging, it doesn't print the red pixel. But the later works on my real machine.
I would say, as a general rule, you should fix issues on the emulator first as it's easier to debug than real hardware and the bug that stops it working on the emulator might also be causing the issue you see on real hardware, the emulator is just reacting to it sooner.