general protection fault when listing pages in 64 long mode
Posted: Sat Jun 08, 2024 11:07 am
hi. I'm getting a general protection fault when trying to list a memory layout retrieved from the multiboot structure after enabling gdt and paging and jumping to 64 bit long protected mode. i don't know, if multiboot structure is properly, it should work. I created a custom kernel which jumps to 32 bit mode after enabling paging and gdt64, then i am enabling interrupts and after i tries according to this article ([wiki]Detecting_Memory_(x86)[/wiki]) list a memory map to proceed to writing a memory manager. The problem is, i am constantly getting general protection faults from interrupt handler when trying to simply list the pages.
Here is my code:
main.asm (entry point is here)
here i have main64.asm which is basically a loader for the 64 bit kernel and just prepares the data segment registers
i also have a header.asm which is just a multiboot header structure:
and here is the kernel itself (main.cpp)
as you can see the memory code was 1:1 taken from the topic mentioned above.
Also, here is a linker file, if this might help:
I am studying computer science now and i wanted to learn something in practice, so thats why i writing this microkernel.
Thx in advice.
Here is my code:
main.asm (entry point is here)
Code: Select all
; Start of bootloader code
bits 32
global start
extern long_mode_start
section .text
align 4
start:
; Set up stack
mov esp, stack_top
; Perform checks
call check_multiboot
call check_cpuid
call check_long_mode
; Set up paging and enable 64-bit mode
call setup_page_tables
call enable_paging
; Load GDT and jump to 64-bit code
lgdt [gdt64_pointer]
jmp 0x08:long_mode_start
hlt
check_multiboot:
cmp eax, 0x2BADB002 ; Multiboot 1 magic number
jne .no_multiboot
ret
.no_multiboot:
mov al, "M"
jmp error
check_cpuid:
pushfd
pop eax
mov ecx, eax
xor eax, 1 << 21
push eax
popfd
pushfd
pop eax
push ecx
popfd
cmp eax, ecx
je .no_cpuid
ret
.no_cpuid:
mov al, "C"
jmp error
check_long_mode:
mov eax, 0x80000000
cpuid
cmp eax, 0x80000001
jb .no_long_mode
mov eax, 0x80000001
cpuid
test edx, 1 << 29
jz .no_long_mode
ret
.no_long_mode:
mov al, "L"
jmp error
setup_page_tables:
mov eax, page_table_l3
or eax, 0b11 ; present, writable
mov [page_table_l4], eax
mov eax, page_table_l2
or eax, 0b11 ; present, writable
mov [page_table_l3], eax
mov ecx, 0 ; counter
.loop:
mov eax, 0x200000 ; 2MiB
mul ecx
or eax, 0b10000011 ; present, writable, huge page
mov [page_table_l2 + ecx * 8], eax
inc ecx ; increment counter
cmp ecx, 512 ; checks if the whole table is mapped
jne .loop ; if not, continue
ret
enable_paging:
; Pass page table location to CPU
mov eax, page_table_l4
mov cr3, eax
; Enable PAE
mov eax, cr4
or eax, 1 << 5
mov cr4, eax
; Enable long mode
mov ecx, 0xC0000080
rdmsr
or eax, 1 << 8
wrmsr
; Enable paging
mov eax, cr0
or eax, 1 << 31
mov cr0, eax
ret
error:
; Print "ERR: X" where X is the error code
mov dword [0xb8000], 0x4f524f45
mov dword [0xb8004], 0x4f3a4f52
mov dword [0xb8008], 0x4f204f20
mov byte [0xb800a], al
hlt
section .bss
align 4096
page_table_l4:
resb 4096
page_table_l3:
resb 4096
page_table_l2:
resb 4096
stack_bottom:
resb 4096 * 4
stack_top:
section .rodata
align 8
gdt64:
dq 0 ; Null descriptor
dq 0x00af9a000000ffff ; Code segment descriptor
dq 0x00af92000000ffff ; Data segment descriptor
gdt64_pointer:
dw gdt64_end - gdt64 - 1 ; Limit (size of GDT)
dq gdt64 ; Base address of GDT
gdt64_end:
section .multiboot_header
align 4
header_start:
dd 0x1BADB002 ; Multiboot header magic number
dd 0x00000000 ; Multiboot flags
dd -(0x1BADB002) ; Checksum
; Alignment tags (optional, helps alignment)
align 8
header_end:
here i have main64.asm which is basically a loader for the 64 bit kernel and just prepares the data segment registers
Code: Select all
global long_mode_start
extern kernel_main
section .text
bits 64
long_mode_start:
; Load null into all data segment registers
mov ax, 0
mov ss, ax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; Set up stack (optional, stack setup can be done here if needed)
; mov rsp, <desired stack address>
; Call kernel_main with multiboot magic and mbi pointer
mov rdi, 0x2BADB002 ; Multiboot 1 magic number
mov rsi, [rbx + 8] ; Multiboot information structure pointer
call kernel_main
hlt
Code: Select all
;;;;;;;;;;;;;;;;;;;;;;;; MULTIBOOT
MBOOT_PAGE_ALIGN EQU 1 << 0
MBOOT_MEM_INFO EQU 1 << 1
MBOOT_USE_GFX EQU 0
MBOOT_MAGIC EQU 0x1BADB002
MBOOT_FLAGS EQU MBOOT_PAGE_ALIGN | MBOOT_MEM_INFO | MBOOT_USE_GFX
MBOOT_CHECKSUM EQU -(MBOOT_MAGIC + MBOOT_FLAGS)
;;;;;;;;;;;;;;;;;;;;;;;; KERNEL STUFF
section .multiboot_header
ALIGN 4
DD MBOOT_MAGIC
DD MBOOT_FLAGS
DD MBOOT_CHECKSUM
DD 0, 0, 0, 0, 0
DD 0
DD 800
DD 600
DD 32
Code: Select all
extern "C" void kernel_main(uint32_t magic, multiboot_info_t* mbd) {
if (is_64bit_mode_enabled()) {
printf("64-bit mode is enabled\n");
} else {
printf("64-bit mode is not enabled\n");
}
if (is_gdt_enabled()) {
printf("GDT is enabled\n");
} else {
printf("GDT is not enabled\n");
}
if (is_paging_enabled()) {
printf("Paging is enabled\n");
} else {
printf("Paging is not enabled\n");
}
if (is_interrupts_enabled()) {
printf("Interrupts are enabled\n");
} else {
printf("Interrupts are not enabled\n");
}
init_idt();
if (is_interrupts_enabled()) {
printf("Interrupts are enabled\n");
} else {
printf("Interrupts are not enabled\n");
}
/* Check bit 6 to see if we have a valid memory map */
if(!(mbd->flags >> 6 & 0x1)) {
printf("invalid memory map given by GRUB bootloader");
}
/* Make sure the magic number matches for memory mapping*/
if(magic != MULTIBOOT_BOOTLOADER_MAGIC) {
printf("invalid magic number!");
}
/* Check bit 6 to see if we have a valid memory map */
if(!(mbd->flags >> 6 & 0x1)) {
printf("invalid memory map given by GRUB bootloader");
}
/* Loop through the memory map and display the values */
int i;
for(i = 0; i < mbd->mmap_length;
i += sizeof(multiboot_memory_map_t))
{
multiboot_memory_map_t* mmmt =
(multiboot_memory_map_t*) (mbd->mmap_addr + i);
if(mmmt->type == MULTIBOOT_MEMORY_AVAILABLE) {
/*
* Do something with this memory block!
* BE WARNED that some of memory shown as availiable is actually
* actively being used by the kernel! You'll need to take that
* into account before writing to memory!
*/
}
}
while (1) {
asm("hlt");
}
}
Also, here is a linker file, if this might help:
Code: Select all
SECTIONS
{
. = 1M;
.boot :
{
KEEP(*(.multiboot_header))
}
.text :
{
*(.text)
}
.rodata :
{
*(.rodata)
}
.data :
{
*(.data)
}
.bss :
{
*(COMMON)
*(.bss)
}
/DISCARD/ : { *(.eh_frame) *(.eh_frame_hdr) }
}
Thx in advice.