Home turboPC
Python scripts Python tutorial Bouncing dice Diagram editor LRC network simulation
Qt embedded
OS Bootfloppy Go 64 bits Bochs

Switching into 64 bits mode

Using pieces of assember around the web [1], I synthesized the listing below. Assemble it with nasm [2]. It uses a multiboot header and is loaded by GRUB. It then sets up paging for 64 bits and enables 64 bits mode.

;
; See http://wiki.osdev.org/User:Stephanvanschaik/Setting_Up_Long_Mode
; Loader assembly to load the 64 bits kernel just after this file.

; Assume that we are loaded at 1M (0x100000)
org 0x100000
;default rel
bits 32 ; Start in 32 bits mode.
; Multiboot header:
; Settings for multiboot header
PAGE_ALIGN equ 1 << 0
MEM_INFO equ 1 << 1
KLUDGE equ 1 << 16
MAGIC equ 0x1BADB002
FLAGS equ PAGE_ALIGN | MEM_INFO | KLUDGE ; align and provide memory map
CHECKSUM equ -(MAGIC+FLAGS)

; actual multiboot header:
align 4
MultiBootHeader:
dd MAGIC
dd FLAGS
dd CHECKSUM
; item below are present if bit 16 is set in flags
dd MultiBootHeader ; physical address in file of header (will be 0x100000 if put at start)
dd 0x100000        ; load_addr: load address, the address to start loading
dd 0x0             ; load_end_addr: zero indicates to load whole file
dd 0x0             ; bss_end_addr: zero indicates no bss segment present
dd loader          ; entry_addr: jump to here

; GDT, three entries: one for code, one for data
GDT64:
.Null: equ $ - GDT64
dw 0
dw 0
db 0
db 0
db 0
db 0
.Code: equ $ - GDT64
dw 0
dw 0
db 0
db 10011000b ; access
db 00100000b ; granularity
db 0
.Data: equ $ - GDT64
dw 0
dw 0
db 0
db 10010000b ; access
db 00000000b ; granularity
db 0
.Pointer: ; GDT pointer
dw $ - GDT64 - 1 ; Limit
dq GDT64 ; Base

; Start of loader code:
loader:

; Prepare paging:
; PML4T - 0x1000
; PDPT - 0x2000
; PDT - 0x3000
; PT - 0x4000

; Clear the tables:
mov edi, 0x1000
mov cr3, edi ; CR3 is the page table address!
xor eax, eax
mov ecx, 4096
rep stosd
mov edi, cr3 ; restore edi

mov DWORD [edi], 0x2003 ; present and readwrite, points to first PDPT
add edi, 0x1000
mov DWORD [edi], 0x3003 ; present and readwrite, points to first PDT
add edi, 0x1000
mov DWORD [edi], 0x4003 ; present and readwrite, points to first PT
add edi, 0x1000

; identity map the first two megabytes:
mov ebx, 0x00000003
mov ecx, 512
; Fill all PT entries at 0x4000
SetEntry:
mov DWORD [edi], ebx
add ebx, 0x1000
add edi, 8
loop SetEntry

; Enable paging:
mov eax, cr4
or eax, 1 << 5 ; PAE-bit is bit 5
mov cr4, eax

; Set LM-bit (Long Mode bit):
mov ecx, 0xC0000080
rdmsr
or eax, 0x100 ; Set bit 8 (LM-bit)
wrmsr

; Enable paging:
mov eax, cr0
or eax, 0x80000000 ; Set bit 31 (PG-bit)
mov cr0, eax

; Load the GDT:
lgdt [GDT64.Pointer]

; Jump to 64 bits kernel:
jmp GDT64.Code:Realm64

bits 64

; realm64
Realm64:

cli
mov ax, GDT64.Data
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax

; Done!

; Setup stack pointer:
mov rsp, stackEnd
; Put a B upper left corner
mov al, 66 ; 'B'
mov [0xb8000], al

; Jump to code that is glued after this file
jmp einde

align 16
dataEnd:
; reserve bytes for stack:
stackBegin:
resb 1024
stackEnd:

einde:
XCHG BX, BX ; bochs breakpoint
jmp einde

References

[1] http://wiki.osdev.org/User:Stephanvanschaik/Setting_Up_Long_Mode
[2] http://www.nasm.us/