global start extern long_mode_start section .text bits 32 start: mov esp, stack_top ; stack mov edi, ebx ; move multiboot pointer to edi ; error suite call check_multiboot call check_cpuid call check_long_mode ; paging call set_up_page_tables call enable_paging ; 64-bit gdt lgdt [gdt64.pointer] jmp gdt64.code:long_mode_start ; change cs to gdt64 and long jump set_up_page_tables: mov eax, p3_table ; map p3_table's address to the first entry in p4_table or eax, 0b11 ; present, writable mov [p4_table], eax ; p4_table's first entry points to p3_table mov eax, p2_table ; sim. or eax, 0b11 mov [p3_table], eax mov ecx, 0 ; counter .map_p2_table: mov eax, 0x200000 ; 2MiB for a huge page mul ecx ; destination is ax register or eax, 0b10000011 ; huge ..... present, writable mov [p2_table + ecx * 8], eax ; map entry inc ecx ; scuffed for-loop section cmp ecx, 512 jne .map_p2_table ret enable_paging: mov eax, p4_table mov cr3, eax ; load p4_table to cr3, where it lives mov eax, cr4 or eax, 1 << 5 mov cr4, eax ; set PAE flag in cr4 mov ecx, 0xC0000080 rdmsr or eax, 1 << 8 wrmsr ; set long bit in MSR mov eax, cr0 or eax, 1 << 31 mov cr0, eax ; set paging bit in cr0 ret ; # | error ; 0 | .no_multiboot ; 1 | .no_cpuid ; 2 | .no_long_mode error: ; prints RE:R . is used to store error codes mov dword [0xb8000], 0x4f524f45 mov dword [0xb8004], 0x4f3a4f52 mov dword [0xb8008], 0x4f204f20 mov byte [0xb800a], al hlt check_multiboot: cmp eax, 0x36d76289 ; eax magic value jne .no_multiboot ret .no_multiboot: mov al, "0" jmp error check_cpuid: ; check if cpuid flag is supported pushfd pop eax ; put flags into eax mov ecx, eax ; copy to ecx xor eax, 1 << 21 ; flip the ID bit push eax popfd ; return eax to flags pushfd pop eax ; put flags to eax again push ecx popfd ; restore flags to old version cmp eax, ecx je .no_cpuid ret .no_cpuid: mov al, "1" jmp error check_long_mode: ; checks ensure cpu is new enough to be 64 bit mov eax, 0x80000000 cpuid cmp eax, 0x80000001 jb .no_long_mode mov eax, 0x80000001 cpuid test edx, 1 << 29 jz .no_long_mode ret .no_long_mode: mov al, "2" jmp error section .bss ; GRUB initialises this to 0 align 4096 ; tables must be aligned p4_table: resb 4096 p3_table: resb 4096 p2_table: resb 4096 stack_bottom: resb 4096 * 4 ; 16kB stack_top: section .rodata gdt64: dq 0 ; null .code: equ $ - gdt64 ; code offset dq (1 << 43) | (1 << 44) | (1 << 47) | (1 << 53) ; code .pointer: dw $ - gdt64 - 1 ; $ is .pointer here; length of gdt dq gdt64 ; pointer to gdt table