.section .text .global _start .type _start, @function _start: cli mov $_stack_top, %esp push %ebx // save the address of the multiboot struct mov $0x80000000, %eax // check CPUID extended functions cpuid cmp $0x80000001, %eax jb panic_early mov $0x80000001, %eax cpuid test $(1<<29), %edx // check long mode support jz panic_early mov %cr4, %eax or $(1<<5 | 1<<9 | 1<<10), %eax // PAE | SSE | SSE mov %eax, %cr4 call pml4_identity_init mov $pml4_identity, %eax mov %eax, %cr3 mov $0xC0000080, %ecx // EFER MSR rdmsr or $(1 | 1<<8 | 1<<11), %eax // syscall/ret | long mode | NX wrmsr mov %cr0, %eax or $0x80000002, %eax // enable paging, coprocessor monitoring and $(~4), %eax // disable coprocessor emulation mov %eax, %cr0 /* fill out the TSS's address */ movw $TSS, (GdtTss + 2) lgdt (GdtPointer) pop %edi mov $(2 << 3), %eax // SEG_r0data mov %eax, %ds mov %eax, %ss mov %eax, %es mov %eax, %fs mov %eax, %gs ljmp $(1 << 3), $boot64 // SEG_r0code panic_early: // output a vga Fuck movl $0x4F754F46, 0xB872A movl $0x4F6B4F63, 0xB872E jmp cpu_halt .global cpu_shutdown .type cpu_shutdown, @function cpu_shutdown: /* This quits QEMU. While I couldn't find this officially documented anywhere, * it is used by QEMU in tests/tcg/i386/system/boot.S (as of commit 40d6ee), so * I assume that this is safe-ish to use */ mov $0x604, %edx mov $0x2000, %eax outw %ax, %dx .global cpu_halt .type cpu_halt, @function cpu_halt: cli 1: hlt jmp 1b .global cpu_pause .type cpu_pause, @function cpu_pause: sti hlt cli ret .section .shared .global GDT .align 8 GDT: #define GdtLimit(p) (p & 0xFFFF) | ((p >> 16) << 48) /* doesn't check if p fits in 20 bits */ #define GdtAccessed (1<<40) #define GdtReadWrite (1<<41) #define GdtCode (1<<43) #define GdtCodeOrData (1<<44) #define GdtRing(r) (r<<45) #define GdtPresent (1<<47) #define GdtAvailable (1<<52) #define GdtLongMode (1<<53) #define GdtPageGran (1<<55) /* limit is in pages */ #define GdtCommon (GdtLimit(0xFFFFF) | GdtPageGran | GdtAccessed | \ GdtReadWrite | GdtCodeOrData | GdtPresent | GdtAvailable) .8byte 0 /* [0] = SEG_null */ .8byte GdtCommon | GdtRing(0) | GdtLongMode | GdtCode /* [1] = SEG_r0code */ .8byte GdtCommon | GdtRing(0) | GdtLongMode /* [2] = SEG_r0data */ .8byte GdtCommon | GdtRing(3) | GdtCode /* [3] = SEG_r3code32 */ .8byte GdtCommon | GdtRing(3) | GdtLongMode /* [4] = SEG_r3data */ .8byte GdtCommon | GdtRing(3) | GdtLongMode | GdtCode /* [5] = SEG_r3code */ GdtTss: .8byte GdtLimit(104) | (9<<40) | GdtPresent | GdtAvailable /* [6] = SEG_TSS */ .8byte 0 /* [7] = SEG_TSS2 */ .section .data .global GdtPointer GdtPointer: .2byte 63 /* size of the GDT - 1 */ .4byte GDT /* address of the GDT */