]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/CapsuleRuntimeDxe/X64/SaveLongModeContext.c
Allocate EfiReservedMemoryType of memory.
[mirror_edk2.git] / MdeModulePkg / Universal / CapsuleRuntimeDxe / X64 / SaveLongModeContext.c
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 - 2014, 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 #include <Library/HobLib.h>
34
35 /**
36 Allocate EfiReservedMemoryType below 4G memory address.
37
38 This function allocates EfiReservedMemoryType below 4G memory address.
39
40 @param Size Size of memory to allocate.
41
42 @return Allocated Address for output.
43
44 **/
45 VOID*
46 AllocateReservedMemoryBelow4G (
47 IN UINTN Size
48 )
49 {
50 UINTN Pages;
51 EFI_PHYSICAL_ADDRESS Address;
52 EFI_STATUS Status;
53 VOID* Buffer;
54
55 Pages = EFI_SIZE_TO_PAGES (Size);
56 Address = 0xffffffff;
57
58 Status = gBS->AllocatePages (
59 AllocateMaxAddress,
60 EfiReservedMemoryType,
61 Pages,
62 &Address
63 );
64 ASSERT_EFI_ERROR (Status);
65
66 Buffer = (VOID *) (UINTN) Address;
67 ZeroMem (Buffer, Size);
68
69 return Buffer;
70 }
71
72 /**
73 Register callback function upon VariableLockProtocol
74 to lock EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to avoid malicious code to update it.
75
76 @param[in] Event Event whose notification function is being invoked.
77 @param[in] Context Pointer to the notification function's context.
78 **/
79 VOID
80 EFIAPI
81 VariableLockCapsuleLongModeBufferVariable (
82 IN EFI_EVENT Event,
83 IN VOID *Context
84 )
85 {
86 EFI_STATUS Status;
87 EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
88 //
89 // Mark EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to read-only if the Variable Lock protocol exists
90 //
91 Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock);
92 if (!EFI_ERROR (Status)) {
93 Status = VariableLock->RequestToLock (VariableLock, EFI_CAPSULE_LONG_MODE_BUFFER_NAME, &gEfiCapsuleVendorGuid);
94 ASSERT_EFI_ERROR (Status);
95 }
96 }
97
98 /**
99 1. Allocate Reserved memory for capsule PEIM to establish a 1:1 Virtual to Physical mapping.
100 2. Allocate Reserved memroy as a stack for capsule PEIM to transfer from 32-bit mdoe to 64-bit mode.
101
102 **/
103 VOID
104 EFIAPI
105 PrepareContextForCapsulePei (
106 VOID
107 )
108 {
109 UINT32 RegEax;
110 UINT32 RegEdx;
111 UINTN TotalPagesNum;
112 UINT8 PhysicalAddressBits;
113 VOID *Hob;
114 UINT32 NumberOfPml4EntriesNeeded;
115 UINT32 NumberOfPdpEntriesNeeded;
116 BOOLEAN Page1GSupport;
117 EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer;
118 EFI_STATUS Status;
119 VOID *Registration;
120
121 LongModeBuffer.PageTableAddress = (EFI_PHYSICAL_ADDRESS) PcdGet64 (PcdIdentifyMappingPageTablePtr);
122
123 if (LongModeBuffer.PageTableAddress == 0x0) {
124 //
125 // Calculate the size of page table, allocate the memory, and set PcdIdentifyMappingPageTablePtr.
126 //
127 Page1GSupport = FALSE;
128 if (PcdGetBool(PcdUse1GPageTable)) {
129 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
130 if (RegEax >= 0x80000001) {
131 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
132 if ((RegEdx & BIT26) != 0) {
133 Page1GSupport = TRUE;
134 }
135 }
136 }
137
138 //
139 // Get physical address bits supported.
140 //
141 Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
142 if (Hob != NULL) {
143 PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;
144 } else {
145 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
146 if (RegEax >= 0x80000008) {
147 AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);
148 PhysicalAddressBits = (UINT8) RegEax;
149 } else {
150 PhysicalAddressBits = 36;
151 }
152 }
153
154 //
155 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
156 //
157 ASSERT (PhysicalAddressBits <= 52);
158 if (PhysicalAddressBits > 48) {
159 PhysicalAddressBits = 48;
160 }
161
162 //
163 // Calculate the table entries needed.
164 //
165 if (PhysicalAddressBits <= 39 ) {
166 NumberOfPml4EntriesNeeded = 1;
167 NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));
168 } else {
169 NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));
170 NumberOfPdpEntriesNeeded = 512;
171 }
172
173 if (!Page1GSupport) {
174 TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1;
175 } else {
176 TotalPagesNum = NumberOfPml4EntriesNeeded + 1;
177 }
178
179 LongModeBuffer.PageTableAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateReservedMemoryBelow4G (EFI_PAGES_TO_SIZE (TotalPagesNum));
180 ASSERT (LongModeBuffer.PageTableAddress != 0);
181 PcdSet64 (PcdIdentifyMappingPageTablePtr, LongModeBuffer.PageTableAddress);
182 }
183
184 //
185 // Allocate stack
186 //
187 LongModeBuffer.StackSize = PcdGet32 (PcdCapsulePeiLongModeStackSize);
188 LongModeBuffer.StackBaseAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateReservedMemoryBelow4G (PcdGet32 (PcdCapsulePeiLongModeStackSize));
189 ASSERT (LongModeBuffer.StackBaseAddress != 0);
190
191 Status = gRT->SetVariable (
192 EFI_CAPSULE_LONG_MODE_BUFFER_NAME,
193 &gEfiCapsuleVendorGuid,
194 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
195 sizeof (EFI_CAPSULE_LONG_MODE_BUFFER),
196 &LongModeBuffer
197 );
198 if (!EFI_ERROR (Status)) {
199 //
200 // Register callback function upon VariableLockProtocol
201 // to lock EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to avoid malicious code to update it.
202 //
203 EfiCreateProtocolNotifyEvent (
204 &gEdkiiVariableLockProtocolGuid,
205 TPL_CALLBACK,
206 VariableLockCapsuleLongModeBufferVariable,
207 NULL,
208 &Registration
209 );
210 } else {
211 DEBUG ((EFI_D_ERROR, "FATAL ERROR: CapsuleLongModeBuffer cannot be saved: %r. Capsule in PEI may fail!\n", Status));
212 gBS->FreePages (LongModeBuffer.StackBaseAddress, EFI_SIZE_TO_PAGES (LongModeBuffer.StackSize));
213 }
214 }
215
216 /**
217 Create the variable to save the base address of page table and stack
218 for transferring into long mode in IA32 capsule PEI.
219 **/
220 VOID
221 SaveLongModeContext (
222 VOID
223 )
224 {
225 if ((FeaturePcdGet(PcdSupportUpdateCapsuleReset)) && (FeaturePcdGet (PcdDxeIplSwitchToLongMode))) {
226 //
227 // Allocate memory for Capsule IA32 PEIM, it will create page table to transfer to long mode to access capsule above 4GB.
228 //
229 PrepareContextForCapsulePei ();
230 }
231 }