Home | History | Annotate | Download | only in X64
      1 /** @file
      2   Create the variable to save the base address of page table and stack
      3   for transferring into long mode in IA32 capsule PEI.
      4 
      5 Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
      6 This program and the accompanying materials
      7 are licensed and made available under the terms and conditions of the BSD License
      8 which accompanies this distribution.  The full text of the license may be found at
      9 http://opensource.org/licenses/bsd-license.php
     10 
     11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     13 
     14 **/
     15 
     16 #include <Uefi.h>
     17 
     18 #include <Protocol/Capsule.h>
     19 #include <Protocol/DxeSmmReadyToLock.h>
     20 #include <Protocol/VariableLock.h>
     21 
     22 #include <Guid/CapsuleVendor.h>
     23 #include <Guid/AcpiS3Context.h>
     24 
     25 #include <Library/DebugLib.h>
     26 #include <Library/PcdLib.h>
     27 #include <Library/UefiBootServicesTableLib.h>
     28 #include <Library/UefiRuntimeServicesTableLib.h>
     29 #include <Library/UefiRuntimeLib.h>
     30 #include <Library/BaseLib.h>
     31 #include <Library/UefiLib.h>
     32 #include <Library/BaseMemoryLib.h>
     33 
     34 //
     35 // 8 extra pages for PF handler.
     36 //
     37 #define EXTRA_PAGE_TABLE_PAGES   8
     38 
     39 /**
     40   Allocate EfiReservedMemoryType below 4G memory address.
     41 
     42   This function allocates EfiReservedMemoryType below 4G memory address.
     43 
     44   @param  Size      Size of memory to allocate.
     45 
     46   @return Allocated Address for output.
     47 
     48 **/
     49 VOID*
     50 AllocateReservedMemoryBelow4G (
     51   IN   UINTN   Size
     52   )
     53 {
     54   UINTN                 Pages;
     55   EFI_PHYSICAL_ADDRESS  Address;
     56   EFI_STATUS            Status;
     57   VOID*                 Buffer;
     58 
     59   Pages = EFI_SIZE_TO_PAGES (Size);
     60   Address = 0xffffffff;
     61 
     62   Status  = gBS->AllocatePages (
     63                    AllocateMaxAddress,
     64                    EfiReservedMemoryType,
     65                    Pages,
     66                    &Address
     67                    );
     68   ASSERT_EFI_ERROR (Status);
     69 
     70   Buffer = (VOID *) (UINTN) Address;
     71   ZeroMem (Buffer, Size);
     72 
     73   return Buffer;
     74 }
     75 
     76 /**
     77   Register callback function upon VariableLockProtocol
     78   to lock EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to avoid malicious code to update it.
     79 
     80   @param[in] Event    Event whose notification function is being invoked.
     81   @param[in] Context  Pointer to the notification function's context.
     82 **/
     83 VOID
     84 EFIAPI
     85 VariableLockCapsuleLongModeBufferVariable (
     86   IN  EFI_EVENT                             Event,
     87   IN  VOID                                  *Context
     88   )
     89 {
     90   EFI_STATUS                    Status;
     91   EDKII_VARIABLE_LOCK_PROTOCOL  *VariableLock;
     92   //
     93   // Mark EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to read-only if the Variable Lock protocol exists
     94   //
     95   Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock);
     96   if (!EFI_ERROR (Status)) {
     97     Status = VariableLock->RequestToLock (VariableLock, EFI_CAPSULE_LONG_MODE_BUFFER_NAME, &gEfiCapsuleVendorGuid);
     98     ASSERT_EFI_ERROR (Status);
     99   }
    100 }
    101 
    102 /**
    103   1. Allocate Reserved memory for capsule PEIM to establish a 1:1 Virtual to Physical mapping.
    104   2. Allocate Reserved memroy as a stack for capsule PEIM to transfer from 32-bit mdoe to 64-bit mode.
    105 
    106 **/
    107 VOID
    108 EFIAPI
    109 PrepareContextForCapsulePei (
    110   VOID
    111   )
    112 {
    113   UINTN                                         ExtraPageTablePages;
    114   UINT32                                        RegEax;
    115   UINT32                                        RegEdx;
    116   UINTN                                         TotalPagesNum;
    117   UINT8                                         PhysicalAddressBits;
    118   UINT32                                        NumberOfPml4EntriesNeeded;
    119   UINT32                                        NumberOfPdpEntriesNeeded;
    120   BOOLEAN                                       Page1GSupport;
    121   EFI_CAPSULE_LONG_MODE_BUFFER                  LongModeBuffer;
    122   EFI_STATUS                                    Status;
    123   VOID                                          *Registration;
    124 
    125   //
    126   // Calculate the size of page table, allocate the memory.
    127   //
    128   Page1GSupport = FALSE;
    129   if (PcdGetBool(PcdUse1GPageTable)) {
    130     AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
    131     if (RegEax >= 0x80000001) {
    132       AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
    133       if ((RegEdx & BIT26) != 0) {
    134         Page1GSupport = TRUE;
    135       }
    136     }
    137   }
    138 
    139   //
    140   // Create 4G page table by default,
    141   // and let PF handler to handle > 4G request.
    142   //
    143   PhysicalAddressBits = 32;
    144   ExtraPageTablePages = EXTRA_PAGE_TABLE_PAGES;
    145 
    146   //
    147   // Calculate the table entries needed.
    148   //
    149   if (PhysicalAddressBits <= 39 ) {
    150     NumberOfPml4EntriesNeeded = 1;
    151     NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));
    152   } else {
    153     NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));
    154     NumberOfPdpEntriesNeeded = 512;
    155   }
    156 
    157   if (!Page1GSupport) {
    158     TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1;
    159   } else {
    160     TotalPagesNum = NumberOfPml4EntriesNeeded + 1;
    161   }
    162   TotalPagesNum += ExtraPageTablePages;
    163   DEBUG ((EFI_D_ERROR, "CapsuleRuntimeDxe X64 TotalPagesNum - 0x%x pages\n", TotalPagesNum));
    164 
    165   LongModeBuffer.PageTableAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateReservedMemoryBelow4G (EFI_PAGES_TO_SIZE (TotalPagesNum));
    166   ASSERT (LongModeBuffer.PageTableAddress != 0);
    167 
    168   //
    169   // Allocate stack
    170   //
    171   LongModeBuffer.StackSize        = PcdGet32 (PcdCapsulePeiLongModeStackSize);
    172   LongModeBuffer.StackBaseAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateReservedMemoryBelow4G (PcdGet32 (PcdCapsulePeiLongModeStackSize));
    173   ASSERT (LongModeBuffer.StackBaseAddress != 0);
    174 
    175   Status = gRT->SetVariable (
    176                   EFI_CAPSULE_LONG_MODE_BUFFER_NAME,
    177                   &gEfiCapsuleVendorGuid,
    178                   EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
    179                   sizeof (EFI_CAPSULE_LONG_MODE_BUFFER),
    180                   &LongModeBuffer
    181                   );
    182   if (!EFI_ERROR (Status)) {
    183       //
    184       // Register callback function upon VariableLockProtocol
    185       // to lock EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to avoid malicious code to update it.
    186       //
    187       EfiCreateProtocolNotifyEvent (
    188         &gEdkiiVariableLockProtocolGuid,
    189         TPL_CALLBACK,
    190         VariableLockCapsuleLongModeBufferVariable,
    191         NULL,
    192         &Registration
    193         );
    194   } else {
    195       DEBUG ((EFI_D_ERROR, "FATAL ERROR: CapsuleLongModeBuffer cannot be saved: %r. Capsule in PEI may fail!\n", Status));
    196       gBS->FreePages (LongModeBuffer.StackBaseAddress, EFI_SIZE_TO_PAGES (LongModeBuffer.StackSize));
    197   }
    198 }
    199 
    200 /**
    201   Create the variable to save the base address of page table and stack
    202   for transferring into long mode in IA32 capsule PEI.
    203 **/
    204 VOID
    205 SaveLongModeContext (
    206   VOID
    207   )
    208 {
    209   if ((FeaturePcdGet(PcdSupportUpdateCapsuleReset)) && (FeaturePcdGet (PcdDxeIplSwitchToLongMode))) {
    210     //
    211     // Allocate memory for Capsule IA32 PEIM, it will create page table to transfer to long mode to access capsule above 4GB.
    212     //
    213     PrepareContextForCapsulePei ();
    214   }
    215 }
    216