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