lm.asm (2657B)
1 ; call in pm_mode 2 call lm_check 3 4 ; Check for Long Mode. 5 lm_check: 6 pusha 7 pushfd ; Push EFLAGS register to the stack. 8 pop eax ; Pop EFLAGS registers to `eax`. 9 mov ecx, eax ; Keep a backup in `ecx`. 10 xor eax, 1 << 21 ; Flip the 21st bit if the it's not 1. 11 push eax 12 popfd ; Pop `eax` to EFLAGS. 13 14 pushfd 15 pop eax ; Copy EFLAGS back to `eax`. 16 push ecx 17 popfd ; Restore the flipped version. 18 xor eax, ecx 19 jz lm_fail ; If EFLAGS' value hasn't changed, we have CPUID. 20 21 mov eax, 0x80000000 ; CPUID argument. 22 cpuid ; CPU identification. `eax` is now populated with info. 23 cmp eax, 0x80000001 ; If it's less than 0x80000000 we cannot have Long Mode. 24 jb lm_fail 25 26 mov eax, 0x80000001 ; Get extended processor information. 27 cpuid 28 test edx, 1 << 29 29 jz lm_fail ; If the 29th bit is 0, we cannot have Long Mode. 30 31 popa 32 ret 33 34 ; No long mode. 35 lm_fail: 36 popa 37 call kernel_exec 38 jmp $ ; Safety hang. 39 40 ; We'll check for (and switch to) Long Mode here. 41 lm_enter: 42 cli 43 mov eax, cr0 44 or eax, 1 << 31 ; Paging Enable. 45 mov cr0, eax 46 ; Set up PAE paging. 47 mov edi, 0x1000 ; Page table starts 4KiB in memory. 48 mov cr3, edi ; Hold the location of the highest page table. 49 xor eax, eax ; Clear memory space. 50 mov ecx, 4096 51 rep stosd ; Write 4096 dwords with the value 0. 52 mov edi, 0x1000 ; Go to the initial location. 53 54 ; Page table locations: 55 ; Page Map Level 4 Table (PML4T): 0x1000 56 ; Page Directory Pointer Table (PDPT): 0x2000 57 ; Page Directory Table (PDT): 0x3000 58 ; Page Table (PT); 0x4000 59 ; 60 ; In order for each table to point to each other we'll use a size directive. 61 ; Since tables are 4KiB away from each other we'll move dwords. 62 mov dword [edi], 0x2003 ; PML4T -> PDPT 63 add edi, 0x1000 ; Move 4KiB forward. 64 mov dword [edi], 0x3003 ; PDPT -> PDT 65 add edi, 0x1000 66 mov dword [edi], 0x4003 ; PDT -> PT 67 add edi, 0x1000 68 69 mov dword ebx, 3 ; Map beginning of code. 70 mov ecx, 512 ; Do it 512 times. 71 setentry: 72 mov dword [edi], ebx 73 add ebx, 0x1000 74 add edi, 8 ; Go to the next entry in the page table. 75 loop setentry 76 77 ; Enable the 6th bit in CR4 (Physical Address Extension Bit) to tell 78 ; the CPU that we're using PAE paging. 79 mov eax, cr4 80 or eax, 1 << 5 81 mov cr4, eax 82 83 ; Activate Long Mode and Paging using the EFER. 84 mov ecx, 0xc0000080 85 rdmsr ; Copy the contents of EFER to `eax`. 86 or eax, 1 << 8 ; Set LME bit to enable Long Mode. 87 wrmsr ; Write `eax` back to EFER. 88 89 lgdt [gdt_ptr] ; Load the GDT. 90 jmp GDT_CODESEG:lm_init ; Switch to Long Mode. 91 92 ; We're in Long Mode now. 93 [bits 64] 94 lm_init: 95 mov ax, GDT_DATASEG 96 mov ds, ax 97 mov ss, ax 98 mov es, ax 99 mov fs, ax 100 mov gs, ax 101 102 mov rbp, 0x90000 103 mov rsp, rbp 104 call kernel_exec 105 jmp $ 106