Home | History | Annotate | Download | only in CpuS3DataDxe
      1 /** @file
      2 ACPI CPU Data initialization module
      3 
      4 This module initializes the ACPI_CPU_DATA structure and registers the address
      5 of this structure in the PcdCpuS3DataAddress PCD.  This is a generic/simple
      6 version of this module.  It does not provide a machine check handler or CPU
      7 register initialization tables for ACPI S3 resume.  It also only supports the
      8 number of CPUs reported by the MP Services Protocol, so this module does not
      9 support hot plug CPUs.  This module can be copied into a CPU specific package
     10 and customized if these additional features are required.
     11 
     12 Copyright (c) 2013 - 2016, Intel Corporation. All rights reserved.<BR>
     13 Copyright (c) 2015, Red Hat, Inc.
     14 
     15 This program and the accompanying materials
     16 are licensed and made available under the terms and conditions of the BSD License
     17 which accompanies this distribution.  The full text of the license may be found at
     18 http://opensource.org/licenses/bsd-license.php
     19 
     20 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     21 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     22 
     23 **/
     24 
     25 #include <PiDxe.h>
     26 
     27 #include <AcpiCpuData.h>
     28 
     29 #include <Library/BaseLib.h>
     30 #include <Library/BaseMemoryLib.h>
     31 #include <Library/UefiBootServicesTableLib.h>
     32 #include <Library/DebugLib.h>
     33 #include <Library/MtrrLib.h>
     34 
     35 #include <Protocol/MpService.h>
     36 #include <Guid/EventGroup.h>
     37 
     38 //
     39 // Data structure used to allocate ACPI_CPU_DATA and its supporting structures
     40 //
     41 typedef struct {
     42   ACPI_CPU_DATA             AcpiCpuData;
     43   MTRR_SETTINGS             MtrrTable;
     44   IA32_DESCRIPTOR           GdtrProfile;
     45   IA32_DESCRIPTOR           IdtrProfile;
     46 } ACPI_CPU_DATA_EX;
     47 
     48 /**
     49   Allocate EfiACPIMemoryNVS below 4G memory address.
     50 
     51   This function allocates EfiACPIMemoryNVS below 4G memory address.
     52 
     53   @param[in] Size   Size of memory to allocate.
     54 
     55   @return       Allocated address for output.
     56 
     57 **/
     58 VOID *
     59 AllocateAcpiNvsMemoryBelow4G (
     60   IN UINTN  Size
     61   )
     62 {
     63   EFI_PHYSICAL_ADDRESS  Address;
     64   EFI_STATUS            Status;
     65   VOID                  *Buffer;
     66 
     67   Address = BASE_4GB - 1;
     68   Status  = gBS->AllocatePages (
     69                    AllocateMaxAddress,
     70                    EfiACPIMemoryNVS,
     71                    EFI_SIZE_TO_PAGES (Size),
     72                    &Address
     73                    );
     74   if (EFI_ERROR (Status)) {
     75     return NULL;
     76   }
     77 
     78   Buffer = (VOID *)(UINTN)Address;
     79   ZeroMem (Buffer, Size);
     80 
     81   return Buffer;
     82 }
     83 
     84 /**
     85   Callback function executed when the EndOfDxe event group is signaled.
     86 
     87   We delay allocating StartupVector and saving the MTRR settings until BDS signals EndOfDxe.
     88 
     89   @param[in]  Event    Event whose notification function is being invoked.
     90   @param[out] Context  Pointer to the MTRR_SETTINGS buffer to fill in.
     91 **/
     92 VOID
     93 EFIAPI
     94 CpuS3DataOnEndOfDxe (
     95   IN  EFI_EVENT  Event,
     96   OUT VOID       *Context
     97   )
     98 {
     99   EFI_STATUS         Status;
    100   ACPI_CPU_DATA_EX   *AcpiCpuDataEx;
    101 
    102   AcpiCpuDataEx = (ACPI_CPU_DATA_EX *) Context;
    103   //
    104   // Allocate a 4KB reserved page below 1MB
    105   //
    106   AcpiCpuDataEx->AcpiCpuData.StartupVector = BASE_1MB - 1;
    107   Status = gBS->AllocatePages (
    108                   AllocateMaxAddress,
    109                   EfiReservedMemoryType,
    110                   1,
    111                   &AcpiCpuDataEx->AcpiCpuData.StartupVector
    112                   );
    113   ASSERT_EFI_ERROR (Status);
    114 
    115   DEBUG ((EFI_D_VERBOSE, "%a\n", __FUNCTION__));
    116   MtrrGetAllMtrrs (&AcpiCpuDataEx->MtrrTable);
    117 
    118   //
    119   // Close event, so it will not be invoked again.
    120   //
    121   gBS->CloseEvent (Event);
    122 }
    123 
    124 /**
    125    The entry function of the CpuS3Data driver.
    126 
    127    Allocate and initialize all fields of the ACPI_CPU_DATA structure except the
    128    MTRR settings.  Register an event notification on gEfiEndOfDxeEventGroupGuid
    129    to capture the ACPI_CPU_DATA MTRR settings.  The PcdCpuS3DataAddress is set
    130    to the address that ACPI_CPU_DATA is allocated at.
    131 
    132    @param[in] ImageHandle  The firmware allocated handle for the EFI image.
    133    @param[in] SystemTable  A pointer to the EFI System Table.
    134 
    135    @retval EFI_SUCCESS     The entry point is executed successfully.
    136    @retval EFI_UNSUPPORTED Do not support ACPI S3.
    137    @retval other           Some error occurs when executing this entry point.
    138 
    139 **/
    140 EFI_STATUS
    141 EFIAPI
    142 CpuS3DataInitialize (
    143   IN EFI_HANDLE        ImageHandle,
    144   IN EFI_SYSTEM_TABLE  *SystemTable
    145   )
    146 {
    147   EFI_STATUS                 Status;
    148   ACPI_CPU_DATA_EX           *AcpiCpuDataEx;
    149   ACPI_CPU_DATA              *AcpiCpuData;
    150   EFI_MP_SERVICES_PROTOCOL   *MpServices;
    151   UINTN                      NumberOfCpus;
    152   UINTN                      NumberOfEnabledProcessors;
    153   VOID                       *Stack;
    154   UINTN                      TableSize;
    155   CPU_REGISTER_TABLE         *RegisterTable;
    156   UINTN                      Index;
    157   EFI_PROCESSOR_INFORMATION  ProcessorInfoBuffer;
    158   UINTN                      GdtSize;
    159   UINTN                      IdtSize;
    160   VOID                       *Gdt;
    161   VOID                       *Idt;
    162   EFI_EVENT                  Event;
    163 
    164   if (!PcdGetBool (PcdAcpiS3Enable)) {
    165     return EFI_UNSUPPORTED;
    166   }
    167 
    168   //
    169   // Allocate ACPI NVS memory below 4G memory for use on ACPI S3 resume.
    170   //
    171   AcpiCpuDataEx = AllocateAcpiNvsMemoryBelow4G (sizeof (ACPI_CPU_DATA_EX));
    172   ASSERT (AcpiCpuDataEx != NULL);
    173   AcpiCpuData = &AcpiCpuDataEx->AcpiCpuData;
    174 
    175   //
    176   // Get MP Services Protocol
    177   //
    178   Status = gBS->LocateProtocol (
    179                   &gEfiMpServiceProtocolGuid,
    180                   NULL,
    181                   (VOID **)&MpServices
    182                   );
    183   ASSERT_EFI_ERROR (Status);
    184 
    185   //
    186   // Get the number of CPUs
    187   //
    188   Status = MpServices->GetNumberOfProcessors (
    189                          MpServices,
    190                          &NumberOfCpus,
    191                          &NumberOfEnabledProcessors
    192                          );
    193   ASSERT_EFI_ERROR (Status);
    194   AcpiCpuData->NumberOfCpus = (UINT32)NumberOfCpus;
    195 
    196   //
    197   // Initialize ACPI_CPU_DATA fields
    198   //
    199   AcpiCpuData->StackSize                 = PcdGet32 (PcdCpuApStackSize);
    200   AcpiCpuData->ApMachineCheckHandlerBase = 0;
    201   AcpiCpuData->ApMachineCheckHandlerSize = 0;
    202   AcpiCpuData->GdtrProfile  = (EFI_PHYSICAL_ADDRESS)(UINTN)&AcpiCpuDataEx->GdtrProfile;
    203   AcpiCpuData->IdtrProfile  = (EFI_PHYSICAL_ADDRESS)(UINTN)&AcpiCpuDataEx->IdtrProfile;
    204   AcpiCpuData->MtrrTable    = (EFI_PHYSICAL_ADDRESS)(UINTN)&AcpiCpuDataEx->MtrrTable;
    205 
    206   //
    207   // Allocate stack space for all CPUs
    208   //
    209   Stack = AllocateAcpiNvsMemoryBelow4G (NumberOfCpus * AcpiCpuData->StackSize);
    210   ASSERT (Stack != NULL);
    211   AcpiCpuData->StackAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Stack;
    212 
    213   //
    214   // Get the boot processor's GDT and IDT
    215   //
    216   AsmReadGdtr (&AcpiCpuDataEx->GdtrProfile);
    217   AsmReadIdtr (&AcpiCpuDataEx->IdtrProfile);
    218 
    219   //
    220   // Allocate GDT and IDT in ACPI NVS and copy current GDT and IDT contents
    221   //
    222   GdtSize = AcpiCpuDataEx->GdtrProfile.Limit + 1;
    223   IdtSize = AcpiCpuDataEx->IdtrProfile.Limit + 1;
    224   Gdt = AllocateAcpiNvsMemoryBelow4G (GdtSize + IdtSize);
    225   ASSERT (Gdt != NULL);
    226   Idt = (VOID *)((UINTN)Gdt + GdtSize);
    227   CopyMem (Gdt, (VOID *)AcpiCpuDataEx->GdtrProfile.Base, GdtSize);
    228   CopyMem (Idt, (VOID *)AcpiCpuDataEx->IdtrProfile.Base, IdtSize);
    229   AcpiCpuDataEx->GdtrProfile.Base = (UINTN)Gdt;
    230   AcpiCpuDataEx->IdtrProfile.Base = (UINTN)Idt;
    231 
    232   //
    233   // Allocate buffer for empty RegisterTable and PreSmmInitRegisterTable for all CPUs
    234   //
    235   TableSize = 2 * NumberOfCpus * sizeof (CPU_REGISTER_TABLE);
    236   RegisterTable = (CPU_REGISTER_TABLE *)AllocateAcpiNvsMemoryBelow4G (TableSize);
    237   ASSERT (RegisterTable != NULL);
    238   for (Index = 0; Index < NumberOfCpus; Index++) {
    239     Status = MpServices->GetProcessorInfo (
    240                            MpServices,
    241                            Index,
    242                            &ProcessorInfoBuffer
    243                            );
    244     ASSERT_EFI_ERROR (Status);
    245 
    246     RegisterTable[Index].InitialApicId      = (UINT32)ProcessorInfoBuffer.ProcessorId;
    247     RegisterTable[Index].TableLength        = 0;
    248     RegisterTable[Index].AllocatedSize      = 0;
    249     RegisterTable[Index].RegisterTableEntry = NULL;
    250 
    251     RegisterTable[NumberOfCpus + Index].InitialApicId      = (UINT32)ProcessorInfoBuffer.ProcessorId;
    252     RegisterTable[NumberOfCpus + Index].TableLength        = 0;
    253     RegisterTable[NumberOfCpus + Index].AllocatedSize      = 0;
    254     RegisterTable[NumberOfCpus + Index].RegisterTableEntry = NULL;
    255   }
    256   AcpiCpuData->RegisterTable           = (EFI_PHYSICAL_ADDRESS)(UINTN)RegisterTable;
    257   AcpiCpuData->PreSmmInitRegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)(RegisterTable + NumberOfCpus);
    258 
    259   //
    260   // Set PcdCpuS3DataAddress to the base address of the ACPI_CPU_DATA structure
    261   //
    262   Status = PcdSet64S (PcdCpuS3DataAddress, (UINT64)(UINTN)AcpiCpuData);
    263   ASSERT_EFI_ERROR (Status);
    264 
    265   //
    266   // Register EFI_END_OF_DXE_EVENT_GROUP_GUID event.
    267   // The notification function allocates StartupVector and saves MTRRs for ACPI_CPU_DATA
    268   //
    269   Status = gBS->CreateEventEx (
    270                   EVT_NOTIFY_SIGNAL,
    271                   TPL_CALLBACK,
    272                   CpuS3DataOnEndOfDxe,
    273                   AcpiCpuData,
    274                   &gEfiEndOfDxeEventGroupGuid,
    275                   &Event
    276                   );
    277   ASSERT_EFI_ERROR (Status);
    278 
    279   return EFI_SUCCESS;
    280 }
    281