;------------------------------------------------------------------------------ ; ; Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.
; 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. ; ; Module Name: ; ; MpFuncs.nasm ; ; Abstract: ; ; This is the assembly code for MP support ; ;------------------------------------------------------------------------------- %include "MpEqu.inc" extern ASM_PFX(InitializeFloatingPointUnits) DEFAULT REL SECTION .text ;------------------------------------------------------------------------------------- ;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. ;------------------------------------------------------------------------------------- global ASM_PFX(RendezvousFunnelProc) ASM_PFX(RendezvousFunnelProc): RendezvousFunnelProcStart: ; At this point CS = 0x(vv00) and ip= 0x0. ; Save BIST information to ebp firstly BITS 16 mov ebp, eax ; Save BIST information mov ax, cs mov ds, ax mov es, ax mov ss, ax xor ax, ax mov fs, ax mov gs, ax mov si, BufferStartLocation mov ebx, [si] mov di, ModeOffsetLocation mov eax, [di] mov di, CodeSegmentLocation mov edx, [di] mov di, ax sub di, 02h mov [di],dx ; Patch long mode CS sub di, 04h add eax, ebx mov [di],eax ; Patch address mov si, GdtrLocation o32 lgdt [cs:si] mov si, IdtrLocation o32 lidt [cs:si] mov si, EnableExecuteDisableLocation cmp byte [si], 0 jz SkipEnableExecuteDisableBit ; ; Enable execute disable bit ; mov ecx, 0c0000080h ; EFER MSR number rdmsr ; Read EFER bts eax, 11 ; Enable Execute Disable Bit wrmsr ; Write EFER SkipEnableExecuteDisableBit: mov di, DataSegmentLocation mov edi, [di] ; Save long mode DS in edi mov si, Cr3Location ; Save CR3 in ecx mov ecx, [si] xor ax, ax mov ds, ax ; Clear data segment mov eax, cr0 ; Get control register 0 or eax, 000000003h ; Set PE bit (bit #0) & MP mov cr0, eax mov eax, cr4 bts eax, 5 mov cr4, eax mov cr3, ecx ; Load CR3 mov ecx, 0c0000080h ; EFER MSR number rdmsr ; Read EFER bts eax, 8 ; Set LME=1 wrmsr ; Write EFER mov eax, cr0 ; Read CR0 bts eax, 31 ; Set PG=1 mov cr0, eax ; Write CR0 jmp 0:strict dword 0 ; far jump to long mode BITS 64 LongModeStart: mov eax, edi mov ds, ax mov es, ax mov ss, ax mov esi, ebx lea edi, [esi + InitFlagLocation] cmp qword [edi], 1 ; ApInitConfig jnz GetApicId ; AP init mov edi, esi add edi, LockLocation mov rax, NotVacantFlag TestLock: xchg qword [edi], rax cmp rax, NotVacantFlag jz TestLock lea ecx, [esi + NumApsExecutingLocation] inc dword [ecx] mov ebx, [ecx] Releaselock: mov rax, VacantFlag xchg qword [edi], rax ; program stack mov edi, esi add edi, StackSizeLocation mov eax, dword [edi] mov ecx, ebx inc ecx mul ecx ; EAX = StackSize * (CpuNumber + 1) mov edi, esi add edi, StackStartAddressLocation add rax, qword [edi] mov rsp, rax jmp CProcedureInvoke GetApicId: mov eax, 0 cpuid cmp eax, 0bh jb NoX2Apic ; CPUID level below CPUID_EXTENDED_TOPOLOGY mov eax, 0bh xor ecx, ecx cpuid test ebx, 0ffffh jz NoX2Apic ; CPUID.0BH:EBX[15:0] is zero ; Processor is x2APIC capable; 32-bit x2APIC ID is already in EDX jmp GetProcessorNumber NoX2Apic: ; Processor is not x2APIC capable, so get 8-bit APIC ID mov eax, 1 cpuid shr ebx, 24 mov edx, ebx GetProcessorNumber: ; ; Get processor number for this AP ; Note that BSP may become an AP due to SwitchBsp() ; xor ebx, ebx lea eax, [esi + CpuInfoLocation] mov edi, [eax] GetNextProcNumber: cmp dword [edi], edx ; APIC ID match? jz ProgramStack add edi, 20 inc ebx jmp GetNextProcNumber ProgramStack: mov rsp, qword [edi + 12] CProcedureInvoke: push rbp ; Push BIST data at top of AP stack xor rbp, rbp ; Clear ebp for call stack trace push rbp mov rbp, rsp mov rax, qword [esi + InitializeFloatingPointUnitsAddress] sub rsp, 20h call rax ; Call assembly function to initialize FPU per UEFI spec add rsp, 20h mov edx, ebx ; edx is NumApsExecuting mov ecx, esi add ecx, LockLocation ; rcx is address of exchange info data buffer mov edi, esi add edi, ApProcedureLocation mov rax, qword [edi] sub rsp, 20h call rax ; Invoke C function add rsp, 20h jmp $ ; Should never reach here RendezvousFunnelProcEnd: ;------------------------------------------------------------------------------------- ; AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment, TopOfApStack, CountTofinish); ;------------------------------------------------------------------------------------- global ASM_PFX(AsmRelocateApLoop) ASM_PFX(AsmRelocateApLoop): AsmRelocateApLoopStart: mov rax, [rsp + 40] ; CountTofinish lock dec dword [rax] ; (*CountTofinish)-- mov rsp, r9 push rcx push rdx lea rsi, [PmEntry] ; rsi <- The start address of transition code push r8 push rsi DB 0x48 retf BITS 32 PmEntry: mov eax, cr0 btr eax, 31 ; Clear CR0.PG mov cr0, eax ; Disable paging and caches mov ebx, edx ; Save EntryPoint to rbx, for rdmsr will overwrite rdx mov ecx, 0xc0000080 rdmsr and ah, ~ 1 ; Clear LME wrmsr mov eax, cr4 and al, ~ (1 << 5) ; Clear PAE mov cr4, eax pop edx add esp, 4 pop ecx, add esp, 4 cmp cl, 1 ; Check mwait-monitor support jnz HltLoop mov ebx, edx ; Save C-State to ebx MwaitLoop: mov eax, esp ; Set Monitor Address xor ecx, ecx ; ecx = 0 xor edx, edx ; edx = 0 monitor mov eax, ebx ; Mwait Cx, Target C-State per eax[7:4] shl eax, 4 mwait jmp MwaitLoop HltLoop: cli hlt jmp HltLoop BITS 64 AsmRelocateApLoopEnd: ;------------------------------------------------------------------------------------- ; AsmGetAddressMap (&AddressMap); ;------------------------------------------------------------------------------------- global ASM_PFX(AsmGetAddressMap) ASM_PFX(AsmGetAddressMap): lea rax, [ASM_PFX(RendezvousFunnelProc)] mov qword [rcx], rax mov qword [rcx + 8h], LongModeStart - RendezvousFunnelProcStart mov qword [rcx + 10h], RendezvousFunnelProcEnd - RendezvousFunnelProcStart lea rax, [ASM_PFX(AsmRelocateApLoop)] mov qword [rcx + 18h], rax mov qword [rcx + 20h], AsmRelocateApLoopEnd - AsmRelocateApLoopStart ret ;------------------------------------------------------------------------------------- ;AsmExchangeRole procedure follows. This procedure executed by current BSP, that is ;about to become an AP. It switches its stack with the current AP. ;AsmExchangeRole (IN CPU_EXCHANGE_INFO *MyInfo, IN CPU_EXCHANGE_INFO *OthersInfo); ;------------------------------------------------------------------------------------- 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 function, stack will be corrupted. push rax push rbx push rcx push rdx push rsi push rdi push rbp push r8 push r9 push r10 push r11 push r12 push r13 push r14 push r15 mov rax, cr0 push rax mov rax, cr4 push rax ; rsi contains MyInfo pointer mov rsi, rcx ; rdi contains OthersInfo pointer mov rdi, rdx ;Store EFLAGS, GDTR and IDTR regiter to stack pushfq sgdt [rsi + 16] sidt [rsi + 26] ; Store the its StackPointer mov [rsi + 8], rsp ; update its switch state to STORED mov byte [rsi], CPU_SWITCH_STATE_STORED WaitForOtherStored: ; wait until the other CPU finish storing its state cmp byte [rdi], CPU_SWITCH_STATE_STORED jz OtherStored pause jmp WaitForOtherStored OtherStored: ; Since another CPU already stored its state, load them ; load GDTR value lgdt [rdi + 16] ; load IDTR value lidt [rdi + 26] ; load its future StackPointer mov rsp, [rdi + 8] ; update the other CPU's switch state to LOADED mov byte [rdi], CPU_SWITCH_STATE_LOADED WaitForOtherLoaded: ; wait until the other CPU finish loading new state, ; otherwise the data in stack may corrupt cmp byte [rsi], CPU_SWITCH_STATE_LOADED jz OtherLoaded pause jmp WaitForOtherLoaded OtherLoaded: ; since the other CPU already get the data it want, leave this procedure popfq pop rax mov cr4, rax pop rax mov cr0, rax pop r15 pop r14 pop r13 pop r12 pop r11 pop r10 pop r9 pop r8 pop rbp pop rdi pop rsi pop rdx pop rcx pop rbx pop rax ret