/*
 * Multiboot Entry
 */

#define STACK_SIZE 0x4000

#define MULTIBOOT_HEADER_MAGIC  0x1BADB002
#define MULTIBOOT_HEADER_FLAGS  0x00010003
//#define KERNEL_BASE 0x0000000000000000
#define KERNEL_BASE 0xFFFF800000000000
#define LOWMEM(_x) (_x - KERNEL_BASE)

.extern mb_entry

.text

/**
 * _start --
 *
 *	ELF entry point.
 */
.globl _start
_start: .code32
    jmp     multiboot_entry

/**
 * This header needs to be present near the start of the ELF binary that 
 * describes how the loader should load the operating system image into memory.
 */
.align  4
multiboot_header: .code32
.long   MULTIBOOT_HEADER_MAGIC
.long   MULTIBOOT_HEADER_FLAGS
.long   -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
.long   LOWMEM(multiboot_header)
.long   LOWMEM(_start)
.long   LOWMEM(_edata)
.long   LOWMEM(_end)
.long   LOWMEM(multiboot_entry)

// 
// Multiboot entry
// %eax: Magic (0x2BADB002)
// %ebx: multiboot info structure
//
multiboot_entry: .code32
    // Save multiboot magic
    movl    %eax, %edx

    // Setup the stack pointer
    movl    $LOWMEM(lmfarptr), %edi
    movw    $(0x7000 + 'A'), (0xB8000)
    movl    $LOWMEM(stack + STACK_SIZE), %esp

    // Reset EFLAGS to a known state
    movw    $(0x7000 + 'B'), (0xB8002)
    pushl   $0
    popf

    // Configure the CPU control register cr4
    movw    $(0x7000 + 'C'), (0xB8004)
    movl    %cr4, %eax
    orl     $0x0000006A0, %eax
    movl    %eax, %cr4

    // Load the 32-bit page table
    movw    $(0x7000 + 'D'), (0xB8006)
    movl    $LOWMEM(bootpgtbl1), %eax
    movl    %eax, %cr3

    // Enable 64-bit mode (long mode) and no-execute (NX) support
    movw    $(0x7000 + 'E'), (0xB8008)
    movl    $0xC0000080, %ecx
    rdmsr
    orl     $0x0900, %eax
    wrmsr

    // Load the GDT that contains segment descriptors.
    movw    $(0x7000 + 'E'), (0xB800A)
    movl    $LOWMEM(bootgdtdesc), %eax
    lgdt    (%eax)

    // Configure cr0
    movw    $(0x7000 + 'F'), (0xB800C)
    movl    %cr0, %eax
    orl     $0x8005002B, %eax
    movl    %eax, %cr0

    // Long jump into 64-bit mode
    movw    $(0x7000 + '0'), (0xB800E)
    ljmp    *(%edi)

    // No we're playing with power
lmenter: .code64
    movw    $(0x7000 + '1'), (0xB8010)
    xorw    %ax, %ax
    movw    %ax, %ss
    movw    %ax, %ds
    movw    %ax, %es
    movw    %ax, %fs
    movw    %ax, %gs

    movq    $LOWMEM(stack + STACK_SIZE), %rax
    movq    $0xFFFF8000, %rcx
    shlq    $32, %rcx
    orq     %rcx, %rax
    movq    %rax, %rsp

    // Long jump into the high memory address where the kernel executes in
    movq    $LOWMEM(lmhighptr), %rdi
    jmp    *(%rdi)

lmhigh:
    movw    $(0x7000 + '2'), (0xB8012)

    movq    %rdx, %rdi // Magic
    movq    %rbx, %rsi // Multiboot info pointer

    call    MachineBoot_Entry

    # Print halt to the graphics memory if we return
    movw    $(0x5000 + 'H'), (0xB8098)
    movw    $(0x5000 + 'A'), (0xB809A)
    movw    $(0x5000 + 'L'), (0xB809C)
    movw    $(0x5000 + 'T'), (0xB809E)
loop:
    hlt
    jmp     loop

.p2align 4
lmfarptr:
.long   LOWMEM(lmenter)
.word   0x08

.p2align 4
lmhighptr:
.quad   lmhigh

.p2align 12
bootgdt:
.quad   0x0000000000000000 /* Null */
.quad   0x00AF9A000000FFFF /* Kernel CS */
.quad   0x00CF92000000FFFF /* Kernel DS */
.quad   0x0000000000000000
.quad   0x0000000000000000
.quad   0x0000000000000000
.quad   0x0000000000000000
.quad   0x0000000000000000

.p2align 4
.globl bootgdtdesc
bootgdtdesc:
.word   0x003F
.quad   LOWMEM(bootgdt)

// Boot stack
.p2align 12
.globl stack
.comm   stack, STACK_SIZE

