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.

Revisions

  1. hibariya revised this gist Aug 14, 2016. 2 changed files with 60 additions and 84 deletions.
    2 changes: 1 addition & 1 deletion Makefile
    Original file line number Diff line number Diff line change
    @@ -7,4 +7,4 @@ clean:
    rm -rf *.bin

    run: bootable.bin
    qemu-system-x86_64 -monitor stdio bootable.bin
    qemu-system-x86_64 -monitor stdio bootable.bin
    142 changes: 59 additions & 83 deletions boot.s
    Original file line number Diff line number Diff line change
    @@ -1,121 +1,97 @@
    ; 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
    %define PML4T 0x8000
    %define PDPT 0x9000
    %define PDT 0xa000
    %define PT 0xb000

    ; 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
    cli

    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 PML4T
    mov dword eax, PDPT
    or dword eax, 011b ; user-mode / R/W / PML4E present
    mov dword [PML4T], eax
    mov dword [PML4T + 4], 0

    ; 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
    ; Set up PDPT
    mov dword eax, PDT
    or dword eax, 011b
    mov dword [PDE_ADDR], eax
    mov dword [PDE_ADDR + 4], 0
    mov dword [PDPT], eax
    mov dword [PDPT + 4], 0

    ; Set up PTE
    mov dword [PTE_ADDR], 011b
    mov dword [PTE_ADDR + 4], 0
    ; Set up PDT
    mov dword eax, PT
    or dword eax, 0011b
    mov dword [PDT], eax
    mov dword [PDT + 4], 0

    ; Set up pages
    mov edi, PT_ADDR
    ; Set up PT
    mov edi, PT
    mov ebx, 011b
    mov ecx, 512

    create_pages:
    mov dword [edi], ebx
    mov dword [edi + 4], 0
    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
    ; Set CR4.PAE to 1 to enable 64-bit paging
    mov eax, cr4
    or eax, 1 << 5
    mov cr4, eax

    ; CR3 = PML4E_ADDR
    mov eax, PML4E_ADDR
    ; CR3 = PML4T
    mov eax, PML4T
    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
    or eax, 1 << 8 ; Set EFER.LME (Long Mode Enable) to 1
    wrmsr

    ; Set CR0.PG to 1 to enable paging
    ; Set CR0.PE and CR0.PG to 1 to enable protected-mode and paging
    mov eax, cr0
    or eax, 0x80000000
    or eax, 1 << 31 | 1 << 0
    mov cr0, eax

    ; Load Global Descriptor Table
    lgdt [gdt_hdr]

    jmp 0x08:k64bits
    lgdt [GDT64.Pointer]
    jmp GDT64.Code:k64bits

    bits 64
    k64bits:
    mov rax, 0xdeadbeef
    mov rax, 0x123
    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:
    GDT64: ; Global Descriptor Table (64-bit).
    .Null: equ $ - GDT64 ; The null descriptor.
    dw 0 ; Limit (low).
    dw 0 ; Base (low).
    db 0 ; Base (middle)
    db 0 ; Access.
    db 0 ; Granularity.
    db 0 ; Base (high).
    .Code: equ $ - GDT64 ; The code descriptor.
    dw 0 ; Limit (low).
    dw 0 ; Base (low).
    db 0 ; Base (middle)
    db 10011010b ; Access (exec/read).
    db 00100000b ; Granularity.
    db 0 ; Base (high).
    .Data: equ $ - GDT64 ; The data descriptor.
    dw 0 ; Limit (low).
    dw 0 ; Base (low).
    db 0 ; Base (middle)
    db 10010010b ; Access (read/write).
    db 00000000b ; Granularity.
    db 0 ; Base (high).
    .Pointer: ; The GDT-pointer.
    dw $ - GDT64 - 1 ; Limit.
    dq GDT64 ; Base.

    times 512 - 2 - ($ - $$) db 0 ; zero-pad the 512-byte sector to the last 2 bytes
    dw 0xaa55 ; Magic "boot signature"
    dw 0xaa55 ; Magic "boot signature"
  2. hibariya created this gist Aug 3, 2016.
    10 changes: 10 additions & 0 deletions Makefile
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,10 @@
    all: bootable.bin

    bootable.bin:
    nasm boot.s -o $@

    clean:
    rm -rf *.bin

    run: bootable.bin
    qemu-system-x86_64 -monitor stdio bootable.bin
    121 changes: 121 additions & 0 deletions boot.s
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,121 @@
    ; 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"