.intel_syntax noprefix
.section .boot32, "awx"
.code32
.include "src/switch/macros.s"
.extern __page_table_memory_start
.extern _code_64
.global _code_32
.global gdt64_offset_code
.global gdt64_offset_data
.global mapped_memory
.global page_table_unused
_code_32:
cli
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esi, offset enter_code32
call serial_send32
call check_cpuid
call check_longmode
setup_paging64:
mov eax, cr0
and eax, ~(1 << 31)
mov cr0, eax
mov edi, offset __page_table_memory_start # page map level 4
mov cr3, edi
xor eax, eax
mov ecx, 4096
rep stosd
mov edi, cr3
# bits 51:12 hold the 4KiB aligned address
# bit 1 | 0 -> read | 1 -> write
# bit 0 | 0 -> not present | 1 -> present
# pml4 entry - 51:12 -> pdpt address, 1 -> read/write, 0 -> present
lea eax, [edi + 0x1003]
mov long ptr [edi], eax # pml4[0] = pml4 entry
add edi, 0x1000
# pdpt entry - 51:12 -> pdt address, 7 -> 1gb pages or 2mb pages, 1 -> read/write, 0 -> present
lea eax, [edi + 0x1003]
mov long ptr [edi], eax # pdpt[0] = pdpt entry
add edi, 0x1000
# pdt entry - 51:12 -> pt address, 7 -> 2mb pages or 4kb pages, 1 -> read/write, 0 -> present
lea eax, [edi + 0x1003]
mov long ptr [edi], eax # pdt[0] = pdt entry
add edi, 0x1000
# pt entry - 51:12 -> phys address, 1 -> read/write, 0 -> present
mov ebx, 0x03
.equ pages, 512
mov ecx, pages
mov long ptr [mapped_memory], pages * 0x1000
set_entries:
mov long ptr [edi], ebx # pt[i] (0x4000 + i * 8) = pt entry
add ebx, 0x1000
add edi, 8
loop set_entries
mov long ptr [page_table_unused], edi
mov esi, offset leave_code32
call serial_send32
enable_PAE_paging:
mov eax, cr4
or eax, 1 << 5
mov cr4, eax
enable_longmode:
mov ecx, 0xc0000080
rdmsr
or eax, 1 << 8
wrmsr
enable_paging:
mov eax, cr0
or eax, 1 << 31
mov cr0, eax
lgdt [gdt64_desc]
.att_syntax prefix
jmpl $gdt64_offset_code, $_code_64
.intel_syntax noprefix
; push offset gdt64_offset_code
; mov eax, offset _code_64
; push eax
; retf
mapped_memory: .8byte 0
page_table_unused: .8byte 0
gdt64:
gdt64_null:
.8byte 0
gdt64_code:
.4byte 0xffff
.byte 0
.byte 0b10011010
.byte 0b10101111
.byte 0
gdt64_data:
.4byte 0xffff
.byte 0
.byte 0b10010010
.byte 0b11001111
.byte 0
gdt64_tss:
.4byte 0x00000068
.4byte 0x00cf8900
gdt64_end:
gdt64_desc:
.2byte gdt64_end - gdt64 - 1
.8byte gdt64
.equ gdt64_offset_code, gdt64_code - gdt64
.equ gdt64_offset_data, gdt64_data - gdt64
check_cpuid:
pushfd # push flags register onto stack
pop eax # pop into eax
mov ecx, eax # save eax into ecx
xor eax, 1 << 21 # flip the ID bit
push eax # push eax onto stack
popfd # pop off stack and copy into flags register
pushfd # push flags register onto stack
pop eax # pop into eax, eax will contain flipped bit if cpuid is supported
push ecx # push ecx onto stack
popfd # restore flags register
xor eax, ecx
jz no_cpuid
ret
no_cpuid:
mov esi, offset _no_cpuid
call serial_send32
1: jmp 1b
_no_cpuid: .asciz "cpuid not detected"
check_longmode:
mov eax, 0x80000000
cpuid
cmp eax, 0x80000001
jb no_longmode
mov eax, 0x80000001
cpuid
test edx, 1 << 29
jz no_longmode
ret
no_longmode:
mov esi, offset _no_longmode
call serial_send32
1: jmp 1b
_no_longmode: .asciz "longmode not detected"
.equ COM1, 0x3F8
serial_send32:
pushad
2:
inb COM1+5
test al, 0x20
jz 2b
lodsb
test al, al
jz 1f
mov dx, COM1+0
out dx, al
jmp 2b
1:
popad
ret
enter_code32: .asciz "enter code32\n"
leave_code32: .asciz "leave code32\n"
.att_syntax prefix