Viewing: boot.S
📄 boot.S (Read Only) ⬅ To go back
/*
 * AdrOS - x86 Higher Half Kernel Bootstrap
 */

/* Constants */
.set MB_MAGIC, 0xE85250D6
.set MB_ARCH,  0          /* i386 */
.set KERNEL_VIRT_BASE, 0xC0000000

/* 
 * Convert virtual symbols to physical addresses before paging is enabled
 * by subtracting KERNEL_VIRT_BASE.
 */

/* .multiboot_header section (Goes to the .boot file in the linker) */
.section .multiboot_header
.align 8
multiboot_header_start:
    .long MB_MAGIC
    .long MB_ARCH
    .long multiboot_header_end - multiboot_header_start
    .long 0x100000000 - (MB_MAGIC + MB_ARCH + (multiboot_header_end - multiboot_header_start))

    /* End tag */
    .align 8
    .word 0, 0
    .long 8
multiboot_header_end:

/** .boot_text section (Goes to the .boot file in the linker)
 * This runs in 1MB (Identity Mapped by the Linker)
 */
.section .boot_text
.global _start
.type _start, @function

_start:
    /* Disable interrupts until the kernel installs a valid IDT */
    cli
    /* 
     * We are loaded at 1MB physical. Paging is OFF.
     * CPU is executing instructions here.
     * ESP is unknown.
     */

    /* Setup a temporary stack (using physical address) */
    mov $stack_top, %esp
    sub $KERNEL_VIRT_BASE, %esp

    /* Save Multiboot Info (ebx) and Magic (eax) */
    push %eax
    push %ebx

    /* Mask PIC (disable all IRQs) until kernel remaps/unmasks */
    movb $0xFF, %al
    outb %al, $0x21
    outb %al, $0xA1

    /*
     * SETUP PAE PAGING
     * PAE uses 3-level page tables:
     *   PDPT (4 entries x 8 bytes = 32 bytes, 32-byte aligned)
     *   PD[0..3] (512 entries x 8 bytes = 4KB each)
     *   PT (512 entries x 8 bytes = 4KB each)
     *
     * Address split: bits 31:30 = PDPT, bits 29:21 = PD, bits 20:12 = PT
     *
     * We map 0-16MB identity + higher-half using 8 page tables (2MB each).
     * PD[0] entries 0..7 = identity map (PT boot_pt0..boot_pt7)
     * PD[3] entries 0..7 = higher-half map (same PTs)
     * Recursive: PD[3][508..511] point to PD[0..3]
     */

    /* --- Zero out all paging structures --- */
    mov $boot_pdpt, %edi
    sub $KERNEL_VIRT_BASE, %edi
    xor %eax, %eax
    mov $(32 + 4*4096 + 8*4096), %ecx  /* PDPT + 4 PDs + 8 PTs in bytes */
    shr $2, %ecx                        /* /4 for stosl */
    rep stosl

    /* --- Fill page tables (boot_pt0..boot_pt7) with 64-bit PAE entries --- */
    /* Each PT has 512 entries x 8 bytes. 8 PTs cover 8*512*4KB = 16MB. */
    mov $boot_pt0, %edi
    sub $KERNEL_VIRT_BASE, %edi
    xor %esi, %esi              /* physical address = 0 */
    mov $(512*8), %ecx          /* 8 PTs x 512 entries = 4096 entries */
1:
    mov %esi, %eax
    or $0x03, %eax              /* PRESENT | RW */
    mov %eax, (%edi)            /* low 32 bits */
    movl $0, 4(%edi)            /* high 32 bits = 0 */
    add $4096, %esi
    add $8, %edi
    dec %ecx
    jnz 1b

    /* --- Set up PD[0]: identity map entries 0..7 -> boot_pt0..boot_pt7 --- */
    mov $boot_pd0, %edi
    sub $KERNEL_VIRT_BASE, %edi
    mov $boot_pt0, %edx
    sub $KERNEL_VIRT_BASE, %edx
    mov $8, %ecx
2:
    mov %edx, %eax
    or $0x03, %eax              /* PRESENT | RW */
    mov %eax, (%edi)
    movl $0, 4(%edi)
    add $4096, %edx
    add $8, %edi
    dec %ecx
    jnz 2b

    /* --- Set up PD[3]: higher-half entries 0..7 -> boot_pt0..boot_pt7 --- */
    mov $boot_pd3, %edi
    sub $KERNEL_VIRT_BASE, %edi
    mov $boot_pt0, %edx
    sub $KERNEL_VIRT_BASE, %edx
    mov $8, %ecx
