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