C++ Higher Half Kernel Loader
Posted: Sat Sep 01, 2007 4:09 pm
I've been rewriting my kernel and it now lives in the higher half (using paging, not the gdt trick), however I have a problem. The following code works fine:
However, if I move the declaration of cout outside the method (which I need to do so it can be used elsewhere), then the code locks up qemu on boot (haven't tried bochs yet as I need to build it with pse support).
I don't know how the output of GCC differs for the declarations inside & outside of a method, but figured someone here will and might be able to hint at what I'm doing wrong. The asm loader for my kernel is below, where I call the constructors for globals, I'm not sure if this is whats wrong or not...
The asm is largely borrowed from the higher half example on the wiki, but modified to call the constructors and to pass the multiboot structure as a virtual address rather than physical (so the C++ code doesn't need to translate).
One thought: can GCC emit calls to the new operator in cases like this? If so, mine currently returns 0 which might be the problem.
Also, in the above code I convert the multiboot cmdline pointer to a virtual address, but I'm far from an assembly expert and wondered if there's any better way to do it? The code I've written works fine, but it seems like a bodge to me (moving addresses around, adding & moving them back), so I wondered if any assembler gurus know of a better way (not a major issue, I just dislike my current code and don't know any way to improve it). It'd be useful to know of any better method to offset the pointers held by the structure before I do the same for the memory map.
Finally, I'll just state that yeah I know the multiboot structure isn't guaranteed to be inside the first 4MiB of physical memory and if it isn't my code will cause a page fault, but with grub legacy it always is and thats good enough for now.
Code: Select all
extern "C" void kmain(void *mboot, uint magic)
{
VGAConsole cout;
cout << "Phobos Version 0.1" << endl;
I don't know how the output of GCC differs for the declarations inside & outside of a method, but figured someone here will and might be able to hint at what I'm doing wrong. The asm loader for my kernel is below, where I call the constructors for globals, I'm not sure if this is whats wrong or not...
Code: Select all
global _loader ; ASM Entry Point
extern kmain ; C++ Code Entry Point
extern start_ctors, end_ctors, start_dtors, end_dtors
MODULEALIGN equ 1<<0 ; Align Modules on Page Boundaries Flag
MEMINFO equ 1<<1 ; Memory Map Flag
FLAGS equ MODULEALIGN | MEMINFO ; Flags
MAGIC equ 0x1BADB002 ; Multiboot Magic Number
CHECKSUM equ -(MAGIC + FLAGS) ; Checksum
KERNEL_VIRTUAL_BASE equ 0xC0000000 ; 3GiB
KERNEL_PAGE_NUMBER equ (KERNEL_VIRTUAL_BASE >> 22) ; Page directory index of kernel's 4MB PTE.
section .data
align 0x1000
BootPageDirectory:
; 0x00000083 = PS,RW,P
dd 0x00000083 ; Low kernel space
times (KERNEL_PAGE_NUMBER - 1) dd 0 ; Pages before kernel space.
dd 0x00000083 ; High kernel space
times (1024 - KERNEL_PAGE_NUMBER - 1) dd 0 ; Pages after the kernel image.
section .text
align 4
MultiBootHeader:
dd MAGIC
dd FLAGS
dd CHECKSUM
STACKSIZE equ 0x4000 ; Initial kernel stack (16KiB)
_loader:
mov ecx, (BootPageDirectory - KERNEL_VIRTUAL_BASE)
mov cr3, ecx ; Load Page Directory Base Register
mov ecx, cr4
or ecx, 0x00000010 ; Set PSE bit in CR4 to enable 4MB pages
mov cr4, ecx
mov ecx, cr0
or ecx, 0x80000000 ; Set PG bit in CR0 to enable paging
mov cr0, ecx
lea ecx, [StartInHigherHalf] ; Begin instructing in higher half
jmp ecx ; Must be absolute jump!
StartInHigherHalf:
mov esp, stack+STACKSIZE ; Setup Stack
add ebx, KERNEL_VIRTUAL_BASE ; Convert multiboot info physical addr to virtual
mov ecx, ebx ; Copy multiboot info addr to ecx
add ecx, 16 ; Add 16 (ecx now holds address of cmdline pointer)
mov edx, [ecx] ; Move cmdline pointer to edx
add edx, KERNEL_VIRTUAL_BASE ; Convert cmdline addr to virtual
mov [ecx], edx ; Copy virtual addr back to multiboot info struct
push eax ; Pass multiboot magic number
push ebx ; Pass multiboot info struct, may not be in 1st 4mb
static_ctors_loop: ; Run constructors of global objects
mov ebx, start_ctors
jmp .test
.body:
call [ebx]
add ebx,4
.test:
cmp ebx, end_ctors
jb .body
mov dword [BootPageDirectory], 0 ; Set page dir. entry 0 to null
invlpg [0] ; Invalidate page dir. entry 0
call kmain ; Call C++ code (should never return)
cli ; Stop interrupts
hlt ; Halt the machine
section .bss
align 32
stack:
resb STACKSIZE ; Reserve 16KiB stack on a quadword boundary
One thought: can GCC emit calls to the new operator in cases like this? If so, mine currently returns 0 which might be the problem.
Also, in the above code I convert the multiboot cmdline pointer to a virtual address, but I'm far from an assembly expert and wondered if there's any better way to do it? The code I've written works fine, but it seems like a bodge to me (moving addresses around, adding & moving them back), so I wondered if any assembler gurus know of a better way (not a major issue, I just dislike my current code and don't know any way to improve it). It'd be useful to know of any better method to offset the pointers held by the structure before I do the same for the memory map.
Finally, I'll just state that yeah I know the multiboot structure isn't guaranteed to be inside the first 4MiB of physical memory and if it isn't my code will cause a page fault, but with grub legacy it always is and thats good enough for now.