1#include <lk/asm.h> 2#include <arch/x86/descriptor.h> 3 4#if WITH_SMP 5 6#define LOAD_ADDRESS 0x4000 7#define MSR_EFER 0xc0000080 8#define EFER_LME 0x00000100 9 10#define ARGS_ADDRESS (LOAD_ADDRESS + 0x1000) 11#define ARGS_CR3 (ARGS_ADDRESS + 0x00) 12#if ARCH_X86_64 13#define ARGS_STACK (ARGS_ADDRESS + 0x08) 14#else 15#define ARGS_STACK (ARGS_ADDRESS + 0x04) 16#endif 17 18.text 19.code16 20// secondary cpu boot entry point and switch to protected mode 21// enters with the following state: 22// real mode, CS 0x0400, PC 0 (physical address 0x4000) 23// LOAD_ADDRESS (physical) == mp_boot_start (virtual) 24FUNCTION(mp_boot_start) 25 // jump over the temp GDT below and switch to a flat memory segment (0) 26 ljmp $0, $(LOAD_ADDRESS + (.Lafter_gdt - mp_boot_start)) 27 28.org 0x8 29.Lgdt: 30 // stuff the GDTR in the first entry 31 .short (8*4) 32 .int (LOAD_ADDRESS + 0x8) // address of .Lgdt 33 .short 0 34 35 // 0x8 code flat 32bit 36 .short 0xffff /* limit 15:00 */ 37 .short 0x0000 /* base 15:00 */ 38 .byte 0x00 /* base 23:16 */ 39 .byte 0b10011010 /* P(1) DPL(00) S(1) 1 C(0) R(1) A(0) */ 40 .byte 0b11001111 /* G(1) D(1) 0 0 limit 19:16 */ 41 .byte 0x0 /* base 31:24 */ 42 43 // 0x10 data flat 32bit 44 .short 0xffff /* limit 15:00 */ 45 .short 0x0000 /* base 15:00 */ 46 .byte 0x00 /* base 23:16 */ 47 .byte 0b10010010 /* P(1) DPL(00) S(1) 0 E(0) W(1) A(0) */ 48 .byte 0b11001111 /* G(1) B(1) 0 0 limit 19:16 */ 49 .byte 0x0 /* base 31:24 */ 50 51 // 0x18 code 64bit 52 .short 0xffff /* limit 15:00 */ 53 .short 0x0000 /* base 15:00 */ 54 .byte 0x00 /* base 23:16 */ 55 .byte 0b10011010 /* P(1) DPL(00) S(1) 1 C(0) R(1) A(0) */ 56 .byte 0b10101111 /* G(1) D(0) L(1) AVL(0) limit 19:16 */ 57 .byte 0x0 /* base 31:24 */ 58 59.Lafter_gdt: 60 // load the above GDT 61 lgdt (LOAD_ADDRESS + 0x08) 62 63 // switch to protected mode 64 movl %cr0, %eax 65 orl $1, %eax 66 movl %eax, %cr0 67 68 // jump to 32bit mode 69 ljmpl $0x8, $(LOAD_ADDRESS + (.Lprot - mp_boot_start)) 70.Lprot: 71 .code32 72 // we're now in 32bit mode, set up the 32bit data segment registers 73 mov $0x10, %ax 74 mov %ax, %ss 75 mov %ax, %ds 76 mov %ax, %es 77 mov %ax, %fs 78 mov %ax, %gs 79 80#if ARCH_X86_64 81 // set up 64bit paging 82 // set PAE bit in CR4 83 mov %cr4, %eax 84 or $(1<<5), %eax 85 mov %eax, %cr4 86 87 // Enable Long mode 88 movl $MSR_EFER ,%ecx 89 rdmsr 90 orl $EFER_LME,%eax 91 wrmsr 92 93 // load trampoline page table 94 movl (ARGS_CR3), %eax 95 mov %eax, %cr3 96 97 // enable paging, now we're in 32bit compatibility mode 98 mov %cr0, %eax 99 btsl $(31), %eax 100 mov %eax, %cr0 101 102 // load a very temporary stack pointer 103 movl $(LOAD_ADDRESS + 0x800), %esp 104 105 // Use a far jump to get into 64bit mode 106 pushl $0x18 107 pushl $(LOAD_ADDRESS + (.Lfarjump64 - mp_boot_start)) 108 lret 109 110.code64 111.Lfarjump64: 112 /* branch to our high address */ 113 movq (.Lhigh_addr), %rax 114 jmp *%rax 115.Lhigh_addr: 116.quad mp_boot_start_high 117 118#else // ARCH_X86_32 119 // set up 32bit paging 120 121 // set PSE bit in CR4 122 mov %cr4, %eax 123 or $(1<<4), %eax 124 mov %eax, %cr4 125 126 // load trampoline page table 127 movl (ARGS_CR3), %eax 128 mov %eax, %cr3 129 130 // enable paging 131 mov %cr0, %eax 132 btsl $(31), %eax 133 mov %eax, %cr0 134 135 // Branch to the high address 136 lea mp_boot_start_high, %eax 137 jmp *%eax 138#endif 139 140DATA(mp_boot_end) 141END_FUNCTION(mp_boot_start) 142 143FUNCTION(mp_boot_start_high) 144#if ARCH_X86_64 145 // set up stack pointer 146 mov (ARGS_STACK), %rsp 147 148 // load the real GDT 149 lgdt _gdtr 150 151 push $CODE_64_SELECTOR 152 lea .Lnext(%rip), %rax 153 push %rax 154 lretq 155.Lnext: 156 // zero out the segment registers 157 xor %ax, %ax 158 mov %ax, %ds 159 mov %ax, %es 160 mov %ax, %fs 161 mov %ax, %gs 162 mov %ax, %ss 163 164 // call into C 165 cld 166 mov $ARGS_ADDRESS, %rdi 167 call secondary_entry 168 jmp . 169 170#else // ARCH_X86_32 171 // set up stack pointer 172 mov (ARGS_STACK), %esp 173 174 // load the real GDT 175 lgdt _gdtr 176 177 push $CODE_SELECTOR 178 lea .Lnext, %eax 179 push %eax 180 lret 181.Lnext: 182 183 // Load the real segment registers 184 mov $DATA_SELECTOR, %ax 185 mov %ax, %ds 186 mov %ax, %es 187 mov %ax, %fs 188 mov %ax, %gs 189 mov %ax, %ss 190 191 // call into C 192 cld 193 push $ARGS_ADDRESS 194 call secondary_entry 195 jmp . 196 197#endif 198END_FUNCTION(mp_boot_start_high) 199 200#endif // WITH_SMP