global start extern long_mode_start section .text bits 32 start: mov esp, stack_top call check_multiboot call check_cpuid call check_long_mode call setup_page_tables call enable_paging ; load the 64-bit GDT lgdt [gdt64.pointer] jmp gdt64.code:long_mode_start hlt check_multiboot: cmp eax, 0x36d76289 jne .no_multiboot ret .no_multiboot: mov al, "0" jmp error check_cpuid: ; Check if CPUID is supported by attempting to flip the ID bit (bit 21) ; in the FLAGS register. If we can flip it, CPUID is available. ; Copy EFLAGS in to EAX via stack pushfd pop eax ; Save the current flags mov ecx, eax ; Flip the ID bit xor eax, 1 << 21 ; push eax to eflags push eax popfd ; Copy EAX to FLAGS via the Stack pushfd pop eax ; Restore FLAGS from the old version stored in ECX (i.e. flipping the ; ID bit back if it was ever flipped). push ecx popfd ; Check if the ID was changed cmp eax, ecx je .no_cpuid ret .no_cpuid: mov al, "1" jmp error check_long_mode: ; test if extended processor info in available mov eax, 0x80000000 ; implicit argument for cpuid cpuid ; get highest supported argument cmp eax, 0x80000001 ; it needs to be at least 0x80000001 jb .no_long_mode ; if it's less, the CPU is too old for long mode ; extended info about long mode mov eax, 0x80000001 ; argument for the cpuid function cpuid ; Cpu id test edx, 1 << 29 ; check long mode availablity jz .no_long_mode ret .no_long_mode: mov al, "2" jmp error setup_page_tables: ; map first P4 entry to P3 table mov eax, p3_table or eax, 0b11 ; present + writable mov [p4_table], eax ; map first P3 entry to P2 table mov eax, p2_table or eax, 0b11 ; present + writable mov [p3_table], eax mov eax, 0b10000111 ; huge + no caching + writable + present mov [p2_table], eax ; map each P2 entry to a huge 2MiB page mov ecx, 8 ; counter variable mov eax, 0x200000 + 0b10000011 ; huge + writable + present .map_p2_table: cmp eax, 0b10000011 + stack_bottom jne .valid_mem mov ebx, 0b10000010 mov [p2_table + ecx], ebx add ecx, 8 cmp ecx, 4096 jne .valid_mem ret .valid_mem: mov [p2_table + ecx], eax add eax, 0x200000 ; 2MiB add ecx, 8 cmp ecx, 4096 jne .map_p2_table ret enable_paging: mov eax, p4_table mov cr3, eax ; enable PAE-flag in cr4 (Physical Address Extension) mov eax, cr4 or eax, 1 << 5 mov cr4, eax ; set the long mode bit in the EFER MSR (model specific register) mov ecx, 0xC0000080 rdmsr or eax, 1 << 8 wrmsr ; enable paging in the cr0 register mov eax, cr0 or eax, 1 << 31 mov cr0, eax ; move stack pointer to accommodate for the guard page add esp, 0x200000 ret section .rodata gdt64: dq 0 ; zero entry .code: equ $ - gdt64 dq (1<<43) | (1<<44) | (1<<47) | (1<<53) ; code segment .pointer: dw $ - gdt64 - 1 dq gdt64 ; Prints 'ERR: ' and the given error code to the screen and halts ; parameter: error code letter (ascii) in al error: mov dword [0xb8000], 0x4f524f45 mov dword [0xb8004], 0x4f3a4f52 mov dword [0xb8008], 0x4f504f20 mov byte [0xb800a], al hlt section .bss align 4096 p4_table: resb 4096 p3_table: resb 4096 p2_table: resb 4096 alignb 4096 * 512 ; align memory into huge memory page stack_bottom: resb 4096 * 64 stack_top: