Skip to content

Instantly share code, notes, and snippets.

@hibariya
Last active May 1, 2021 21:22
Show Gist options
  • Select an option

  • Save hibariya/9dc9b836e39a04300a410e92368dec7d to your computer and use it in GitHub Desktop.

Select an option

Save hibariya/9dc9b836e39a04300a410e92368dec7d to your computer and use it in GitHub Desktop.
Trying to get 4KB paging in long-mode...
; http://davidad.github.io/blog/2014/02/18/kernel-from-scratch/
; http://wiki.osdev.org/Setting_Up_Long_Mode#Setting_up_the_Paging
bits 16
org 0x7c00
k16bits:
; The cli instruction disables maskable external interrupts.
cli
; Set CR0.PE to 1 to enable 32-bit mode (al == eax)
; (Control register: https://en.wikipedia.org/wiki/Control_register)
mov eax, cr0
or al, 1
mov cr0, eax
jmp 0x08:k32bits
bits 32
k32bits:
; Set up paging for 64-bit mode
%define PML4E_ADDR 0x8000
%define PDPTE_ADDR 0x9000
%define PDE_ADDR 0xa000
%define PTE_ADDR 0xb000
%define PT_ADDR 0xc000
; Disable paging in advance
mov eax, cr0
and eax, 01111111111111111111111111111111b
mov cr0, eax
; Set up PML4E
mov dword eax, PDPTE_ADDR ; PDPTE_ADDR is the 1st entry of PML4E
or dword eax, 011b ; user-mode / R/W / PML4E present
mov dword [PML4E_ADDR], eax ; PML4E[0] = eax
mov dword [PML4E_ADDR + 4], 0 ; Although we're in 32-bit mode, the table entry is 64 bits. We can just zero out the upper bits in this case.
; Set up PDPTE
mov dword eax, PDE_ADDR
or dword eax, 011b
mov dword [PDPTE_ADDR], eax
mov dword [PDPTE_ADDR + 4], 0
; Set up PDE
mov dword eax, PTE_ADDR
or dword eax, 011b
mov dword [PDE_ADDR], eax
mov dword [PDE_ADDR + 4], 0
; Set up PTE
mov dword [PTE_ADDR], 011b
mov dword [PTE_ADDR + 4], 0
; Set up pages
mov edi, PT_ADDR
mov ebx, 011b
mov ecx, 512
create_pages:
mov dword [edi], ebx
add ebx, 0x1000
add edi, 8
loop create_pages
; Set each CR4.PGE and CR4.PAE to 1 to enable 64-bit paging and global pages
mov eax, 10100000b
mov cr4, eax
; CR3 = PML4E_ADDR
mov eax, PML4E_ADDR
mov cr3, eax
; Enable 64-bit mode
; A logical processor uses IA-32e paging if CR0.PG = 1, CR4.PAE = 1, and IA32_EFER.LME = 1. (intel manual: Vol.3A 4.5 IA-32E PAGING)
mov ecx, 0xc0000080 ; It's the argument for rdmsr following / MSR number of EFER is this constant
rdmsr ; Read EFER into eax
or eax, 100000000b ; Set EFER.LME (Long Mode Enable) to 1
wrmsr
; Set CR0.PG to 1 to enable paging
mov eax, cr0
or eax, 0x80000000
mov cr0, eax
; Load Global Descriptor Table
lgdt [gdt_hdr]
jmp 0x08:k64bits
bits 64
k64bits:
mov rax, 0xdeadbeef
hlt
; Grobal descriptor table entry
; https://en.wikipedia.org/wiki/Global_Descriptor_Table
; http://wiki.osdev.org/Global_Descriptor_Table
%macro GDT_ENTRY 4 ; GDT_ENTRY(Base, Limit, Ring, Access)
dw %2 & 0xffff ; Limit 0:15
dw %1 & 0xffff ; Base 0:15
db (%1 >> 16) & 0xff ; Base 16:23
db %4 | ((%3 << 4) & 0xf0) ; Access | (Ring << 4 & 0xf0)
db (%3 & 0xf0) | ((%2 >> 16) & 0x0f) ; Flags (Ring & 0xf0) | Limit 16:19
db %1 >> 24 ; Base 24:31
%endmacro
; (Executable, Direction/Conforming, RW, Accessed)
%define EXECUTE_READ 1010b
%define READ_WRITE 0010b
; (Granularity, Size, ?, 0), (Present, Privilege(2), 1)
%define RING0 10101001b
gdt_hdr:
dw gdt_end - gdt - 1
dd gdt
gdt:
GDT_ENTRY 0, 0, 0, 0
GDT_ENTRY 0, 0xffffff, RING0, EXECUTE_READ
GDT_ENTRY 0, 0xffffff, RING0, READ_WRITE
gdt_end:
times 512 - 2 - ($ - $$) db 0 ; zero-pad the 512-byte sector to the last 2 bytes
dw 0xaa55 ; Magic "boot signature"
all: bootable.bin
bootable.bin:
nasm boot.s -o $@
clean:
rm -rf *.bin
run: bootable.bin
qemu-system-x86_64 -monitor stdio bootable.bin
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment