forked from SerenityOS/serenity
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ap_setup.S
234 lines (203 loc) Β· 6.22 KB
/
ap_setup.S
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
#include <Kernel/Sections.h>
.section .text
.global gdt64ptr
gdt64ptr:
#if ARCH(X86_64)
.quad 0
#else
.int 0
#endif
.global code64_sel
code64_sel:
.short 0
/*
The apic_ap_start function will be loaded to P0x00008000 where the APIC
will boot the AP from in real mode. This code also contains space for
special variables that *must* remain here. When initializing the APIC,
the code here gets copied to P0x00008000, the variables in here get
populated and then the the boot of the APs will be triggered. Having
the variables here allows us to access them from real mode. Also, the
code here avoids the need for relocation entries.
Basically, the variables between apic_ap_start and end_apic_ap_start
*MUST* remain here and cannot be moved into a .bss or any other location.
*/
.global apic_ap_start
.type apic_ap_start, @function
.align 8
apic_ap_start:
.code16
cli
jmp $0x800, $(1f - apic_ap_start) /* avoid relocation entries */
1:
mov %cs, %ax
mov %ax, %ds
xor %ax, %ax
mov %ax, %sp
/* load the first temporary gdt */
lgdt (ap_cpu_gdtr_initial - apic_ap_start)
/* enable PM */
movl %cr0, %eax
orl $1, %eax
movl %eax, %cr0
ljmpl $8, $(apic_ap_start32 - apic_ap_start + 0x8000)
apic_ap_start32:
.code32
mov $0x10, %ax
mov %ax, %ss
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
movl $0x8000, %ebp
/* generate a unique ap cpu id (0 means 1st ap, not bsp!) */
xorl %eax, %eax
incl %eax
lock; xaddl %eax, (ap_cpu_id - apic_ap_start)(%ebp) /* avoid relocation entries */
movl %eax, %esi
/* check if we support NX and enable it if we do */
movl $0x80000001, %eax
cpuid
testl $0x100000, %edx
je (1f - apic_ap_start + 0x8000)
/* turn on IA32_EFER.NXE */
movl $0xc0000080, %ecx
rdmsr
orl $0x800, %eax
wrmsr
1:
/* load the bsp's cr3 value */
movl (ap_cpu_init_cr3 - apic_ap_start)(%ebp), %eax
movl %eax, %cr3
/* Enter Long-mode! ref(https://wiki.osdev.org/Setting_Up_Long_Mode)*/
mov $0xC0000080, %ecx /* Set the C-register to 0xC0000080, which is the EFER MSR.*/
rdmsr /* Read from the model-specific register.*/
or $(1 << 8), %eax /* Set the LM-bit which is the 9th bit (bit 8).*/
wrmsr /* Write to the model-specific register.*/
/* enable PAE + PSE */
movl %cr4, %eax
orl $0x60, %eax
movl %eax, %cr4
/* enable PG */
movl %cr0, %eax
orl $0x80000000, %eax
movl %eax, %cr0
/* load the temporary 64-bit gdt from boot that points above 3GB */
lgdt (ap_cpu_gdt64ptr - apic_ap_start + 0x8000)
/* Jump into our identity mapped area, stay in low memory for now.
We need to fully enable 64 bit mode before we can adjust rsp and rip
to values higher than 4GB */
ljmpl $(ap_cpu_gdt64code - ap_cpu_gdt64), $(apic_ap_start64 - apic_ap_start + 0x8000)
.code64
apic_ap_start64:
movq (ap_cpu_kernel_map_base - apic_ap_start)(%rbp), %rbp
addq $0x8000, %rbp
/* find our allocated stack based on the generated id */
movq (ap_cpu_init_stacks - apic_ap_start)(%rbp, %rsi, 8), %rsp
/* Now jump from our identity mapped area into high memory */
movq $(1f - apic_ap_start), %rax
addq %rbp, %rax
jmp *%rax
1:
mov $0, %ax
mov %ax, %ss
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
/* flush the TLB */
movq %cr3, %rax
movq %rax, %cr3
/* now load the final gdt and idt from the identity mapped area */
movq (ap_cpu_gdtr - apic_ap_start)(%rbp), %rax
lgdt (%rax)
movq (ap_cpu_idtr - apic_ap_start)(%rbp), %rax
lidt (%rax)
/* set same cr0 and cr4 values as the BSP */
movq (ap_cpu_init_cr0 - apic_ap_start)(%rbp), %rax
movq %rax, %cr0
movq (ap_cpu_init_cr4 - apic_ap_start)(%rbp), %rax
movq %rax, %cr4
/* Save the cpu id into rdi (first argument), 0 representing the bsp */
movq %rsi, %rdi
incq %rdi
/* Save the Processor pointer this CPU is going to use into rsi (second argument) */
movq (ap_cpu_init_processor_info_array - apic_ap_start)(%rbp), %rax
movq 0(%rax, %rsi, 8), %rsi
/* Get the entry function */
movq (ap_cpu_kernel_entry_function - apic_ap_start)(%rbp), %r10
movq %rsp, %rbp
cld
/* We are in identity mapped P0x8000 and the BSP will unload this code
once all APs are initialized, so call the entry function and return to our
infinite loop if it ever were to return. */
call *%r10
loop:
hlt
jmp loop
.align 4
.global apic_ap_start_size
apic_ap_start_size:
.2byte end_apic_ap_start - apic_ap_start
.align 4
ap_cpu_id:
.4byte 0x0
ap_cpu_gdt:
/* null */
.8byte 0x0
/* code */
.4byte 0x0000FFFF
.4byte 0x00cf9a00
/* data */
.4byte 0x0000FFFF
.4byte 0x00cf9200
ap_cpu_gdt_end:
ap_cpu_gdtr_initial:
.2byte ap_cpu_gdt_end - ap_cpu_gdt - 1
.4byte (ap_cpu_gdt - apic_ap_start) + 0x8000
.align 8
.global ap_cpu_gdtr
ap_cpu_gdtr:
.8byte 0x0 /* will be set at runtime */
.global ap_cpu_idtr
ap_cpu_idtr:
.8byte 0x0 /* will be set at runtime */
.global ap_cpu_init_cr0
ap_cpu_init_cr0:
.8byte 0x0 /* will be set at runtime */
.global ap_cpu_init_cr3
ap_cpu_init_cr3:
.8byte 0x0 /* will be set at runtime */
.global ap_cpu_init_cr4
ap_cpu_init_cr4:
.8byte 0x0 /* will be set at runtime */
.global ap_cpu_gdt64
ap_cpu_gdt64:
.8byte 0x0
.global ap_cpu_gdt64code
ap_cpu_gdt64code:
.4byte 0xffff
.4byte 0xaf9a00
.global ap_cpu_gdt64data
ap_cpu_gdt64data:
.4byte 0xffff
.4byte 0xaf9200
.global ap_cpu_gdt64ptr
ap_cpu_gdt64ptr:
.2byte ap_cpu_gdt64ptr - ap_cpu_gdt64 - 1
.8byte (ap_cpu_gdt64 - apic_ap_start) + 0x8000
.align 8
.global ap_cpu_kernel_entry_function
ap_cpu_kernel_entry_function:
.8byte 0x0 /* will be set at runtime */
.global ap_cpu_kernel_map_base
ap_cpu_kernel_map_base:
.8byte 0x0 /* will be set at runtime */
.global ap_cpu_init_processor_info_array
ap_cpu_init_processor_info_array:
.8byte 0x0 /* will be set at runtime */
.global ap_cpu_init_stacks
ap_cpu_init_stacks:
/* array of allocated stack pointers */
/* NOTE: ap_cpu_init_stacks must be the last variable before
end_apic_ap_start! */
.set end_apic_ap_start, .