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