.section .stage1
.code32
.globl start_32bit
start_32bit:
    mov $0x10, %ax
    mov %ax, %ds
    mov %ax, %es
    mov %ax, %fs
    mov %ax, %gs
    mov %ax, %ss

    cld
    xor %eax, %eax

    # clear paging structures
    mov $0x100000, %edi
    mov %edi, %ecx
    shr $2, %ecx # %ecx /= 4
    rep stosl

    # set P, RW, G
    mov $0x00000103, %ebx
	xor %edx, %edx
    mov $0x00101000, %esi

    # PML4E 0x000
    # we need the first 1GB identically mapped
    # so that we won't trigger a triple fault after
    # enabling paging
	lea -0x1000(%esi), %edi # %edi = 0x100000
    call fill_pxe

    # PML4E 0xff0
	mov $0x80000000, %edx
	lea 0xff0(%edi), %edi
	call fill_pxe
	xor %edx, %edx

    # setup PDPT for physical memory mapping
    mov %esi, %edi

    # set PS
    or $0x00000080, %ebx
    mov $256, %ecx
    xor %esi, %esi
_fill_loop1:
    call fill_pxe
    lea 8(%edi), %edi
    add $0x40000000, %esi # 1GB
    adc $0, %edx
    loop _fill_loop1

	mov $0x80000000, %edx

    # set PCD, PWT
    or $0x00000018, %ebx
    mov $256, %ecx
    xor %esi, %esi
_fill_loop2:
    call fill_pxe
    lea 8(%edi), %edi
    add $0x40000000, %esi # 1GB
    adc $0, %edx
    loop _fill_loop2

	xor %edx, %edx

    # PML4E 0xff8
    mov %edi, %esi # 0x102000
    mov $0x100ff8, %edi
    # clear PCD, PWT, PS
    and $(~0x00000098), %ebx
    call fill_pxe

    # PDPTE 0xff8
    lea 0xff8(%esi), %edi  # 0x102ff8
    lea 0x1000(%esi), %esi # 0x103000
    call fill_pxe

    # PDE 0xff0
    lea 0xff0(%esi), %edi  # 0x103ff0
    lea 0x1000(%esi), %esi # 0x104000
    call fill_pxe

    # fill PT (kernel image)
    mov %esi, %edi # 0x104000
    mov $0x2000, %esi

.extern KERNEL_PAGES
    mov $KIMAGE_PAGES, %ecx

_fill_loop3:
    call fill_pxe
    lea 8(%edi), %edi
	lea 0x1000(%esi), %esi
    loop _fill_loop3

    # set msr
    mov $0xc0000080, %ecx
    rdmsr
    or $0x901, %eax # set LME, NXE, SCE
    wrmsr

    # set cr4
    mov %cr4, %eax
    or $0xa0, %eax # set PAE, PGE
    mov %eax, %cr4

    # load new page table
	xor %eax, %eax
	inc %eax
	shl $20, %eax # %eax = 0x100000
    mov %eax, %cr3

    mov %cr0, %eax
    // SET PE, WP, PG
    or $0x80010001, %eax
    mov %eax, %cr0

    # create gdt
	xor %eax, %eax # at 0x0000
	mov %eax, 0x00(%eax)
	mov %eax, 0x04(%eax) # null descriptor
	mov %eax, 0x08(%eax) # code segment lower
	mov %eax, 0x10(%eax) # data segment lower
	mov $0x00209a00, %ecx
	mov %ecx, 0x0c(%eax) # code segment higher
	mov $0x00009200, %ecx
	mov %ecx, 0x14(%eax) # data segment higher

    # gdt descriptor
	push %eax
	push %eax

    # pad with a word
	mov $0x00170000, %eax
	push %eax

	lgdt 2(%esp)
	add $12, %esp

    ljmp $0x08, $_64bit_entry

# %ebx: attribute low
# %edx: attribute high
# %esi: page physical address
# %edi: page x entry address
fill_pxe:
    lea (%ebx, %esi, 1), %eax
    mov %eax, (%edi)
    mov %edx, 4(%edi)

    ret

.code64
_64bit_entry:
	jmp start_64bit

.section .text.kinit
start_64bit:
    # set stack pointer and clear stack bottom
	movzw %sp, %rdi
	xor %rsp, %rsp
	inc %rsp
	neg %rsp
	shr $40, %rsp
	shl $40, %rsp

	add %rdi, %rsp
	mov %rsp, %rdi

    # make stack frame
	lea -16(%rsp), %rsp
	mov %rsp, %rbp

	xor %rax, %rax
	mov %rax, (%rsp)
	mov %rax, 8(%rsp)

    call kernel_init

_64bit_hlt:
	cli
	hlt
	jmp _64bit_hlt
