[BITS 32]
;;
;; Multiboot header code
;;
[SECTION .mbhdr]
[EXTERN low_end]
[EXTERN high_end]
ALIGN 8
MbHdr:
dd 0xE85250D6 ;; MB2 Magic number
dd 0 ;; Architecture
dd HdrEnd - MbHdr ;; Length
dd -(0xE85250D6 + HdrEnd - MbHdr) ;; Checksum
;; Tags start here
;; Set correct sections
dw 2, 0
dd 24
dd MbHdr
dd HdrEnd
dd low_end
dd high_end
;; Set correct entry point
dw 3, 0
dd 12
dd start
;; Mark the end of the tags (see Multiboot specification)
dw 0, 0
dd 8
HdrEnd:
;;
;; Actual boot code
;;
[SECTION .boot]
[EXTERN Stack]
[GLOBAL start]
start:
xchg bx, bx
mov eax, GDT32Init ;; Get the GDT address
lgdt [eax] ;; Load the GDT
push 0x08 ;; Jump to
push .GDT32works ;; code
retf ;; segment
.GDT32works:
mov eax, 0x10
mov ds, ax ;; Set data and stack segment registers to
mov ss, ax ;; the newly defined data segment
mov esp, Stack ;; Set the Stack pointer to our reserved Stack
call EnableLongMode ;; Call the function that enables Long Mode
mov eax, GDT64Init ;; Now that we are in long mode, we need
;; a new GDT with 64 bit entries
lgdt [eax] ;; Load the new GDT
push 0x08 ;; Analogue to the previous stuff
push .GDT64works
retf
[BITS 64]
[EXTERN kernel_main]
.GDT64works:
;; xchg bx, bx
mov eax, 0x10 ;; Analogue to the previous stuff
mov ds, ax
mov es, ax
mov ss, ax
;; xchg bx, bx
mov rsp, Stack + 0xFFFFFFFF80000000 ;; correct the stack pointer
mov rax, GDT64HigherHalfInit
lgdt [rax]
xchg bx, bx
mov rax, qword kernel_main
call rax
cli
jmp $
[BITS 32]
[EXTERN PML4]
[EXTERN PDPT]
[EXTERN PD]
EnableLongMode:
mov eax, PDPT ;; Get the address of our page directory pointer table
;; This address is 4 kb aligned so the lower 12 bits
;; are
free to contain flags
or eax, 1 ;; Set the "present" flag of the address by
;; making sure, the lowest bit is 1
mov [PML4], eax ;; Insert our newly created PDPT into the first
;; position of our PML4
mov [PML4 + 0xFF8], eax ;; Insert it into the higher half part
;; as well, so we can access this part after enabling
;; the higher half part as well
mov eax, PD ;; Get the address of our first page directory
or eax, 1 ;; Set the present flag
mov [PDPT], eax ;; Insert it into our PDPTs first position
mov [PDPT + 0xFF0], eax ;; Insert it into the highest part as well
mov dword [PD], 0x000083 ;; Identity map the first pages and set
mov dword [PD + 8], 0x200083 ;; their flags to "present", "read/write"
mov dword [PD + 16], 0x400083 ;; and "global" (0x83 = 10000011)
mov dword [PD + 24], 0x600083 ;;
mov eax, PML4 ;; Get the address of our root page directory
mov cr3, eax ;; Insert it into the page directory register
mov eax, cr4 ;; Get content of the cr4 register
or eax, 1 << 5 ;; Set the 5th flag of the cr4 content to
;; enable PAE
mov cr4, eax ;; And reinsert into the cr4 register
mov ecx, 0xC0000080 ;; Specify which registers we want to read
;; The upper 32 bit go into EDX, the lower
;; ones into EAX
rdmsr ;; Read the model specific registers
or eax, 1 << 8 ;; Set the 8th bit to enable long mode
wrmsr ;; Write back the data
mov eax, cr0 ;; Get the content of the cr0 register
or eax, 1 << 31 ;; Set the 31st flag (paging enabled)
mov cr0, eax ;; Reinsert the modified data
ret ;; Now that long mode is enabled, we jump
;; back to the calling function
;;
;; Data structures we need for GDT and Paging setup
;;
GDT32Init:
dw 23 ;; Number of bytes our gdt uses - 1
dd GDT32 ;; Address of the GDT to load
GDT64Init:
dw 23 ;; Number of bytes our gdt uses - 1
dd GDT64 ;; Address of the GDT to load
dd 0 ;; Empty the rest of the address to
;; stop remaining code remnants from
;; crapping our address
GDT64HigherHalfInit:
dw 23 ;; Number of bytes our gdt uses - 1
dq GDT64 + 0xFFFFFFFF80000000 ;; Translated address after
;; enabling paging
GDT32:
dq 0x0000000000000000 ;; Null Segment
dq 0x00CF9A000000FFFF ;; Code Segment
dq 0x00CF92000000FFFF ;; Data Segment
GDT64:
dq 0x0000000000000000 ;; Null Segment
dq 0x00A09A0000000000 ;; Code Segment
dq 0x00A0920000000000 ;; Data Segment