Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
global _loader ; Make entry point visible to linker.
extern _main ; _main is defined elsewhere
; setting up the Multiboot header - see GRUB docs for details
MODULEALIGN equ 1<<0 ; align loaded modules on page boundaries
MEMINFO equ 1<<1 ; provide memory map
FLAGS equ MODULEALIGN | MEMINFO ; this is the Multiboot 'flag' field
MAGIC equ 0x1BADB002 ; 'magic number' lets bootloader find the header
CHECKSUM equ -(MAGIC + FLAGS) ; checksum required
; This is the virtual base address of kernel space. It must be used to convert virtual
; addresses into physical addresses until paging is enabled. Note that this is not
; the virtual address where the kernel image itself is loaded -- just the amount that must
; be subtracted from a virtual address to get a physical address.
KERNEL_VIRTUAL_BASE equ 0xC0000000 ; 3GB
KERNEL_PAGE_NUMBER equ (KERNEL_VIRTUAL_BASE >> 22) ; Page directory index of kernel's 4MB PTE.
section .data
align 0x1000
BootPageDirectory:
; This page directory entry identity-maps the first 4MB of the 32-bit physical address space.
; All bits are clear except the following:
; bit 7: PS The kernel page is 4MB.
; bit 1: RW The kernel page is read/write.
; bit 0: P The kernel page is present.
; This entry must be here -- otherwise the kernel will crash immediately after paging is
; enabled because it can't fetch the next instruction! It's ok to unmap this page later.
dd 0x00000083
times (KERNEL_PAGE_NUMBER - 1) dd 0 ; Pages before kernel space.
; This page directory entry defines a 4MB page containing the kernel.
dd 0x00000083
times (1024 - KERNEL_PAGE_NUMBER - 1) dd 0 ; Pages after the kernel image.
section .text
align 4
MultiBootHeader:
dd MAGIC
dd FLAGS
dd CHECKSUM
; reserve initial kernel stack space -- that's 16k.
STACKSIZE equ 0x4000
_loader:
; NOTE: Until paging is set up, the code must be position-independent and use physical
; addresses, not virtual ones!
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
; Start fetching instructions in kernel space.
lea ecx, [StartInHigherHalf]
jmp ecx ; NOTE: Must be absolute jump!
StartInHigherHalf:
; Unmap the identity-mapped first 4MB of physical address space. It should not be needed
; anymore.
mov dword [BootPageDirectory], 0
invlpg [0]
; NOTE: From now on, paging should be enabled. The first 4MB of physical address space is
; mapped starting at KERNEL_VIRTUAL_BASE. Everything is linked to this address, so no more
; position-independent code or funny business with virtual-to-physical address translation
; should be necessary. We now have a higher-half kernel.
mov esp, stack+STACKSIZE ; set up the stack
push eax ; pass Multiboot magic number
; pass Multiboot info structure -- WARNING: This is a physical address and may not be
; in the first 4MB!
push ebx
call _main ; call kernel proper
hlt ; halt machine should kernel return
section .bss
align 32
stack:
resb STACKSIZE ; reserve 16k stack on a quadword boundary
Hi, here is my code to use 4KB pages, it's based on the Higher Half tutorial.
Every virtual address has the form DIRECTORY | PAGE | OFFSET, so:
I create one page table that maps virtual address *|PAGE|* to physical address 0|PAGE|*
Then in my page directory I map every address like 0|*|* and 0xC0 << 2|*|* to point to the previous page table.
[GLOBAL mboot] ; Make 'mboot' accessible from C.
[GLOBAL kaddr_start]
[GLOBAL kaddr_end]
[EXTERN code] ; Start of the '.text' section.
[EXTERN bss] ; Start of the .bss section.
[EXTERN end] ; End of the last loadable section.
MBOOT_PAGE_ALIGN equ 1<<0 ; Load kernel and modules on a page boundary
MBOOT_MEM_INFO equ 1<<1 ; Provide your kernel with memory info
MBOOT_CMD_LINE equ 1<<2 ; Provide your kernel with CMD line
MBOOT_HEADER_MAGIC equ 0x1BADB002 ; Multiboot Magic value
MBOOT_HEADER_FLAGS equ MBOOT_PAGE_ALIGN | MBOOT_MEM_INFO | MBOOT_CMD_LINE
MBOOT_CHECKSUM equ -(MBOOT_HEADER_MAGIC + MBOOT_HEADER_FLAGS)
KERNEL_VIRTUAL_BASE equ 0xC0000000 ; 3GB
KERNEL_DIR_NUMBER equ (KERNEL_VIRTUAL_BASE >> 22)
section .data
; Page table to map the first 4MB of physical memory
align 0x4096
boot_page_table:
%assign i 0
%rep 1024
dd 0x00000003 + (i * 4096)
%assign i i+1
%endrep
; Kernel page directory
align 0x1000
boot_page_directory:
dd 0x00000003 + boot_page_table - KERNEL_VIRTUAL_BASE
times (KERNEL_DIR_NUMBER - 1) dd 0 ; Pages before kernel space.
; This page directory entry defines a 4MB page containing the kernel.
dd 0x00000003 + boot_page_table - KERNEL_VIRTUAL_BASE
times (1024 - KERNEL_DIR_NUMBER - 1) dd 0 ; Pages after the kernel image.
section .text
[BITS 32] ; All instructions should be 32-bit.
[GLOBAL _loader] ; Kernel entry point.
[EXTERN start] ; This is the entry point of our C code
mboot:
dd MBOOT_HEADER_MAGIC ; GRUB will search for this value on each
; 4-byte boundary in your kernel file
dd MBOOT_HEADER_FLAGS ; How GRUB should load your file / settings
dd MBOOT_CHECKSUM ; To ensure that the above values are correct
dd mboot ; Location of this descriptor
kaddr_start: dd code ; Start of kernel '.text' (code) section.
dd bss ; End of kernel '.data' section.
kaddr_end: dd end ; End of kernel.
dd _loader ; Kernel entry point (initial EIP).
_loader:
; Initialize VM
; TODO: set CR4 state
mov ecx, (boot_page_directory - KERNEL_VIRTUAL_BASE)
mov cr3, ecx ; Load Page Directory Base Register.
mov ecx, cr0
or ecx, 0x80000000 ; enable paging.
mov cr0, ecx
; Start fetching instructions in kernel space.
lea ecx, [StartInHigherHalf]
jmp ecx
StartInHigherHalf:
; Load multiboot information:
mov esp, kernel_stack ; Setup kernel stack
push ebx
push boot_page_directory
; Execute the C init:
cli ; Disable interrupts.
call start
jmp $ ; Avoid executing unknown things if start return
section .bss
resb 8192 ; Kernel stack
kernel_stack:
I'm wondering, what is the point of using the `lea` to eax and then jumping from the value in eax?
jumps are normally relative. it is assembled as the difference between the current instruction and the instruction where you end up. While in memory the difference is minimal, you change from the lower half to the upper half, adding 3GB to your address.
JMP register on the contrary performs an absolute jump: the address provided is where you'll end up. So if you provide an address in the higher half, you'll end up in the higher half, not in the same bit of memory in the lower half.
00119927614i[CPU0 ] WARNING: Encountered an unknown instruction (signalling illegal instruction)
00119927614i[CPU0 ] protected mode
00119927614i[CPU0 ] CS.d_b = 32 bit
00119927614i[CPU0 ] SS.d_b = 32 bit
00119927614i[CPU0 ] EFER = 0x00000000
00119927614i[CPU0 ] | RAX=000000002badb002 RBX=000000000002bdc0
00119927614i[CPU0 ] | RCX=0000000080000011 RDX=00000000000003f2
00119927614i[CPU0 ] | RSP=0000000000067ef0 RBP=0000000000067f00
00119927614i[CPU0 ] | RSI=000000000002bf4f RDI=000000000002bf50
00119927614i[CPU0 ] | R8=0000000000000000 R9=0000000000000000
00119927614i[CPU0 ] | R10=0000000000000000 R11=0000000000000000
00119927614i[CPU0 ] | R12=0000000000000000 R13=0000000000000000
00119927614i[CPU0 ] | R14=0000000000000000 R15=0000000000000000
00119927614i[CPU0 ] | IOPL=0 id vip vif ac vm RF nt of df if tf SF zf af PF cf
00119927614i[CPU0 ] | SEG selector base limit G D
00119927614i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D
00119927614i[CPU0 ] | CS:0008( 0001| 0| 0) 00000000 000fffff 1 1
00119927614i[CPU0 ] | DS:0010( 0002| 0| 0) 00000000 000fffff 1 1
00119927614i[CPU0 ] | SS:0010( 0002| 0| 0) 00000000 000fffff 1 1
00119927614i[CPU0 ] | ES:0010( 0002| 0| 0) 00000000 000fffff 1 1
00119927614i[CPU0 ] | FS:0010( 0002| 0| 0) 00000000 000fffff 1 1
00119927614i[CPU0 ] | GS:0010( 0002| 0| 0) 00000000 000fffff 1 1
00119927614i[CPU0 ] | MSR_FS_BASE:0000000000000000
00119927614i[CPU0 ] | MSR_GS_BASE:0000000000000000
00119927614i[CPU0 ] | RIP=0000000000107014 (0000000000107014)
00119927614i[CPU0 ] | CR0=0x80000011 CR1=0x0 CR2=0x0000000000000070
00119927614i[CPU0 ] | CR3=0x00106000 CR4=0x00000000
00119927614i[CPU0 ] >> (invalid) : FFFF
00119927614e[CPU0 ] exception(): 3rd (14) exception with no resolution, shutdown status is 00h, resetting
This looks like you ran into bogus memory: when reloading CR3, or enabling paging, you must make sure that the code you are currently executing still appears in memory at the same place. Check that the memory is indeed mapped there, and that your pagetables point to some sensible location rather than somewhere into nothingness.
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]