]> git.proxmox.com Git - mirror_edk2.git/blobdiff - UefiCpuPkg/CpuS3DataDxe/CpuS3Data.c
UefiCpuPkg/CpuS3DataDxe: Add module to initialize ACPI_CPU_DATA for S3
[mirror_edk2.git] / UefiCpuPkg / CpuS3DataDxe / CpuS3Data.c
diff --git a/UefiCpuPkg/CpuS3DataDxe/CpuS3Data.c b/UefiCpuPkg/CpuS3DataDxe/CpuS3Data.c
new file mode 100644 (file)
index 0000000..9fb47dc
--- /dev/null
@@ -0,0 +1,271 @@
+/** @file\r
+ACPI CPU Data initialization module\r
+\r
+This module initializes the ACPI_CPU_DATA structure and registers the address\r
+of this structure in the PcdCpuS3DataAddress PCD.  This is a generic/simple\r
+version of this module.  It does not provide a machine check handler or CPU\r
+register initialization tables for ACPI S3 resume.  It also only supports the\r
+number of CPUs reported by the MP Services Protocol, so this module does not\r
+support hot plug CPUs.  This module can be copied into a CPU specific package\r
+and customized if these additional features are required.\r
+\r
+Copyright (c) 2013 - 2015, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2015, Red Hat, Inc.\r
+\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include <PiDxe.h>\r
+\r
+#include <AcpiCpuData.h>\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/MtrrLib.h>\r
+\r
+#include <Protocol/MpService.h>\r
+#include <Guid/EventGroup.h>\r
+\r
+//\r
+// Data structure used to allocate ACPI_CPU_DATA and its supporting structures\r
+//\r
+typedef struct {\r
+  ACPI_CPU_DATA             AcpiCpuData;\r
+  MTRR_SETTINGS             MtrrTable;\r
+  IA32_DESCRIPTOR           GdtrProfile;\r
+  IA32_DESCRIPTOR           IdtrProfile;\r
+} ACPI_CPU_DATA_EX;\r
+\r
+/**\r
+  Allocate EfiACPIMemoryNVS below 4G memory address.\r
+\r
+  This function allocates EfiACPIMemoryNVS below 4G memory address.\r
+\r
+  @param[in] Size   Size of memory to allocate.\r
+\r
+  @return       Allocated address for output.\r
+\r
+**/\r
+VOID *\r
+AllocateAcpiNvsMemoryBelow4G (\r
+  IN UINTN  Size\r
+  )\r
+{\r
+  EFI_PHYSICAL_ADDRESS  Address;\r
+  EFI_STATUS            Status;\r
+  VOID                  *Buffer;\r
+\r
+  Address = BASE_4GB - 1;\r
+  Status  = gBS->AllocatePages (\r
+                   AllocateMaxAddress,\r
+                   EfiACPIMemoryNVS,\r
+                   EFI_SIZE_TO_PAGES (Size),\r
+                   &Address\r
+                   );\r
+  if (EFI_ERROR (Status)) {\r
+    return NULL;\r
+  }\r
+\r
+  Buffer = (VOID *)(UINTN)Address;\r
+  ZeroMem (Buffer, Size);\r
+\r
+  return Buffer;\r
+}\r
+\r
+/**\r
+  Callback function executed when the EndOfDxe event group is signaled.\r
+\r
+  We delay saving the MTRR settings until BDS signals EndOfDxe.\r
+\r
+  @param[in]  Event    Event whose notification function is being invoked.\r
+  @param[out] Context  Pointer to the MTRR_SETTINGS buffer to fill in.\r
+**/\r
+VOID\r
+EFIAPI\r
+SaveMtrrsOnEndOfDxe (\r
+  IN  EFI_EVENT  Event,\r
+  OUT VOID       *Context\r
+  )\r
+{\r
+  DEBUG ((EFI_D_VERBOSE, "%a\n", __FUNCTION__));\r
+  MtrrGetAllMtrrs (Context);\r
+\r
+  //\r
+  // Close event, so it will not be invoked again.\r
+  //\r
+  gBS->CloseEvent (Event);\r
+}\r
+\r
+/**\r
+   The entry function of the CpuS3Data driver.\r
+\r
+   Allocate and initialize all fields of the ACPI_CPU_DATA structure except the\r
+   MTRR settings.  Register an event notification on gEfiEndOfDxeEventGroupGuid\r
+   to capture the ACPI_CPU_DATA MTRR settings.  The PcdCpuS3DataAddress is set\r
+   to the address that ACPI_CPU_DATA is allocated at.\r
+\r
+   @param[in] ImageHandle  The firmware allocated handle for the EFI image.\r
+   @param[in] SystemTable  A pointer to the EFI System Table.\r
+\r
+   @retval EFI_SUCCESS     The entry point is executed successfully.\r
+   @retval other           Some error occurs when executing this entry point.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+CpuS3DataInitialize (\r
+  IN EFI_HANDLE        ImageHandle,\r
+  IN EFI_SYSTEM_TABLE  *SystemTable\r
+  )\r
+{\r
+  EFI_STATUS                 Status;\r
+  ACPI_CPU_DATA_EX           *AcpiCpuDataEx;\r
+  ACPI_CPU_DATA              *AcpiCpuData;\r
+  EFI_MP_SERVICES_PROTOCOL   *MpServices;\r
+  UINTN                      NumberOfCpus;\r
+  UINTN                      NumberOfEnabledProcessors;\r
+  VOID                       *Stack;\r
+  UINTN                      TableSize;\r
+  CPU_REGISTER_TABLE         *RegisterTable;\r
+  UINTN                      Index;\r
+  EFI_PROCESSOR_INFORMATION  ProcessorInfoBuffer;\r
+  UINTN                      GdtSize;\r
+  UINTN                      IdtSize;\r
+  VOID                       *Gdt;\r
+  VOID                       *Idt;\r
+  EFI_EVENT                  Event;\r
+\r
+  //\r
+  // Allocate ACPI NVS memory below 4G memory for use on ACPI S3 resume.\r
+  //\r
+  AcpiCpuDataEx = AllocateAcpiNvsMemoryBelow4G (sizeof (ACPI_CPU_DATA_EX));\r
+  ASSERT (AcpiCpuDataEx != NULL);\r
+  AcpiCpuData = &AcpiCpuDataEx->AcpiCpuData;\r
+\r
+  //\r
+  // Get MP Services Protocol\r
+  //\r
+  Status = gBS->LocateProtocol (\r
+                  &gEfiMpServiceProtocolGuid,\r
+                  NULL,\r
+                  (VOID **)&MpServices\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  //\r
+  // Allocate a 4KB reserved page below 1MB\r
+  //\r
+  AcpiCpuData->StartupVector = BASE_1MB - 1;\r
+  Status = gBS->AllocatePages (\r
+                  AllocateMaxAddress,\r
+                  EfiReservedMemoryType,\r
+                  1,\r
+                  &AcpiCpuData->StartupVector\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  //\r
+  // Get the number of CPUs\r
+  //\r
+  Status = MpServices->GetNumberOfProcessors (\r
+                         MpServices,\r
+                         &NumberOfCpus,\r
+                         &NumberOfEnabledProcessors\r
+                         );\r
+  ASSERT_EFI_ERROR (Status);\r
+  AcpiCpuData->NumberOfCpus = (UINT32)NumberOfCpus;\r
+\r
+  //\r
+  // Initialize ACPI_CPU_DATA fields\r
+  //\r
+  AcpiCpuData->StackSize                 = PcdGet32 (PcdCpuApStackSize);\r
+  AcpiCpuData->ApMachineCheckHandlerBase = 0;\r
+  AcpiCpuData->ApMachineCheckHandlerSize = 0;\r
+  AcpiCpuData->GdtrProfile  = (EFI_PHYSICAL_ADDRESS)(UINTN)&AcpiCpuDataEx->GdtrProfile;\r
+  AcpiCpuData->IdtrProfile  = (EFI_PHYSICAL_ADDRESS)(UINTN)&AcpiCpuDataEx->IdtrProfile;\r
+  AcpiCpuData->MtrrTable    = (EFI_PHYSICAL_ADDRESS)(UINTN)&AcpiCpuDataEx->MtrrTable;\r
+\r
+  //\r
+  // Allocate stack space for all CPUs\r
+  //\r
+  Stack = AllocateAcpiNvsMemoryBelow4G (NumberOfCpus * AcpiCpuData->StackSize);\r
+  ASSERT (Stack != NULL);\r
+  AcpiCpuData->StackAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Stack;\r
+\r
+  //\r
+  // Get the boot processor's GDT and IDT\r
+  //\r
+  AsmReadGdtr (&AcpiCpuDataEx->GdtrProfile);\r
+  AsmReadIdtr (&AcpiCpuDataEx->IdtrProfile);\r
+\r
+  //\r
+  // Allocate GDT and IDT in ACPI NVS and copy current GDT and IDT contents\r
+  //\r
+  GdtSize = AcpiCpuDataEx->GdtrProfile.Limit + 1;\r
+  IdtSize = AcpiCpuDataEx->IdtrProfile.Limit + 1;\r
+  Gdt = AllocateAcpiNvsMemoryBelow4G (GdtSize + IdtSize);\r
+  ASSERT (Gdt != NULL);\r
+  Idt = (VOID *)((UINTN)Gdt + GdtSize);\r
+  CopyMem (Gdt, (VOID *)AcpiCpuDataEx->GdtrProfile.Base, GdtSize);\r
+  CopyMem (Idt, (VOID *)AcpiCpuDataEx->IdtrProfile.Base, IdtSize);\r
+  AcpiCpuDataEx->GdtrProfile.Base = (UINTN)Gdt;\r
+  AcpiCpuDataEx->IdtrProfile.Base = (UINTN)Idt;\r
+\r
+  //\r
+  // Allocate buffer for empty RegisterTable and PreSmmInitRegisterTable for all CPUs\r
+  //\r
+  TableSize = 2 * NumberOfCpus * sizeof (CPU_REGISTER_TABLE);\r
+  RegisterTable = (CPU_REGISTER_TABLE *)AllocateAcpiNvsMemoryBelow4G (TableSize);\r
+  ASSERT (RegisterTable != NULL);\r
+  for (Index = 0; Index < NumberOfCpus; Index++) {\r
+    Status = MpServices->GetProcessorInfo (\r
+                           MpServices,\r
+                           Index,\r
+                           &ProcessorInfoBuffer\r
+                           );\r
+    ASSERT_EFI_ERROR (Status);\r
+\r
+    RegisterTable[Index].InitialApicId      = (UINT32)ProcessorInfoBuffer.ProcessorId;\r
+    RegisterTable[Index].TableLength        = 0;\r
+    RegisterTable[Index].AllocatedSize      = 0;\r
+    RegisterTable[Index].RegisterTableEntry = NULL;\r
+\r
+    RegisterTable[NumberOfCpus + Index].InitialApicId      = (UINT32)ProcessorInfoBuffer.ProcessorId;\r
+    RegisterTable[NumberOfCpus + Index].TableLength        = 0;\r
+    RegisterTable[NumberOfCpus + Index].AllocatedSize      = 0;\r
+    RegisterTable[NumberOfCpus + Index].RegisterTableEntry = NULL;\r
+  }\r
+  AcpiCpuData->RegisterTable           = (EFI_PHYSICAL_ADDRESS)(UINTN)RegisterTable;\r
+  AcpiCpuData->PreSmmInitRegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)(RegisterTable + NumberOfCpus);\r
+\r
+  //\r
+  // Set PcdCpuS3DataAddress to the base address of the ACPI_CPU_DATA structure\r
+  //\r
+  Status = PcdSet64S (PcdCpuS3DataAddress, (UINT64)(UINTN)AcpiCpuData);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  //\r
+  // Register EFI_END_OF_DXE_EVENT_GROUP_GUID event.\r
+  // The notification function saves MTRRs for ACPI_CPU_DATA\r
+  //\r
+  Status = gBS->CreateEventEx (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  SaveMtrrsOnEndOfDxe,\r
+                  &AcpiCpuDataEx->MtrrTable,\r
+                  &gEfiEndOfDxeEventGroupGuid,\r
+                  &Event\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  return EFI_SUCCESS;\r
+}\r