view cos/kernel/goto64.asm @ 219:1fa3e0050b49

Expanded ad hoc code generator
author Windel Bouwman
date Sat, 06 Jul 2013 12:38:09 +0200
parents bcb3b68c8147
children
line wrap: on
line source

;#!/usr/bin/nasm

;
; 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)

; This file sets up long mode and creates paging tables.
; Use 2 mbyte pages. Is this more efficient?

; Intended memory map (copied from pure64), at the end of this file:
; MOVED TO 0x5000!! 0x0 : IDT, 256 entries
; 0x1000 - 0x2000 : PML4 (Page map level 4)
; 0x2000 - 0x3000 : PDPT (page directory pointer table)
; 0x3000 - 0x4000 : PDT (page directory table)
; 0x4000 - 0x5000 : PT (page table)
; 0x5000 - 0x6000 : IDT entries
; 0x6000 - 0xA000 : Stack

bits 32 ; Start in 32 bits mode, as loaded by GRUB

; 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
extern load_end_address ; Import load end address from linker script
extern bss_end_address  ; Import bss end address to make sure data is zero initialized.
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 load_end_address ; load_end_addr: zero indicates to load whole file
dd bss_end_address  ; bss_end_addr: zero indicates no bss segment present
dd loader           ; entry_addr: jump to here

; 32 bits temporary GDT:
align 16                                                                        
gdt32:
dw 0x0000, 0x0000, 0x0000, 0x0000   ; Null desciptor
dw 0xFFFF, 0x0000, 0x9A00, 0x00CF   ; 32-bit code desciptor
dw 0xFFFF, 0x0000, 0x9200, 0x008F   ; 32-bit data desciptor
gdt32_end:

; 32 bits gdt pointer:
align 16
gdt32pointer:              ; Global Descriptors Table Register
dw gdt32_end - gdt32 - 1      ; limit of GDT (size minus one)
dq gdt32          ; linear address of GDT

; GDT, three entries: one for code, one for data
align 16
gdt64:
.Null: equ $ - gdt64
dq 0
.Code: equ $ - gdt64
dw 0 ; Segment limit 15-0
dw 0 ; Base 15 - 0
db 0 ; Base 23 - 16
db 10011000b ; access 0x98 (P=1 => Present)
db 00100000b ; granularity 0x20 (L=1 => long mode)
db 0
.Data: equ $ - gdt64
dw 0
dw 0
db 0
db 10010000b ; access ; 0x90
db 00000000b ; granularity 0x00
db 0
gdt64end:

;SYS64_NULL_SEL equ $-gdt64    ; Null Segment                                    
; dq 0x0000000000000000 
; SYS64_CODE_SEL equ $-gdt64    ; Code segment, read/execute, nonconforming
; dq 0x0020980000000000      ; 0x00209A0000000000 
; SYS64_DATA_SEL equ $-gdt64    ; Data segment, read/write, expand down
; dq 0x0000900000000000      ; 0x0020920000000000
; gdt64_end: 

gdt64pointer: ; GDT pointer
dw gdt64end - gdt64 - 1 ; Limit (size)
dq gdt64 ; Base

hltmessage:
db "Long mode not supported", 0x0

; Start of loader code:
loader:
; here ebx contains the pointer to the multiboot header, store is for later use.
; Make sure that multiboot_info is located in the BSS section and that this section
; is proper zeroed.
extern multiboot_info
mov [multiboot_info], ebx

; Check that the CPU supports long mode:
mov eax, 80000000h
cpuid
cmp eax, 80000000h
jbe no_long_mode
mov eax, 80000001h
cpuid
bt edx, 29
jnc no_long_mode
jmp cpu_has_long_mode

no_long_mode:
; Print long mode not supported
mov edi, 0xb8000
mov esi, hltmessage
xor eax, eax
loop1:
lodsb
mov dl, al
stosb
mov al, 0x1f
stosb
cmp dl, 0
jne loop1
hlt

cpu_has_long_mode:

lgdt [gdt32pointer]         ; Reload a valid temporary 32 bits GDT, overload GRUB gdt.
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp 8:start32            ; make sure CS is loaded.
start32:

cld   ; clear direction?

; Clear the paging tables 0x1000, 0x2000, 0x3000 and 0x4000:
mov edi, 0x1000
xor eax, eax
mov ecx, 0x1000
rep stosd

; Create PML4 table:
mov edi, 0x1000 
mov eax, 0x2003
stosd

; Create PDP (page directory pointer) table:
mov edi, 0x2000
mov eax, 0x3003 ; PDPT entry, present and read/write
stosd

; Create PD (page directory) table
mov edi, 0x3000
; First entry:
mov eax, 0x8F ; PD entry, present (bit 0), read write (bit 1) and bit 7, page size=2MB
stosd
xor eax, eax
stosd

; Second entry:
mov eax, 0x20008f
stosd
xor eax, eax
stosd

; Third entry:
mov eax, 0x40008f
stosd
xor eax, eax
stosd
; 6 MB mapped in total now.

mov edi, 0x1000   ; Set load address
mov cr3, edi      ; CR3 is the PML4 base address!

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

lgdt [gdt64pointer]              ; Load the GDT

; 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

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

bits 64
align 16

Realm64:

; Clear segment registers:
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov fs, ax
mov gs, ax

lgdt [gdt64pointer]    ; Reload GDT in 64 bits mode

; Test:
mov ax, 0x10
mov ds, ax
; End of test

; TODO: determine a good place for the kernel stack.
mov rsp, 0xA000      ; Setup stack pointer.

extern kmain
call kmain              ; Call kernel in C-code