1 /** @file 2 Code for Processor S3 restoration 3 4 Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR> 5 This program and the accompanying materials 6 are licensed and made available under the terms and conditions of the BSD License 7 which accompanies this distribution. The full text of the license may be found at 8 http://opensource.org/licenses/bsd-license.php 9 10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 12 13 **/ 14 15 #include "PiSmmCpuDxeSmm.h" 16 17 typedef struct { 18 UINTN Lock; 19 VOID *StackStart; 20 UINTN StackSize; 21 VOID *ApFunction; 22 IA32_DESCRIPTOR GdtrProfile; 23 IA32_DESCRIPTOR IdtrProfile; 24 UINT32 BufferStart; 25 UINT32 Cr3; 26 } MP_CPU_EXCHANGE_INFO; 27 28 typedef struct { 29 UINT8 *RendezvousFunnelAddress; 30 UINTN PModeEntryOffset; 31 UINTN FlatJumpOffset; 32 UINTN Size; 33 UINTN LModeEntryOffset; 34 UINTN LongJumpOffset; 35 } MP_ASSEMBLY_ADDRESS_MAP; 36 37 /** 38 Get starting address and size of the rendezvous entry for APs. 39 Information for fixing a jump instruction in the code is also returned. 40 41 @param AddressMap Output buffer for address map information. 42 **/ 43 VOID * 44 EFIAPI 45 AsmGetAddressMap ( 46 MP_ASSEMBLY_ADDRESS_MAP *AddressMap 47 ); 48 49 #define LEGACY_REGION_SIZE (2 * 0x1000) 50 #define LEGACY_REGION_BASE (0xA0000 - LEGACY_REGION_SIZE) 51 #define MSR_SPIN_LOCK_INIT_NUM 15 52 53 ACPI_CPU_DATA mAcpiCpuData; 54 UINT32 mNumberToFinish; 55 MP_CPU_EXCHANGE_INFO *mExchangeInfo; 56 BOOLEAN mRestoreSmmConfigurationInS3 = FALSE; 57 VOID *mGdtForAp = NULL; 58 VOID *mIdtForAp = NULL; 59 VOID *mMachineCheckHandlerForAp = NULL; 60 MP_MSR_LOCK *mMsrSpinLocks = NULL; 61 UINTN mMsrSpinLockCount = MSR_SPIN_LOCK_INIT_NUM; 62 UINTN mMsrCount = 0; 63 64 /** 65 Get MSR spin lock by MSR index. 66 67 @param MsrIndex MSR index value. 68 69 @return Pointer to MSR spin lock. 70 71 **/ 72 SPIN_LOCK * 73 GetMsrSpinLockByIndex ( 74 IN UINT32 MsrIndex 75 ) 76 { 77 UINTN Index; 78 for (Index = 0; Index < mMsrCount; Index++) { 79 if (MsrIndex == mMsrSpinLocks[Index].MsrIndex) { 80 return &mMsrSpinLocks[Index].SpinLock; 81 } 82 } 83 return NULL; 84 } 85 86 /** 87 Initialize MSR spin lock by MSR index. 88 89 @param MsrIndex MSR index value. 90 91 **/ 92 VOID 93 InitMsrSpinLockByIndex ( 94 IN UINT32 MsrIndex 95 ) 96 { 97 UINTN NewMsrSpinLockCount; 98 99 if (mMsrSpinLocks == NULL) { 100 mMsrSpinLocks = (MP_MSR_LOCK *) AllocatePool (sizeof (MP_MSR_LOCK) * mMsrSpinLockCount); 101 ASSERT (mMsrSpinLocks != NULL); 102 } 103 if (GetMsrSpinLockByIndex (MsrIndex) == NULL) { 104 // 105 // Initialize spin lock for MSR programming 106 // 107 mMsrSpinLocks[mMsrCount].MsrIndex = MsrIndex; 108 InitializeSpinLock (&mMsrSpinLocks[mMsrCount].SpinLock); 109 mMsrCount ++; 110 if (mMsrCount == mMsrSpinLockCount) { 111 // 112 // If MSR spin lock buffer is full, enlarge it 113 // 114 NewMsrSpinLockCount = mMsrSpinLockCount + MSR_SPIN_LOCK_INIT_NUM; 115 mMsrSpinLocks = ReallocatePool ( 116 sizeof (MP_MSR_LOCK) * mMsrSpinLockCount, 117 sizeof (MP_MSR_LOCK) * NewMsrSpinLockCount, 118 mMsrSpinLocks 119 ); 120 mMsrSpinLockCount = NewMsrSpinLockCount; 121 } 122 } 123 } 124 125 /** 126 Sync up the MTRR values for all processors. 127 128 @param MtrrTable Table holding fixed/variable MTRR values to be loaded. 129 **/ 130 VOID 131 EFIAPI 132 LoadMtrrData ( 133 EFI_PHYSICAL_ADDRESS MtrrTable 134 ) 135 /*++ 136 137 Routine Description: 138 139 Sync up the MTRR values for all processors. 140 141 Arguments: 142 143 Returns: 144 None 145 146 --*/ 147 { 148 MTRR_SETTINGS *MtrrSettings; 149 150 MtrrSettings = (MTRR_SETTINGS *) (UINTN) MtrrTable; 151 MtrrSetAllMtrrs (MtrrSettings); 152 } 153 154 /** 155 Programs registers for the calling processor. 156 157 This function programs registers for the calling processor. 158 159 @param RegisterTable Pointer to register table of the running processor. 160 161 **/ 162 VOID 163 SetProcessorRegister ( 164 IN CPU_REGISTER_TABLE *RegisterTable 165 ) 166 { 167 CPU_REGISTER_TABLE_ENTRY *RegisterTableEntry; 168 UINTN Index; 169 UINTN Value; 170 SPIN_LOCK *MsrSpinLock; 171 172 // 173 // Traverse Register Table of this logical processor 174 // 175 RegisterTableEntry = (CPU_REGISTER_TABLE_ENTRY *) (UINTN) RegisterTable->RegisterTableEntry; 176 for (Index = 0; Index < RegisterTable->TableLength; Index++, RegisterTableEntry++) { 177 // 178 // Check the type of specified register 179 // 180 switch (RegisterTableEntry->RegisterType) { 181 // 182 // The specified register is Control Register 183 // 184 case ControlRegister: 185 switch (RegisterTableEntry->Index) { 186 case 0: 187 Value = AsmReadCr0 (); 188 Value = (UINTN) BitFieldWrite64 ( 189 Value, 190 RegisterTableEntry->ValidBitStart, 191 RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, 192 (UINTN) RegisterTableEntry->Value 193 ); 194 AsmWriteCr0 (Value); 195 break; 196 case 2: 197 Value = AsmReadCr2 (); 198 Value = (UINTN) BitFieldWrite64 ( 199 Value, 200 RegisterTableEntry->ValidBitStart, 201 RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, 202 (UINTN) RegisterTableEntry->Value 203 ); 204 AsmWriteCr2 (Value); 205 break; 206 case 3: 207 Value = AsmReadCr3 (); 208 Value = (UINTN) BitFieldWrite64 ( 209 Value, 210 RegisterTableEntry->ValidBitStart, 211 RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, 212 (UINTN) RegisterTableEntry->Value 213 ); 214 AsmWriteCr3 (Value); 215 break; 216 case 4: 217 Value = AsmReadCr4 (); 218 Value = (UINTN) BitFieldWrite64 ( 219 Value, 220 RegisterTableEntry->ValidBitStart, 221 RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, 222 (UINTN) RegisterTableEntry->Value 223 ); 224 AsmWriteCr4 (Value); 225 break; 226 default: 227 break; 228 } 229 break; 230 // 231 // The specified register is Model Specific Register 232 // 233 case Msr: 234 // 235 // If this function is called to restore register setting after INIT signal, 236 // there is no need to restore MSRs in register table. 237 // 238 if (RegisterTableEntry->ValidBitLength >= 64) { 239 // 240 // If length is not less than 64 bits, then directly write without reading 241 // 242 AsmWriteMsr64 ( 243 RegisterTableEntry->Index, 244 RegisterTableEntry->Value 245 ); 246 } else { 247 // 248 // Get lock to avoid Package/Core scope MSRs programming issue in parallel execution mode 249 // to make sure MSR read/write operation is atomic. 250 // 251 MsrSpinLock = GetMsrSpinLockByIndex (RegisterTableEntry->Index); 252 AcquireSpinLock (MsrSpinLock); 253 // 254 // Set the bit section according to bit start and length 255 // 256 AsmMsrBitFieldWrite64 ( 257 RegisterTableEntry->Index, 258 RegisterTableEntry->ValidBitStart, 259 RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, 260 RegisterTableEntry->Value 261 ); 262 ReleaseSpinLock (MsrSpinLock); 263 } 264 break; 265 // 266 // Enable or disable cache 267 // 268 case CacheControl: 269 // 270 // If value of the entry is 0, then disable cache. Otherwise, enable cache. 271 // 272 if (RegisterTableEntry->Value == 0) { 273 AsmDisableCache (); 274 } else { 275 AsmEnableCache (); 276 } 277 break; 278 279 default: 280 break; 281 } 282 } 283 } 284 285 /** 286 AP initialization before SMBASE relocation in the S3 boot path. 287 **/ 288 VOID 289 EarlyMPRendezvousProcedure ( 290 VOID 291 ) 292 { 293 CPU_REGISTER_TABLE *RegisterTableList; 294 UINT32 InitApicId; 295 UINTN Index; 296 297 LoadMtrrData (mAcpiCpuData.MtrrTable); 298 299 // 300 // Find processor number for this CPU. 301 // 302 RegisterTableList = (CPU_REGISTER_TABLE *) (UINTN) mAcpiCpuData.PreSmmInitRegisterTable; 303 InitApicId = GetInitialApicId (); 304 for (Index = 0; Index < mAcpiCpuData.NumberOfCpus; Index++) { 305 if (RegisterTableList[Index].InitialApicId == InitApicId) { 306 SetProcessorRegister (&RegisterTableList[Index]); 307 break; 308 } 309 } 310 311 // 312 // Count down the number with lock mechanism. 313 // 314 InterlockedDecrement (&mNumberToFinish); 315 } 316 317 /** 318 AP initialization after SMBASE relocation in the S3 boot path. 319 **/ 320 VOID 321 MPRendezvousProcedure ( 322 VOID 323 ) 324 { 325 CPU_REGISTER_TABLE *RegisterTableList; 326 UINT32 InitApicId; 327 UINTN Index; 328 329 ProgramVirtualWireMode (); 330 DisableLvtInterrupts (); 331 332 RegisterTableList = (CPU_REGISTER_TABLE *) (UINTN) mAcpiCpuData.RegisterTable; 333 InitApicId = GetInitialApicId (); 334 for (Index = 0; Index < mAcpiCpuData.NumberOfCpus; Index++) { 335 if (RegisterTableList[Index].InitialApicId == InitApicId) { 336 SetProcessorRegister (&RegisterTableList[Index]); 337 break; 338 } 339 } 340 341 // 342 // Count down the number with lock mechanism. 343 // 344 InterlockedDecrement (&mNumberToFinish); 345 } 346 347 /** 348 Prepares startup vector for APs. 349 350 This function prepares startup vector for APs. 351 352 @param WorkingBuffer The address of the work buffer. 353 **/ 354 VOID 355 PrepareApStartupVector ( 356 EFI_PHYSICAL_ADDRESS WorkingBuffer 357 ) 358 { 359 EFI_PHYSICAL_ADDRESS StartupVector; 360 MP_ASSEMBLY_ADDRESS_MAP AddressMap; 361 362 // 363 // Get the address map of startup code for AP, 364 // including code size, and offset of long jump instructions to redirect. 365 // 366 ZeroMem (&AddressMap, sizeof (AddressMap)); 367 AsmGetAddressMap (&AddressMap); 368 369 StartupVector = WorkingBuffer; 370 371 // 372 // Copy AP startup code to startup vector, and then redirect the long jump 373 // instructions for mode switching. 374 // 375 CopyMem ((VOID *) (UINTN) StartupVector, AddressMap.RendezvousFunnelAddress, AddressMap.Size); 376 *(UINT32 *) (UINTN) (StartupVector + AddressMap.FlatJumpOffset + 3) = (UINT32) (StartupVector + AddressMap.PModeEntryOffset); 377 if (AddressMap.LongJumpOffset != 0) { 378 *(UINT32 *) (UINTN) (StartupVector + AddressMap.LongJumpOffset + 2) = (UINT32) (StartupVector + AddressMap.LModeEntryOffset); 379 } 380 381 // 382 // Get the start address of exchange data between BSP and AP. 383 // 384 mExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN) (StartupVector + AddressMap.Size); 385 ZeroMem ((VOID *) mExchangeInfo, sizeof (MP_CPU_EXCHANGE_INFO)); 386 387 CopyMem ((VOID *) (UINTN) &mExchangeInfo->GdtrProfile, (VOID *) (UINTN) mAcpiCpuData.GdtrProfile, sizeof (IA32_DESCRIPTOR)); 388 CopyMem ((VOID *) (UINTN) &mExchangeInfo->IdtrProfile, (VOID *) (UINTN) mAcpiCpuData.IdtrProfile, sizeof (IA32_DESCRIPTOR)); 389 390 // 391 // Copy AP's GDT, IDT and Machine Check handler from SMRAM to ACPI NVS memory 392 // 393 CopyMem ((VOID *) mExchangeInfo->GdtrProfile.Base, mGdtForAp, mExchangeInfo->GdtrProfile.Limit + 1); 394 CopyMem ((VOID *) mExchangeInfo->IdtrProfile.Base, mIdtForAp, mExchangeInfo->IdtrProfile.Limit + 1); 395 CopyMem ((VOID *)(UINTN) mAcpiCpuData.ApMachineCheckHandlerBase, mMachineCheckHandlerForAp, mAcpiCpuData.ApMachineCheckHandlerSize); 396 397 mExchangeInfo->StackStart = (VOID *) (UINTN) mAcpiCpuData.StackAddress; 398 mExchangeInfo->StackSize = mAcpiCpuData.StackSize; 399 mExchangeInfo->BufferStart = (UINT32) StartupVector; 400 mExchangeInfo->Cr3 = (UINT32) (AsmReadCr3 ()); 401 } 402 403 /** 404 The function is invoked before SMBASE relocation in S3 path to restores CPU status. 405 406 The function is invoked before SMBASE relocation in S3 path. It does first time microcode load 407 and restores MTRRs for both BSP and APs. 408 409 **/ 410 VOID 411 EarlyInitializeCpu ( 412 VOID 413 ) 414 { 415 CPU_REGISTER_TABLE *RegisterTableList; 416 UINT32 InitApicId; 417 UINTN Index; 418 419 LoadMtrrData (mAcpiCpuData.MtrrTable); 420 421 // 422 // Find processor number for this CPU. 423 // 424 RegisterTableList = (CPU_REGISTER_TABLE *) (UINTN) mAcpiCpuData.PreSmmInitRegisterTable; 425 InitApicId = GetInitialApicId (); 426 for (Index = 0; Index < mAcpiCpuData.NumberOfCpus; Index++) { 427 if (RegisterTableList[Index].InitialApicId == InitApicId) { 428 SetProcessorRegister (&RegisterTableList[Index]); 429 break; 430 } 431 } 432 433 ProgramVirtualWireMode (); 434 435 PrepareApStartupVector (mAcpiCpuData.StartupVector); 436 437 mNumberToFinish = mAcpiCpuData.NumberOfCpus - 1; 438 mExchangeInfo->ApFunction = (VOID *) (UINTN) EarlyMPRendezvousProcedure; 439 440 // 441 // Send INIT IPI - SIPI to all APs 442 // 443 SendInitSipiSipiAllExcludingSelf ((UINT32)mAcpiCpuData.StartupVector); 444 445 while (mNumberToFinish > 0) { 446 CpuPause (); 447 } 448 } 449 450 /** 451 The function is invoked after SMBASE relocation in S3 path to restores CPU status. 452 453 The function is invoked after SMBASE relocation in S3 path. It restores configuration according to 454 data saved by normal boot path for both BSP and APs. 455 456 **/ 457 VOID 458 InitializeCpu ( 459 VOID 460 ) 461 { 462 CPU_REGISTER_TABLE *RegisterTableList; 463 UINT32 InitApicId; 464 UINTN Index; 465 466 RegisterTableList = (CPU_REGISTER_TABLE *) (UINTN) mAcpiCpuData.RegisterTable; 467 InitApicId = GetInitialApicId (); 468 for (Index = 0; Index < mAcpiCpuData.NumberOfCpus; Index++) { 469 if (RegisterTableList[Index].InitialApicId == InitApicId) { 470 SetProcessorRegister (&RegisterTableList[Index]); 471 break; 472 } 473 } 474 475 mNumberToFinish = mAcpiCpuData.NumberOfCpus - 1; 476 // 477 // StackStart was updated when APs were waken up in EarlyInitializeCpu. 478 // Re-initialize StackAddress to original beginning address. 479 // 480 mExchangeInfo->StackStart = (VOID *) (UINTN) mAcpiCpuData.StackAddress; 481 mExchangeInfo->ApFunction = (VOID *) (UINTN) MPRendezvousProcedure; 482 483 // 484 // Send INIT IPI - SIPI to all APs 485 // 486 SendInitSipiSipiAllExcludingSelf ((UINT32)mAcpiCpuData.StartupVector); 487 488 while (mNumberToFinish > 0) { 489 CpuPause (); 490 } 491 } 492