1/** @file 2 This is a replacement for the ACPI S3 Save protocol. 3 4 The ACPI S3 Save protocol used to be defined in the S3 boot path 5 specification 0.9. Instead, the same functionality is now hooked to the 6 End-of-Dxe event. 7 8Copyright (c) 2014-2015, Red Hat, Inc.<BR> 9Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR> 10 11This program and the accompanying materials 12are licensed and made available under the terms and conditions 13of the BSD License which accompanies this distribution. The 14full text of the license may be found at 15http://opensource.org/licenses/bsd-license.php 16 17THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 18WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 19 20**/ 21 22#include <PiDxe.h> 23#include <Library/BaseLib.h> 24#include <Library/BaseMemoryLib.h> 25#include <Library/UefiBootServicesTableLib.h> 26#include <Library/UefiRuntimeServicesTableLib.h> 27#include <Library/HobLib.h> 28#include <Library/LockBoxLib.h> 29#include <Library/PcdLib.h> 30#include <Library/DebugLib.h> 31#include <Library/QemuFwCfgLib.h> 32#include <Guid/AcpiVariableCompatibility.h> 33#include <Guid/AcpiS3Context.h> 34#include <Guid/Acpi.h> 35#include <Guid/EventGroup.h> 36#include <Protocol/LockBox.h> 37#include <IndustryStandard/Acpi.h> 38 39EFI_GUID mAcpiS3IdtrProfileGuid = { 40 0xdea652b0, 0xd587, 0x4c54, { 0xb5, 0xb4, 0xc6, 0x82, 0xe7, 0xa0, 0xaa, 0x3d } 41}; 42 43/** 44 Allocate memory below 4G memory address. 45 46 This function allocates memory below 4G memory address. 47 48 @param MemoryType Memory type of memory to allocate. 49 @param Size Size of memory to allocate. 50 51 @return Allocated address for output. 52 53**/ 54VOID* 55AllocateMemoryBelow4G ( 56 IN EFI_MEMORY_TYPE MemoryType, 57 IN UINTN Size 58 ) 59{ 60 UINTN Pages; 61 EFI_PHYSICAL_ADDRESS Address; 62 EFI_STATUS Status; 63 VOID* Buffer; 64 65 Pages = EFI_SIZE_TO_PAGES (Size); 66 Address = 0xffffffff; 67 68 Status = gBS->AllocatePages ( 69 AllocateMaxAddress, 70 MemoryType, 71 Pages, 72 &Address 73 ); 74 ASSERT_EFI_ERROR (Status); 75 76 Buffer = (VOID *) (UINTN) Address; 77 ZeroMem (Buffer, Size); 78 79 return Buffer; 80} 81 82/** 83 84 This function scan ACPI table in RSDT. 85 86 @param Rsdt ACPI RSDT 87 @param Signature ACPI table signature 88 89 @return ACPI table 90 91**/ 92VOID * 93ScanTableInRSDT ( 94 IN EFI_ACPI_DESCRIPTION_HEADER *Rsdt, 95 IN UINT32 Signature 96 ) 97{ 98 UINTN Index; 99 UINT32 EntryCount; 100 UINT32 *EntryPtr; 101 EFI_ACPI_DESCRIPTION_HEADER *Table; 102 103 if (Rsdt == NULL) { 104 return NULL; 105 } 106 107 EntryCount = (Rsdt->Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) / sizeof(UINT32); 108 109 EntryPtr = (UINT32 *)(Rsdt + 1); 110 for (Index = 0; Index < EntryCount; Index ++, EntryPtr ++) { 111 Table = (EFI_ACPI_DESCRIPTION_HEADER *)((UINTN)(*EntryPtr)); 112 if (Table->Signature == Signature) { 113 return Table; 114 } 115 } 116 117 return NULL; 118} 119 120/** 121 122 This function scan ACPI table in XSDT. 123 124 @param Xsdt ACPI XSDT 125 @param Signature ACPI table signature 126 127 @return ACPI table 128 129**/ 130VOID * 131ScanTableInXSDT ( 132 IN EFI_ACPI_DESCRIPTION_HEADER *Xsdt, 133 IN UINT32 Signature 134 ) 135{ 136 UINTN Index; 137 UINT32 EntryCount; 138 UINT64 EntryPtr; 139 UINTN BasePtr; 140 EFI_ACPI_DESCRIPTION_HEADER *Table; 141 142 if (Xsdt == NULL) { 143 return NULL; 144 } 145 146 EntryCount = (Xsdt->Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) / sizeof(UINT64); 147 148 BasePtr = (UINTN)(Xsdt + 1); 149 for (Index = 0; Index < EntryCount; Index ++) { 150 CopyMem (&EntryPtr, (VOID *)(BasePtr + Index * sizeof(UINT64)), sizeof(UINT64)); 151 Table = (EFI_ACPI_DESCRIPTION_HEADER *)((UINTN)(EntryPtr)); 152 if (Table->Signature == Signature) { 153 return Table; 154 } 155 } 156 157 return NULL; 158} 159 160/** 161 To find Facs in FADT. 162 163 @param Fadt FADT table pointer 164 165 @return Facs table pointer. 166**/ 167EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE * 168FindAcpiFacsFromFadt ( 169 IN EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt 170 ) 171{ 172 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs; 173 UINT64 Data64; 174 175 if (Fadt == NULL) { 176 return NULL; 177 } 178 179 if (Fadt->Header.Revision < EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_REVISION) { 180 Facs = (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)Fadt->FirmwareCtrl; 181 } else { 182 if (Fadt->FirmwareCtrl != 0) { 183 Facs = (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)Fadt->FirmwareCtrl; 184 } else { 185 CopyMem (&Data64, &Fadt->XFirmwareCtrl, sizeof(UINT64)); 186 Facs = (EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *)(UINTN)Data64; 187 } 188 } 189 return Facs; 190} 191 192/** 193 To find Facs in Acpi tables. 194 195 To find Firmware ACPI control strutcure in Acpi Tables since the S3 waking vector is stored 196 in the table. 197 198 @param AcpiTableGuid The guid used to find ACPI table in UEFI ConfigurationTable. 199 200 @return Facs table pointer. 201**/ 202EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE * 203FindAcpiFacsTableByAcpiGuid ( 204 IN EFI_GUID *AcpiTableGuid 205 ) 206{ 207 EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp; 208 EFI_ACPI_DESCRIPTION_HEADER *Rsdt; 209 EFI_ACPI_DESCRIPTION_HEADER *Xsdt; 210 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt; 211 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs; 212 UINTN Index; 213 214 Rsdp = NULL; 215 // 216 // found ACPI table RSD_PTR from system table 217 // 218 for (Index = 0; Index < gST->NumberOfTableEntries; Index++) { 219 if (CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), AcpiTableGuid)) { 220 // 221 // A match was found. 222 // 223 Rsdp = gST->ConfigurationTable[Index].VendorTable; 224 break; 225 } 226 } 227 228 if (Rsdp == NULL) { 229 return NULL; 230 } 231 232 // 233 // Search XSDT 234 // 235 if (Rsdp->Revision >= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION) { 236 Xsdt = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN) Rsdp->XsdtAddress; 237 Fadt = ScanTableInXSDT (Xsdt, EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE); 238 if (Fadt != NULL) { 239 Facs = FindAcpiFacsFromFadt (Fadt); 240 if (Facs != NULL) { 241 return Facs; 242 } 243 } 244 } 245 246 // 247 // Search RSDT 248 // 249 Rsdt = (EFI_ACPI_DESCRIPTION_HEADER *)(UINTN) Rsdp->RsdtAddress; 250 Fadt = ScanTableInRSDT (Rsdt, EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE); 251 if (Fadt != NULL) { 252 Facs = FindAcpiFacsFromFadt (Fadt); 253 if (Facs != NULL) { 254 return Facs; 255 } 256 } 257 258 return NULL; 259} 260 261/** 262 To find Facs in Acpi tables. 263 264 To find Firmware ACPI control strutcure in Acpi Tables since the S3 waking vector is stored 265 in the table. 266 267 @return Facs table pointer. 268**/ 269EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE * 270FindAcpiFacsTable ( 271 VOID 272 ) 273{ 274 EFI_ACPI_2_0_FIRMWARE_ACPI_CONTROL_STRUCTURE *Facs; 275 276 Facs = FindAcpiFacsTableByAcpiGuid (&gEfiAcpi20TableGuid); 277 if (Facs != NULL) { 278 return Facs; 279 } 280 281 return FindAcpiFacsTableByAcpiGuid (&gEfiAcpi10TableGuid); 282} 283 284/** 285 Allocates and fills in the Page Directory and Page Table Entries to 286 establish a 1:1 Virtual to Physical mapping. 287 If BootScriptExector driver will run in 64-bit mode, this function will establish the 1:1 288 virtual to physical mapping page table. 289 If BootScriptExector driver will not run in 64-bit mode, this function will do nothing. 290 291 @return the 1:1 Virtual to Physical identity mapping page table base address. 292 293**/ 294EFI_PHYSICAL_ADDRESS 295S3CreateIdentityMappingPageTables ( 296 VOID 297 ) 298{ 299 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) { 300 UINT32 RegEax; 301 UINT32 RegEdx; 302 UINT8 PhysicalAddressBits; 303 UINT32 NumberOfPml4EntriesNeeded; 304 UINT32 NumberOfPdpEntriesNeeded; 305 EFI_PHYSICAL_ADDRESS S3NvsPageTableAddress; 306 UINTN TotalPageTableSize; 307 VOID *Hob; 308 BOOLEAN Page1GSupport; 309 310 Page1GSupport = FALSE; 311 if (PcdGetBool(PcdUse1GPageTable)) { 312 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); 313 if (RegEax >= 0x80000001) { 314 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx); 315 if ((RegEdx & BIT26) != 0) { 316 Page1GSupport = TRUE; 317 } 318 } 319 } 320 321 // 322 // Get physical address bits supported. 323 // 324 Hob = GetFirstHob (EFI_HOB_TYPE_CPU); 325 if (Hob != NULL) { 326 PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace; 327 } else { 328 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); 329 if (RegEax >= 0x80000008) { 330 AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL); 331 PhysicalAddressBits = (UINT8) RegEax; 332 } else { 333 PhysicalAddressBits = 36; 334 } 335 } 336 337 // 338 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses. 339 // 340 ASSERT (PhysicalAddressBits <= 52); 341 if (PhysicalAddressBits > 48) { 342 PhysicalAddressBits = 48; 343 } 344 345 // 346 // Calculate the table entries needed. 347 // 348 if (PhysicalAddressBits <= 39 ) { 349 NumberOfPml4EntriesNeeded = 1; 350 NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30)); 351 } else { 352 NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39)); 353 NumberOfPdpEntriesNeeded = 512; 354 } 355 356 // 357 // We need calculate whole page size then allocate once, because S3 restore page table does not know each page in Nvs. 358 // 359 if (!Page1GSupport) { 360 TotalPageTableSize = (UINTN)(1 + NumberOfPml4EntriesNeeded + NumberOfPml4EntriesNeeded * NumberOfPdpEntriesNeeded); 361 } else { 362 TotalPageTableSize = (UINTN)(1 + NumberOfPml4EntriesNeeded); 363 } 364 DEBUG ((EFI_D_ERROR, "TotalPageTableSize - %Lx pages\n", 365 (UINT64)TotalPageTableSize)); 366 367 // 368 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it. 369 // 370 S3NvsPageTableAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, EFI_PAGES_TO_SIZE(TotalPageTableSize)); 371 ASSERT (S3NvsPageTableAddress != 0); 372 return S3NvsPageTableAddress; 373 } else { 374 // 375 // If DXE is running 32-bit mode, no need to establish page table. 376 // 377 return (EFI_PHYSICAL_ADDRESS) 0; 378 } 379} 380 381/** 382 Prepares all information that is needed in the S3 resume boot path. 383 384 Allocate the resources or prepare informations and save in ACPI variable set for S3 resume boot path 385 386 @retval EFI_SUCCESS All information was saved successfully. 387**/ 388STATIC 389EFI_STATUS 390EFIAPI 391S3Ready ( 392 VOID 393 ) 394{ 395 EFI_STATUS Status; 396 EFI_PHYSICAL_ADDRESS AcpiS3ContextBuffer; 397 ACPI_S3_CONTEXT *AcpiS3Context; 398 STATIC BOOLEAN AlreadyEntered; 399 IA32_DESCRIPTOR *Idtr; 400 IA32_IDT_GATE_DESCRIPTOR *IdtGate; 401 402 DEBUG ((EFI_D_INFO, "S3Ready!\n")); 403 404 ASSERT (!AlreadyEntered); 405 if (AlreadyEntered) { 406 return EFI_SUCCESS; 407 } 408 AlreadyEntered = TRUE; 409 410 AcpiS3Context = AllocateMemoryBelow4G (EfiReservedMemoryType, sizeof(*AcpiS3Context)); 411 ASSERT (AcpiS3Context != NULL); 412 AcpiS3ContextBuffer = (EFI_PHYSICAL_ADDRESS)(UINTN)AcpiS3Context; 413 414 // 415 // Get ACPI Table because we will save its position to variable 416 // 417 AcpiS3Context->AcpiFacsTable = (EFI_PHYSICAL_ADDRESS)(UINTN)FindAcpiFacsTable (); 418 ASSERT (AcpiS3Context->AcpiFacsTable != 0); 419 420 IdtGate = AllocateMemoryBelow4G (EfiReservedMemoryType, sizeof(IA32_IDT_GATE_DESCRIPTOR) * 0x100 + sizeof(IA32_DESCRIPTOR)); 421 Idtr = (IA32_DESCRIPTOR *)(IdtGate + 0x100); 422 Idtr->Base = (UINTN)IdtGate; 423 Idtr->Limit = (UINT16)(sizeof(IA32_IDT_GATE_DESCRIPTOR) * 0x100 - 1); 424 AcpiS3Context->IdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)Idtr; 425 426 Status = SaveLockBox ( 427 &mAcpiS3IdtrProfileGuid, 428 (VOID *)(UINTN)Idtr, 429 (UINTN)sizeof(IA32_DESCRIPTOR) 430 ); 431 ASSERT_EFI_ERROR (Status); 432 433 Status = SetLockBoxAttributes (&mAcpiS3IdtrProfileGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE); 434 ASSERT_EFI_ERROR (Status); 435 436 // 437 // Allocate page table 438 // 439 AcpiS3Context->S3NvsPageTableAddress = S3CreateIdentityMappingPageTables (); 440 441 // 442 // Allocate stack 443 // 444 AcpiS3Context->BootScriptStackSize = PcdGet32 (PcdS3BootScriptStackSize); 445 AcpiS3Context->BootScriptStackBase = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, PcdGet32 (PcdS3BootScriptStackSize)); 446 ASSERT (AcpiS3Context->BootScriptStackBase != 0); 447 448 // 449 // Allocate a code buffer < 4G for S3 debug to load external code, set invalid code instructions in it. 450 // 451 AcpiS3Context->S3DebugBufferAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateMemoryBelow4G (EfiReservedMemoryType, EFI_PAGE_SIZE); 452 SetMem ((VOID *)(UINTN)AcpiS3Context->S3DebugBufferAddress, EFI_PAGE_SIZE, 0xff); 453 454 DEBUG ((EFI_D_INFO, "AcpiS3Context: AcpiFacsTable is 0x%8Lx\n", 455 AcpiS3Context->AcpiFacsTable)); 456 DEBUG ((EFI_D_INFO, "AcpiS3Context: IdtrProfile is 0x%8Lx\n", 457 AcpiS3Context->IdtrProfile)); 458 DEBUG ((EFI_D_INFO, "AcpiS3Context: S3NvsPageTableAddress is 0x%8Lx\n", 459 AcpiS3Context->S3NvsPageTableAddress)); 460 DEBUG ((EFI_D_INFO, "AcpiS3Context: S3DebugBufferAddress is 0x%8Lx\n", 461 AcpiS3Context->S3DebugBufferAddress)); 462 463 Status = SaveLockBox ( 464 &gEfiAcpiVariableGuid, 465 &AcpiS3ContextBuffer, 466 sizeof(AcpiS3ContextBuffer) 467 ); 468 ASSERT_EFI_ERROR (Status); 469 470 Status = SaveLockBox ( 471 &gEfiAcpiS3ContextGuid, 472 (VOID *)(UINTN)AcpiS3Context, 473 (UINTN)sizeof(*AcpiS3Context) 474 ); 475 ASSERT_EFI_ERROR (Status); 476 477 Status = SetLockBoxAttributes (&gEfiAcpiS3ContextGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE); 478 ASSERT_EFI_ERROR (Status); 479 480 return EFI_SUCCESS; 481} 482 483/** 484 Callback function executed when the EndOfDxe event group is signaled. 485 486 @param[in] Event Event whose notification function is being invoked. 487 @param[in] Context The pointer to the notification function's context, which 488 is implementation-dependent. 489**/ 490VOID 491EFIAPI 492OnEndOfDxe ( 493 IN EFI_EVENT Event, 494 IN VOID *Context 495 ) 496{ 497 EFI_STATUS Status; 498 499 // 500 // Our S3Ready() function always succeeds. 501 // 502 Status = S3Ready (); 503 ASSERT_EFI_ERROR (Status); 504 505 // 506 // Close the event, deregistering the callback and freeing resources. 507 // 508 Status = gBS->CloseEvent (Event); 509 ASSERT_EFI_ERROR (Status); 510} 511 512 513/** 514 The Driver Entry Point. 515 516 The function is the driver Entry point that will register the End-of-Dxe 517 callback. 518 519 @param ImageHandle A handle for the image that is initializing this driver 520 @param SystemTable A pointer to the EFI system table 521 522 @retval EFI_SUCCESS: Driver initialized successfully 523 @retval EFI_LOAD_ERROR: Failed to Initialize or has been loaded 524 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources 525 526**/ 527EFI_STATUS 528EFIAPI 529InstallEndOfDxeCallback ( 530 IN EFI_HANDLE ImageHandle, 531 IN EFI_SYSTEM_TABLE *SystemTable 532 ) 533{ 534 EFI_STATUS Status; 535 EFI_EVENT EndOfDxeEvent; 536 537 if (!QemuFwCfgS3Enabled()) { 538 return EFI_LOAD_ERROR; 539 } 540 541 if (!FeaturePcdGet (PcdSmmSmramRequire)) { 542 Status = gBS->InstallMultipleProtocolInterfaces ( 543 &ImageHandle, 544 &gEfiLockBoxProtocolGuid, NULL, 545 NULL 546 ); 547 ASSERT_EFI_ERROR (Status); 548 } 549 550 Status = gBS->CreateEventEx ( 551 EVT_NOTIFY_SIGNAL, 552 TPL_CALLBACK, 553 OnEndOfDxe, 554 NULL, /* NotifyContext */ 555 &gEfiEndOfDxeEventGroupGuid, 556 &EndOfDxeEvent 557 ); 558 ASSERT_EFI_ERROR (Status); 559 return Status; 560} 561