]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/CapsuleRuntimeDxe/X64/SaveLongModeContext.c
Add capsule > 4GB support. When capsule data is put above 4GB, IA32 PEI transfers...
[mirror_edk2.git] / MdeModulePkg / Universal / CapsuleRuntimeDxe / X64 / SaveLongModeContext.c
CommitLineData
ab7017fe 1/** @file\r
2 Create the variable to save the base address of page table and stack\r
3 for transferring into long mode in IA32 capsule PEI.\r
4\r
5Copyright (c) 2011, Intel Corporation. All rights reserved.<BR>\r
6This program and the accompanying materials\r
7are licensed and made available under the terms and conditions of the BSD License\r
8which accompanies this distribution. The full text of the license may be found at\r
9http://opensource.org/licenses/bsd-license.php\r
10\r
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
15\r
16#include <Uefi.h>\r
17\r
18#include <Protocol/Capsule.h>\r
19#include <Protocol/DxeSmmReadyToLock.h>\r
20\r
21#include <Guid/CapsuleVendor.h>\r
22#include <Guid/AcpiS3Context.h>\r
23\r
24#include <Library/DebugLib.h>\r
25#include <Library/PcdLib.h>\r
26#include <Library/UefiBootServicesTableLib.h>\r
27#include <Library/UefiRuntimeServicesTableLib.h>\r
28#include <Library/UefiRuntimeLib.h>\r
29#include <Library/BaseLib.h>\r
30#include <Library/LockBoxLib.h>\r
31#include <Library/UefiLib.h>\r
32#include <Library/BaseMemoryLib.h>\r
33#include <Library/HobLib.h>\r
34\r
35/**\r
36 Allocate EfiACPIMemoryNVS below 4G memory address.\r
37\r
38 This function allocates EfiACPIMemoryNVS below 4G memory address.\r
39\r
40 @param Size Size of memory to allocate.\r
41 \r
42 @return Allocated address for output.\r
43\r
44**/\r
45VOID*\r
46AllocateAcpiNvsMemoryBelow4G (\r
47 IN UINTN Size\r
48 )\r
49{\r
50 UINTN Pages;\r
51 EFI_PHYSICAL_ADDRESS Address;\r
52 EFI_STATUS Status;\r
53 VOID* Buffer;\r
54\r
55 Pages = EFI_SIZE_TO_PAGES (Size);\r
56 Address = 0xffffffff;\r
57\r
58 Status = gBS->AllocatePages (\r
59 AllocateMaxAddress,\r
60 EfiACPIMemoryNVS,\r
61 Pages,\r
62 &Address\r
63 );\r
64 ASSERT_EFI_ERROR (Status);\r
65\r
66 Buffer = (VOID *) (UINTN) Address;\r
67 ZeroMem (Buffer, Size);\r
68\r
69 return Buffer;\r
70}\r
71\r
72/**\r
73 DxeSmmReadyToLock Protocol notification event handler.\r
74 We reuse S3 ACPI NVS reserved memory to do capsule process\r
75 after reset.\r
76\r
77 @param[in] Event Event whose notification function is being invoked.\r
78 @param[in] Context Pointer to the notification function's context.\r
79**/\r
80VOID\r
81EFIAPI\r
82DxeSmmReadyToLockNotification (\r
83 IN EFI_EVENT Event,\r
84 IN VOID *Context\r
85 )\r
86{\r
87 EFI_STATUS Status;\r
88 VOID *DxeSmmReadyToLock;\r
89 UINTN VarSize;\r
90 EFI_PHYSICAL_ADDRESS TempAcpiS3Context;\r
91 ACPI_S3_CONTEXT *AcpiS3Context;\r
92 EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer;\r
93 UINTN TotalPagesNum;\r
94 UINT8 PhysicalAddressBits;\r
95 VOID *Hob;\r
96 UINT32 NumberOfPml4EntriesNeeded;\r
97 UINT32 NumberOfPdpEntriesNeeded;\r
98 BOOLEAN LockBoxFound;\r
99\r
100 Status = gBS->LocateProtocol (\r
101 &gEfiDxeSmmReadyToLockProtocolGuid,\r
102 NULL,\r
103 &DxeSmmReadyToLock\r
104 );\r
105 if (EFI_ERROR (Status)) {\r
106 return ;\r
107 }\r
108\r
109 //\r
110 // Get the ACPI NVS pages reserved by AcpiS3Save\r
111 //\r
112 LockBoxFound = FALSE;\r
113 VarSize = sizeof (EFI_PHYSICAL_ADDRESS);\r
114 Status = RestoreLockBox (\r
115 &gEfiAcpiVariableGuid,\r
116 &TempAcpiS3Context,\r
117 &VarSize\r
118 );\r
119 if (!EFI_ERROR (Status)) {\r
120 AcpiS3Context = (ACPI_S3_CONTEXT *)(UINTN)TempAcpiS3Context;\r
121 ASSERT (AcpiS3Context != NULL);\r
122 \r
123 Status = RestoreLockBox (\r
124 &gEfiAcpiS3ContextGuid,\r
125 NULL,\r
126 NULL\r
127 );\r
128 if (!EFI_ERROR (Status)) {\r
129 LongModeBuffer.PageTableAddress = AcpiS3Context->S3NvsPageTableAddress;\r
130 LongModeBuffer.StackBaseAddress = AcpiS3Context->BootScriptStackBase;\r
131 LongModeBuffer.StackSize = AcpiS3Context->BootScriptStackSize;\r
132 LockBoxFound = TRUE;\r
133 }\r
134 }\r
135 \r
136 if (!LockBoxFound) {\r
137 //\r
138 // Page table base address and stack base address can not be found in lock box,\r
139 // allocate both here. \r
140 //\r
141\r
142 //\r
143 // Get physical address bits supported from CPU HOB.\r
144 //\r
145 PhysicalAddressBits = 36;\r
146 \r
147 Hob = GetFirstHob (EFI_HOB_TYPE_CPU);\r
148 if (Hob != NULL) {\r
149 PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;\r
150 }\r
151 \r
152 //\r
153 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.\r
154 //\r
155 ASSERT (PhysicalAddressBits <= 52);\r
156 if (PhysicalAddressBits > 48) {\r
157 PhysicalAddressBits = 48;\r
158 }\r
159 \r
160 //\r
161 // Calculate page table size and allocate memory for it.\r
162 //\r
163 if (PhysicalAddressBits <= 39 ) {\r
164 NumberOfPml4EntriesNeeded = 1;\r
165 NumberOfPdpEntriesNeeded = 1 << (PhysicalAddressBits - 30);\r
166 } else {\r
167 NumberOfPml4EntriesNeeded = 1 << (PhysicalAddressBits - 39);\r
168 NumberOfPdpEntriesNeeded = 512;\r
169 }\r
170 \r
171 TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1;\r
172 LongModeBuffer.PageTableAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateAcpiNvsMemoryBelow4G (EFI_PAGES_TO_SIZE (TotalPagesNum));\r
173 ASSERT (LongModeBuffer.PageTableAddress != 0);\r
174 \r
175 //\r
176 // Allocate stack\r
177 //\r
178 LongModeBuffer.StackSize = PcdGet32 (PcdCapsulePeiLongModeStackSize);\r
179 LongModeBuffer.StackBaseAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateAcpiNvsMemoryBelow4G (PcdGet32 (PcdCapsulePeiLongModeStackSize));\r
180 ASSERT (LongModeBuffer.StackBaseAddress != 0);\r
181 }\r
182\r
183 Status = gRT->SetVariable (\r
184 EFI_CAPSULE_LONG_MODE_BUFFER_NAME,\r
185 &gEfiCapsuleVendorGuid,\r
186 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
187 sizeof (EFI_CAPSULE_LONG_MODE_BUFFER),\r
188 &LongModeBuffer\r
189 );\r
190 ASSERT_EFI_ERROR (Status);\r
191\r
192 //\r
193 // Close event, so it will not be invoked again.\r
194 //\r
195 gBS->CloseEvent (Event);\r
196\r
197 return ;\r
198}\r
199\r
200/**\r
201 Create the variable to save the base address of page table and stack\r
202 for transferring into long mode in IA32 capsule PEI.\r
203**/\r
204VOID\r
205SaveLongModeContext (\r
206 VOID\r
207 )\r
208{\r
209 VOID *Registration;\r
210 \r
211 if ((FeaturePcdGet(PcdSupportUpdateCapsuleReset)) && (FeaturePcdGet (PcdDxeIplSwitchToLongMode))) {\r
212 //\r
213 // Register event to get ACPI NVS pages reserved from lock box, these pages will be used by\r
214 // Capsule IA32 PEI to transfer to long mode to access capsule above 4GB.\r
215 //\r
216 EfiCreateProtocolNotifyEvent (\r
217 &gEfiDxeSmmReadyToLockProtocolGuid,\r
218 TPL_CALLBACK,\r
219 DxeSmmReadyToLockNotification,\r
220 NULL,\r
221 &Registration\r
222 );\r
223 }\r
224}\r