3:
    mov %edx, %eax
    or $0x03, %eax              /* PRESENT | RW */
    mov %eax, (%edi)
    movl $0, 4(%edi)
    add $4096, %edx
    add $8, %edi
    dec %ecx
    jnz 3b

    /* --- Recursive mapping: PD[3][508..511] -> PD[0..3] --- */
    mov $boot_pd3, %edi
    sub $KERNEL_VIRT_BASE, %edi
    /* entry 508 = offset 508*8 = 4064 */
    add $4064, %edi

    mov $boot_pd0, %edx
    sub $KERNEL_VIRT_BASE, %edx
    mov %edx, %eax
    or $0x03, %eax
    mov %eax, (%edi)            /* PD[3][508] -> PD[0] */
    movl $0, 4(%edi)

    mov $boot_pd1, %edx
    sub $KERNEL_VIRT_BASE, %edx
    mov %edx, %eax
    or $0x03, %eax
    mov %eax, 8(%edi)           /* PD[3][509] -> PD[1] */
    movl $0, 12(%edi)

    mov $boot_pd2, %edx
    sub $KERNEL_VIRT_BASE, %edx
    mov %edx, %eax
    or $0x03, %eax
    mov %eax, 16(%edi)          /* PD[3][510] -> PD[2] */
    movl $0, 20(%edi)

    mov $boot_pd3, %edx
    sub $KERNEL_VIRT_BASE, %edx
    mov %edx, %eax
    or $0x03, %eax
    mov %eax, 24(%edi)          /* PD[3][511] -> PD[3] */
    movl $0, 28(%edi)

    /* --- Set up PDPT: 4 entries -> PD[0..3] --- */
    mov $boot_pdpt, %edi
    sub $KERNEL_VIRT_BASE, %edi

    mov $boot_pd0, %edx
    sub $KERNEL_VIRT_BASE, %edx
    mov %edx, %eax
    or $0x01, %eax              /* PRESENT only (PDPT entries have limited flags) */
    mov %eax, (%edi)
    movl $0, 4(%edi)

    mov $boot_pd1, %edx
    sub $KERNEL_VIRT_BASE, %edx
    mov %edx, %eax
    or $0x01, %eax
    mov %eax, 8(%edi)
    movl $0, 12(%edi)

    mov $boot_pd2, %edx
    sub $KERNEL_VIRT_BASE, %edx
    mov %edx, %eax
    or $0x01, %eax
    mov %eax, 16(%edi)
    movl $0, 20(%edi)

    mov $boot_pd3, %edx
    sub $KERNEL_VIRT_BASE, %edx
    mov %edx, %eax
    or $0x01, %eax
    mov %eax, 24(%edi)
    movl $0, 28(%edi)

    /* --- Enable PAE in CR4 --- */
    mov %cr4, %ecx
    or $0x20, %ecx              /* CR4.PAE = bit 5 */
    mov %ecx, %cr4

    /* --- Load CR3 with PDPT physical address --- */
    mov $boot_pdpt, %ecx
    sub $KERNEL_VIRT_BASE, %ecx
    mov %ecx, %cr3

    /* --- Enable Paging (Set PG bit in CR0) --- */
    mov %cr0, %ecx
    or $0x80000000, %ecx
    mov %ecx, %cr0

    /* 
     * PAGING IS ON!
     * We are still executing low code (thanks to identity map).
     * Now jump to the higher half!
     */
    lea higher_half_start, %ecx
    jmp *%ecx

/** From here, we switch to the default .text section.
 * The linker will place this in the 3GB area.
 */
.section .text 
higher_half_start:
    /* 
     * We are now running at 0xC0xxxxxx.
     * We can safely remove the identity mapping (index 0) if we want,
     * but let's leave it for C code to clean up.
     */

    /* Update Stack Pointer to be Virtual */
    /* (Currently ESP points to physical, which is valid mapped, 
     *  but let's fix it to use the virtual range) */
    add $KERNEL_VIRT_BASE, %esp

    /* Restore Multiboot args (popped from stack) */
    pop %ebx /* EBX now has the Multiboot Info address */
    pop %eax /* EAX now has the Magic Number */

    /* Build arch_boot_args (in .bss) and call arch_early_setup(args) */
    mov $arch_boot_args, %ecx
    mov %eax, 0(%ecx)          /* args->a0 = multiboot magic */
    mov %ebx, 4(%ecx)          /* args->a1 = multiboot info phys */
    movl $0, 8(%ecx)           /* args->a2 = 0 */
    movl $0, 12(%ecx)          /* args->a3 = 0 */

    push %ecx
    call arch_early_setup
    add $4, %esp

    /* Hang */
    cli
1:  hlt
    jmp 1b

/* Global PAE Paging Structures (Pre-allocated in BSS) */
.section .bss
.align 32
.global boot_pdpt
boot_pdpt:
    .skip 32

.align 4096
.global boot_pd0
boot_pd0:
    .skip 4096
.global boot_pd1
boot_pd1:
    .skip 4096
.global boot_pd2
boot_pd2:
    .skip 4096
.global boot_pd3
boot_pd3:
    .skip 4096

.global boot_pt0
boot_pt0:
    .skip 4096*8

.align 16
.global arch_boot_args
arch_boot_args:
    .skip 16

.align 16
stack_bottom:
    .skip 16384
.global stack_top
stack_top:

.section .note.GNU-stack,"",@progbits

/* Helper symbol for map loop limit */
_kernel_physical_end: