|
| 1 | +.section .text, "ax" |
| 2 | +.code32 |
| 3 | + |
| 4 | +ram32_start: |
| 5 | + # Indicate (via serial) that we are executing out of RAM |
| 6 | + movw $0x3f8, %dx |
| 7 | + movb $'R', %al |
| 8 | + outb %al, %dx |
| 9 | + |
| 10 | +setup_page_tables: |
| 11 | + # First PML2 entry identity maps [0, 2 MiB) |
| 12 | + movl $0b10000011, (pml2) # huge (bit 7), writable (bit 1), present (bit 0) |
| 13 | + # First PML3 entry points to PML2 table |
| 14 | + movl $pml2, %eax |
| 15 | + orb $0b00000011, %al # writable (bit 1), present (bit 0) |
| 16 | + movl %eax, (pml3) |
| 17 | + # First PML4 entry points to PML3 table |
| 18 | + movl $pml3, %eax |
| 19 | + orb $0b00000011, %al # writable (bit 1), present (bit 0) |
| 20 | + movl %eax, (pml4) |
| 21 | + |
| 22 | +enable_paging: |
| 23 | + # Load page table root into CR3 |
| 24 | + movl $pml4, %eax |
| 25 | + movl %eax, %cr3 |
| 26 | + |
| 27 | + # Set CR4.PAE (Physical Address Extension) |
| 28 | + movl %cr4, %eax |
| 29 | + orb $0b00100000, %al # Set bit 5 |
| 30 | + movl %eax, %cr4 |
| 31 | + # Set EFER.LME (Long Mode Enable) |
| 32 | + movl $0xC0000080, %ecx |
| 33 | + rdmsr |
| 34 | + orb $0b00000001, %ah # Set bit 8 |
| 35 | + wrmsr |
| 36 | + # Set CRO.PG (Paging) |
| 37 | + movl %cr0, %eax |
| 38 | + orl $(1 << 31), %eax |
| 39 | + movl %eax, %cr0 |
| 40 | + |
| 41 | + # Indicate (via serial) that we have enabled paging |
| 42 | + movw $0x3f8, %dx |
| 43 | + movb $'P', %al |
| 44 | + outb %al, %dx |
| 45 | + |
| 46 | +jump_to_64bit: |
| 47 | + # We are now in 32-bit compatibility mode. To enter 64-bit mode, we need to |
| 48 | + # load a 64-bit code segment into our GDT. |
| 49 | + lgdtl gdt64_ptr |
| 50 | + # Set CS to a 64-bit segment and jump to 64-bit code. |
| 51 | + ljmpl $(code64_desc - gdt64_start), $ram64_start |
| 52 | + |
| 53 | +gdt64_ptr: |
| 54 | + .short gdt64_end - gdt64_start - 1 # GDT length is actually (length - 1) |
| 55 | + .long gdt64_start |
| 56 | +gdt64_start: |
| 57 | + # First descriptor is null |
| 58 | + .quad 0 |
| 59 | +code64_desc: |
| 60 | + # For 64-bit code descriptors, all bits except the following are ignored: |
| 61 | + # - CS.A=1 (bit 40) segment is accessed, prevents a write on first use. |
| 62 | + # - CS.R=1 (bit 41) segment is readable. (this might not be necessary) |
| 63 | + # - CS.C=1 (bit 42) segment is conforming. (this might not be necessary) |
| 64 | + # - CS.E=1 (bit 43) required, we are a executable code segment. |
| 65 | + # - CS.S=1 (bit 44) required, we are not a system segment. |
| 66 | + # - CS.DPL=0 (bits 45/46) we are using this segment in Ring 0. |
| 67 | + # - CS.P=1 (bit 47) required, the segment is present. |
| 68 | + # - CS.L=1 (bit 53) required, we are a 64-bit (long mode) segment. |
| 69 | + # - CS.D=0 (bit 54) required, CS.L=1 && CS.D=1 is resevered for future use. |
| 70 | + .quad (1<<40) | (1<<41) | (1<<42) | (1<<43) | (1<<44) | (1<<47) | (1<<53) |
| 71 | +gdt64_end: |
0 commit comments