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
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
|
## @file
# This is the assembly code for MP (Multiple-processor) support.
#
# Copyright (c) 1999 - 2015, Intel Corporation. All rights reserved.<BR>
#
# This program and the accompanying materials
# are licensed and made available under the terms and conditions of the BSD License
# which accompanies this distribution. The full text of the license may be found at
# http://opensource.org/licenses/bsd-license.php.
#
# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#
##
##include Htequ.inc
.equ VacantFlag , 0x00
.equ NotVacantFlag , 0xff
.equ StartupApSignal , 0x6E750000
.equ MonitorFilterSize, 0x10
.equ ApCounterInit , 0
.equ ApInHltLoop , 1
.equ ApInMwaitLoop , 2
.equ ApInRunLoop , 3
.equ LockLocation , 0x1000 - 0x0400
.equ StackStart , LockLocation + 0x4
.equ StackSize , LockLocation + 0x8
.equ RendezvousProc , LockLocation + 0x0C
.equ GdtrProfile , LockLocation + 0x10
.equ IdtrProfile , LockLocation + 0x16
.equ BufferStart , LockLocation + 0x1C
.equ Cr3Location , LockLocation + 0x20
.equ InitFlag , LockLocation + 0x24
.equ WakeUpApManner , LockLocation + 0x28
.equ BistBuffer , LockLocation + 0x2C
#-------------------------------------------------------------------------------------
.macro PAUSE32
.byte 0xF3
.byte 0x90
.endm
.macro FJMP32 Selector, Offset
.byte 0x066
.byte 0x067
.byte 0x0EA # far jump
.long \Offset # 32-bit offset
.word \Selector # 16-bit selector
.endm
.macro FCALL32 Selector, Offset
.byte 0x09A
.long \Offset # 32-bit offset
.word \Selector # 16-bit selector
.endm
#-------------------------------------------------------------------------------------
#RendezvousFunnelProc procedure follows. All APs execute their procedure. This
#procedure serializes all the AP processors through an Init sequence. It must be
#noted that APs arrive here very raw...ie: real mode, no stack.
#ALSO THIS PROCEDURE IS EXECUTED BY APs ONLY ON 16 BIT MODE. HENCE THIS PROC
#IS IN MACHINE CODE.
#-------------------------------------------------------------------------------------
#RendezvousFunnelProc (&WakeUpBuffer,MemAddress)
ASM_GLOBAL ASM_PFX(RendezvousFunnelProc)
ASM_PFX(RendezvousFunnelProc):
RendezvousFunnelProcStart:
# At this point CS = 0x(vv00) and ip= 0x0.
.byte 0x66,0x8b,0xe8 # mov ebp, eax
.byte 0x8c,0xc8 # mov ax, cs
.byte 0x8e,0xd8 # mov ds, ax
.byte 0x8e,0xc0 # mov es, ax
.byte 0x8e,0xd0 # mov ss, ax
.byte 0x33,0xc0 # xor ax, ax
.byte 0x8e,0xe0 # mov fs, ax
.byte 0x8e,0xe8 # mov gs, ax
# Get APIC ID
#
.byte 0x66, 0xB8
.long 0x00000001 # mov %eax, 1
.byte 0x0F, 0xA2 # cpuid
.byte 0x66, 0xC1, 0xEB, 0x18 # shr %ebx, 24
.byte 0x66, 0x81, 0xE3
.long 0x000000FF # and %ebx, 0ffh # EBX is keeping APIC ID
# If it is the first time AP wakes up, just record AP's BIST
# Otherwise, switch to flat mode
.byte 0xBE, 0x24, 0x0C # mov si, InitFlag
.byte 0x66, 0x83, 0x3C, 0x01 # cmp dword ptr [si], 1
.byte 0x75, 0x18 # jnz flat32Start
# Record BIST information
#
.byte 0xB0, 0x08 # mov al, 8
.byte 0xF6, 0xE3 # mul bl
.byte 0xBE, 0x2C, 0x0C # mov si, BistBuffer
.byte 0x03, 0xF0 # add si, ax
.byte 0x66, 0xC7, 0x04
.byte 0x00000001 # mov dword ptr [si], 1 # Set Valid Flag
.byte 0x66, 0x89, 0x6C, 0x04 # mov dword ptr [si + 4], ebp # Store BIST value
cli
hlt
jmp .-2
# Switch to flat mode.
flat32Start:
.byte 0xBE, 0x1C, 0x0C # mov si, BufferStart
.byte 0x66, 0x8B, 0x0C # mov ecx,dword ptr [si] # ECX is keeping the start address of wakeup buffer
.byte 0xFA # cli
.byte 0xBE, 0x10, 0x0C # mov si, GdtrProfile
.byte 0x66 # db 66h
.byte 0x2E, 0x0F, 0x01, 0x14 # lgdt fword ptr cs:[si]
.byte 0xBE, 0x16, 0x0C # mov si, IdtrProfile
.byte 0x66 # db 66h
.byte 0x2E, 0x0F, 0x01, 0x1C # lidt fword ptr cs:[si]
.byte 0x33, 0xC0 # xor ax, ax
.byte 0x8E, 0xD8 # mov ds, ax
.byte 0x0F, 0x20, 0xC0 # mov eax, cr0 # Get control register 0
.byte 0x66, 0x83, 0xC8, 0x01 # or eax, 000000001h # Set PE bit (bit #0)
.byte 0x0F, 0x22, 0xC0 # mov cr0, eax
#step-4:
FLAT32_JUMP:
FJMP32 0x010, 0x0 # Far jmp using code segment descriptor
PMODE_ENTRY: # protected mode entry point
movw $0x8,%ax
movw %ax,%ds
movw %ax,%es
movw %ax,%fs
movw %ax,%gs
movw %ax,%ss # Flat mode setup.
movl %ecx,%esi
movl %esi,%edi
addl $InitFlag, %edi
cmpl $2, (%edi) # Check whether in S3 boot path
jz ProgramDynamicStack
ProgramStaticStack:
xorl %ecx, %ecx
movl %esi, %edi
addl $BistBuffer, %edi
movl (%edi, %ebx, 8), %ecx # EBX = CpuNumber
movl %esi, %edi
addl $StackSize, %edi
movl (%edi), %eax
incl %ecx
mull %ecx # EAX = StackSize * (CpuNumber + 1)
movl %esi, %edi
addl $StackStart, %edi
movl (%edi), %edx
addl %edx, %eax # EAX = StackStart + StackSize * (CpuNumber + 1)
movl %eax, %esp
subl $MonitorFilterSize, %esp # Reserved Monitor data space
orl $StartupApSignal, %ebx # EBX = #Cpu run signature
jmp ProgramLocalApic
ProgramDynamicStack:
movl %esi, %edi
addl $LockLocation, %edi
movb $NotVacantFlag, %al
TestLock:
xchgb %al, (%edi)
cmpb $NotVacantFlag, %al
jz TestLock
movl %esi, %edi
addl $StackSize, %edi
movl (%edi), %eax
movl %esi, %edi
addl $StackStart, %edi
addl (%edi), %eax
movl %eax, %esp
movl %eax, (%edi)
Releaselock:
movb $VacantFlag, %al
movl %esi, %edi
addl $LockLocation, %edi
xchgb %al, (%edi)
ProgramLocalApic:
movl $0x0FEE000F0, %edi
movl (%edi), %eax
andl $0x0FFFFFD0F, %eax
orl $0x10F, %eax
movl %eax, (%edi)
movl $0x0FEE00350, %edi
movl (%edi), %eax
andl $0x0FFFE00FF, %eax
orl $0x700, %eax
movl %eax, (%edi)
movl $0x0FEE00360, %edi
movl (%edi), %eax
andl $0x0FFFE00FF, %eax
orl $0x10400, %eax
movl %eax, (%edi)
EnableXmm:
movl $1, %eax
cpuid
btl $0x1A, %edx
jnc L1
#
# Enable XMM
#
movl %cr0, %eax
orl $2, %eax
movl %eax, %cr0
movl %cr4, %eax
orl $0x600, %eax
movl %eax, %cr4
L1:
#
# Call C Function
#
movl %esi, %edi
addl $RendezvousProc, %edi
addl $WakeUpApManner, %esi # esi = WakeUpApManner Address Location
WakeUpThisAp:
movl (%edi), %eax
testl %eax, %eax
jz CheckWakeUpCounterInit
push %ebx
push %ebx
push %esi
push %edi
subl $0x20, %esp
call *%eax # Call C function
addl $0x20, %esp
pop %edi
pop %esi
pop %ebx
pop %ebx
CheckWakeUpCounterInit:
cmpl $ApCounterInit, (%esi)
jnz CheckWakeUpManner
#
# Initi%alize MONITOR_MWAIT_DATA data structure per thread
#
xorl %ecx, %ecx
movl %ecx, 0(%esp) # BreakToRunApSignal
movl %ecx, 4(%esp) # HltLoopBreakCounter
movl %ecx, 8(%esp) # MwaitLoopBreakCounter
movl %ecx, 12(%esp) # RunLoopBreakCounter
movl %ecx, 16(%esp) # WakeUpApVectorChangeFlag
movl %ecx, 20(%esp) # MwaitTargetCstate
CheckWakeUpManner:
cmpl $ApInHltLoop, (%esi)
jz HltApLoop
cmpl $ApInMwaitLoop, (%esi)
jnz CheckRunSignal
ApMwaitLoop:
cli
movl %esp, %eax # Set Monitor Address
xorl %ecx, %ecx
xorl %edx, %edx
.byte 0x0f, 0x1, 0xc8 # MONITOR
movl 20(%esp), %eax # Mwait Target C-State per rax[7:4]
.byte 0x0f, 0x1, 0xc9 # MWAIT
CheckRunSignal:
cmpl %ebx, (%esp) # Check if run sign%al correct?
jnz CheckWakeUpManner # Unknown break, go checking run manner
jmp WakeUpThisAp # Jmp to execute AP task
HltApLoop:
cli
hlt
jmp HltApLoop
#RendezvousFunnelProc ENDP
RendezvousFunnelProcEnd:
#-------------------------------------------------------------------------------------
# AsmGetAddressMap (&AddressMap)
#-------------------------------------------------------------------------------------
ASM_GLOBAL ASM_PFX(AsmGetAddressMap)
ASM_PFX(AsmGetAddressMap):
pushal
movl %esp, %ebp
movl 0x24(%ebp), %ebx
movl $RendezvousFunnelProcStart, (%ebx)
movl $(PMODE_ENTRY - RendezvousFunnelProcStart), 0x4(%ebx)
movl $(FLAT32_JUMP - RendezvousFunnelProcStart), 0x8(%ebx)
movl $(RendezvousFunnelProcEnd - RendezvousFunnelProcStart), 0xc(%ebx)
popal
ret
#AsmGetAddressMap ENDP
#-------------------------------------------------------------------------------------
#AsmExchangeRole procedure follows. This procedure executed by current BSP, that is
#about to become an AP. It switches it'stack with the current AP.
#AsmExchangeRole (IN CPU_EXCHANGE_INFO *MyInfo, IN CPU_EXCHANGE_INFO *OthersInfo)#
#-------------------------------------------------------------------------------------
.equ CPU_SWITCH_STATE_IDLE, 0
.equ CPU_SWITCH_STATE_STORED, 1
.equ CPU_SWITCH_STATE_LOADED, 2
ASM_GLOBAL ASM_PFX(AsmExchangeRole)
ASM_PFX(AsmExchangeRole):
# DO NOT call other functions in this function, since 2 CPU may use 1 stack
# at the same time. If 1 CPU try to call a functiosn, stack will be corrupted.
pushal
movl %esp, %ebp
# %esi contains MyInfo pointer
movl 0x24(%ebp), %esi
# %edi contains OthersInfo pointer
movl 0x28(%ebp), %edi
#Store EFLAGS, GDTR and IDTR regiter to stack
pushfl
sgdt 8(%esi)
sidt 14(%esi)
# Store the its StackPointer
movl %esp, 4(%esi)
# update its switch state to STORED
movb $NotVacantFlag, %al
TryLock1:
lock xchgb (%esi), %al
cmpb $VacantFlag, %al
jz LockObtained1
PAUSE32
jmp TryLock1
LockObtained1:
movb $CPU_SWITCH_STATE_STORED, 1(%esi)
lock xchgb (%esi), %al
WaitForOtherStored:
# wait until the other CPU finish storing its state
movb $NotVacantFlag, %al
TryLock2:
lock xchgb (%edi), %al
cmpb $VacantFlag, %al
jz LockObtained2
PAUSE32
jmp TryLock2
LockObtained2:
movb 1(%edi), %bl
lock xchgb (%edi), %al
cmpb $CPU_SWITCH_STATE_STORED, %bl
jb WaitForOtherStored
# Since another CPU already stored its state, load them
# load GDTR value
lgdt 8(%edi)
# load IDTR value
lidt 14(%edi)
# load its future StackPointer
movl 4(%edi), %esp
# update its switch state to LOADED
movb $NotVacantFlag, %al
TryLock3:
lock xchgb (%esi), %al
cmpb $VacantFlag, %al
jz LockObtained3
PAUSE32
jmp TryLock3
LockObtained3:
movb $CPU_SWITCH_STATE_LOADED, 1(%esi)
lock xchgb (%esi), %al
WaitForOtherLoaded:
# wait until the other CPU finish loading new state,
# otherwise the data in stack may corrupt
movb $NotVacantFlag, %al
TryLock4:
lock xchgb (%edi), %al
cmpb $VacantFlag, %al
jz LockObtained4
PAUSE32
jmp TryLock4
LockObtained4:
movb 1(%edi), %bl
lock xchgb (%edi), %al
cmpb $CPU_SWITCH_STATE_LOADED, %bl
jb WaitForOtherLoaded
# since the other CPU already get the data it want, leave this procedure
popfl
popal
ret
#AsmExchangeRole ENDP
|