Instruction page faults appear when more code is linked
Posted: Tue Mar 24, 2009 8:39 pm
After playing around for a while with various tutorial code, I've decided to truly start writing my OS. However, I ran into this really strange problem right off the bat. I have a ~70 line piece of NASM code that sets up segmentation and paging, and moves the kernel into the higher half. By itself, it works flawlessly. But when I link some other code to it (about 50 lines of C worth, just some string manipulation stuff), it will page fault on an instruction right after paging is enabled. *None of that C code is executed at all*, and the page fault happens when everything is still being read from 0x00000000 - 0x00400000. It uses the same 4MB page trick that the higher half barebones guide does on the wiki. If I don't link that code, it works again. I checked the binary, and no symbols exceed about 0x(F8)10B000. Any ideas about what could be causing the problem?
Here is the file (boot.s):
; Multiboot stuff
MODULEALIGN equ 1<<0
MEMINFO equ 1<<1
FLAGS equ MODULEALIGN | MEMINFO
MAGIC equ 0x1BADB002
CHECKSUM equ -(MAGIC + FLAGS)
section .data
; Initial kernel address space
global init_kmap
align 0x1000
init_kmap:
dd 0x00000083 ; Identity map first 4 MB
times 991 dd 0 ; Fill until 0xF8000000
dd 0x00000083 ; Map first 4 MB again in higher mem
times 31 dd 0 ; Fill remainder of map
STACKSIZE equ 0x2000
init_stack:
align 4
times STACKSIZE dd 0
gdt:
align 4
dd 0x00000000, 0x00000000
dd 0x0000FFFF, 0x00CF9A00
dd 0x0000FFFF, 0x00CF9200
dd 0x0000FFFF, 0x00CFFA00
dd 0x0000FFFF, 0x00CFF200
gdt_ptr:
align 4
dw 0x0027 ; 39 bytes limit
dd gdt - 0xF8000000 ; Points to physical GDT
section .text
; Multiboot header
MultiBootHeader:
align 4
dd MAGIC
dd FLAGS
dd CHECKSUM
global start
start:
mov ecx, gdt_ptr - 0xF8000000 ; Load (real) GDT pointer
lgdt [ecx] ; Load new GDT
mov ecx, 0x10 ; Load all kernel data segments
mov ds, cx
mov es, cx
mov fs, cx
mov gs, cx
mov ss, cx
mov ecx, init_kmap - 0xF8000000 ; Get physical address of the kernel address space
mov cr3, ecx ; Load address into CR3
mov ecx, cr4
mov edx, cr0
or ecx, 0x00000010 ; Set 4MB page flag
or edx, 0x80000000 ; Set paging flag
mov cr4, ecx ; Return to CR4 (make 4MB pgs)
mov cr0, edx ; Return to CR0 (start paging)
jmp 0x08:.upper ; Jump to the higher half (in new code segment)
.upper:
mov esp, (init_stack + STACKSIZE) ; Setup init stack
mov ebp, (init_stack + STACKSIZE) ; and base pointer
push eax ; Push multiboot magic number
add ebx, 0xF8000000 ; Make multiboot pointer virtual
push ebx ; Push multiboot pointer
hlt ; Halt for now
By the way, the reason the kernel is being mapped so far up is because it only really handles the allocation of page tables; the block caches will be in various usermode driver processes. There's enough space for the creation of 10,000+ processes' page tables - plenty for me.
Here is the file (boot.s):
; Multiboot stuff
MODULEALIGN equ 1<<0
MEMINFO equ 1<<1
FLAGS equ MODULEALIGN | MEMINFO
MAGIC equ 0x1BADB002
CHECKSUM equ -(MAGIC + FLAGS)
section .data
; Initial kernel address space
global init_kmap
align 0x1000
init_kmap:
dd 0x00000083 ; Identity map first 4 MB
times 991 dd 0 ; Fill until 0xF8000000
dd 0x00000083 ; Map first 4 MB again in higher mem
times 31 dd 0 ; Fill remainder of map
STACKSIZE equ 0x2000
init_stack:
align 4
times STACKSIZE dd 0
gdt:
align 4
dd 0x00000000, 0x00000000
dd 0x0000FFFF, 0x00CF9A00
dd 0x0000FFFF, 0x00CF9200
dd 0x0000FFFF, 0x00CFFA00
dd 0x0000FFFF, 0x00CFF200
gdt_ptr:
align 4
dw 0x0027 ; 39 bytes limit
dd gdt - 0xF8000000 ; Points to physical GDT
section .text
; Multiboot header
MultiBootHeader:
align 4
dd MAGIC
dd FLAGS
dd CHECKSUM
global start
start:
mov ecx, gdt_ptr - 0xF8000000 ; Load (real) GDT pointer
lgdt [ecx] ; Load new GDT
mov ecx, 0x10 ; Load all kernel data segments
mov ds, cx
mov es, cx
mov fs, cx
mov gs, cx
mov ss, cx
mov ecx, init_kmap - 0xF8000000 ; Get physical address of the kernel address space
mov cr3, ecx ; Load address into CR3
mov ecx, cr4
mov edx, cr0
or ecx, 0x00000010 ; Set 4MB page flag
or edx, 0x80000000 ; Set paging flag
mov cr4, ecx ; Return to CR4 (make 4MB pgs)
mov cr0, edx ; Return to CR0 (start paging)
jmp 0x08:.upper ; Jump to the higher half (in new code segment)
.upper:
mov esp, (init_stack + STACKSIZE) ; Setup init stack
mov ebp, (init_stack + STACKSIZE) ; and base pointer
push eax ; Push multiboot magic number
add ebx, 0xF8000000 ; Make multiboot pointer virtual
push ebx ; Push multiboot pointer
hlt ; Halt for now
By the way, the reason the kernel is being mapped so far up is because it only really handles the allocation of page tables; the block caches will be in various usermode driver processes. There's enough space for the creation of 10,000+ processes' page tables - plenty for me.