]> git.proxmox.com Git - mirror_edk2.git/blob - 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
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, 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
21 #include <Guid/CapsuleVendor.h>
22 #include <Guid/AcpiS3Context.h>
23
24 #include <Library/DebugLib.h>
25 #include <Library/PcdLib.h>
26 #include <Library/UefiBootServicesTableLib.h>
27 #include <Library/UefiRuntimeServicesTableLib.h>
28 #include <Library/UefiRuntimeLib.h>
29 #include <Library/BaseLib.h>
30 #include <Library/LockBoxLib.h>
31 #include <Library/UefiLib.h>
32 #include <Library/BaseMemoryLib.h>
33 #include <Library/HobLib.h>
34
35 /**
36 Allocate EfiACPIMemoryNVS below 4G memory address.
37
38 This function allocates EfiACPIMemoryNVS 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 AllocateAcpiNvsMemoryBelow4G (
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 EfiACPIMemoryNVS,
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 DxeSmmReadyToLock Protocol notification event handler.
74 We reuse S3 ACPI NVS reserved memory to do capsule process
75 after reset.
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 DxeSmmReadyToLockNotification (
83 IN EFI_EVENT Event,
84 IN VOID *Context
85 )
86 {
87 EFI_STATUS Status;
88 VOID *DxeSmmReadyToLock;
89 UINTN VarSize;
90 EFI_PHYSICAL_ADDRESS TempAcpiS3Context;
91 ACPI_S3_CONTEXT *AcpiS3Context;
92 EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer;
93 UINTN TotalPagesNum;
94 UINT8 PhysicalAddressBits;
95 VOID *Hob;
96 UINT32 NumberOfPml4EntriesNeeded;
97 UINT32 NumberOfPdpEntriesNeeded;
98 BOOLEAN LockBoxFound;
99
100 Status = gBS->LocateProtocol (
101 &gEfiDxeSmmReadyToLockProtocolGuid,
102 NULL,
103 &DxeSmmReadyToLock
104 );
105 if (EFI_ERROR (Status)) {
106 return ;
107 }
108
109 //
110 // Get the ACPI NVS pages reserved by AcpiS3Save
111 //
112 LockBoxFound = FALSE;
113 VarSize = sizeof (EFI_PHYSICAL_ADDRESS);
114 Status = RestoreLockBox (
115 &gEfiAcpiVariableGuid,
116 &TempAcpiS3Context,
117 &VarSize
118 );
119 if (!EFI_ERROR (Status)) {
120 AcpiS3Context = (ACPI_S3_CONTEXT *)(UINTN)TempAcpiS3Context;
121 ASSERT (AcpiS3Context != NULL);
122
123 Status = RestoreLockBox (
124 &gEfiAcpiS3ContextGuid,
125 NULL,
126 NULL
127 );
128 if (!EFI_ERROR (Status)) {
129 LongModeBuffer.PageTableAddress = AcpiS3Context->S3NvsPageTableAddress;
130 LongModeBuffer.StackBaseAddress = AcpiS3Context->BootScriptStackBase;
131 LongModeBuffer.StackSize = AcpiS3Context->BootScriptStackSize;
132 LockBoxFound = TRUE;
133 }
134 }
135
136 if (!LockBoxFound) {
137 //
138 // Page table base address and stack base address can not be found in lock box,
139 // allocate both here.
140 //
141
142 //
143 // Get physical address bits supported from CPU HOB.
144 //
145 PhysicalAddressBits = 36;
146
147 Hob = GetFirstHob (EFI_HOB_TYPE_CPU);
148 if (Hob != NULL) {
149 PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;
150 }
151
152 //
153 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
154 //
155 ASSERT (PhysicalAddressBits <= 52);
156 if (PhysicalAddressBits > 48) {
157 PhysicalAddressBits = 48;
158 }
159
160 //
161 // Calculate page table size and allocate memory for it.
162 //
163 if (PhysicalAddressBits <= 39 ) {
164 NumberOfPml4EntriesNeeded = 1;
165 NumberOfPdpEntriesNeeded = 1 << (PhysicalAddressBits - 30);
166 } else {
167 NumberOfPml4EntriesNeeded = 1 << (PhysicalAddressBits - 39);
168 NumberOfPdpEntriesNeeded = 512;
169 }
170
171 TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1;
172 LongModeBuffer.PageTableAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateAcpiNvsMemoryBelow4G (EFI_PAGES_TO_SIZE (TotalPagesNum));
173 ASSERT (LongModeBuffer.PageTableAddress != 0);
174
175 //
176 // Allocate stack
177 //
178 LongModeBuffer.StackSize = PcdGet32 (PcdCapsulePeiLongModeStackSize);
179 LongModeBuffer.StackBaseAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateAcpiNvsMemoryBelow4G (PcdGet32 (PcdCapsulePeiLongModeStackSize));
180 ASSERT (LongModeBuffer.StackBaseAddress != 0);
181 }
182
183 Status = gRT->SetVariable (
184 EFI_CAPSULE_LONG_MODE_BUFFER_NAME,
185 &gEfiCapsuleVendorGuid,
186 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
187 sizeof (EFI_CAPSULE_LONG_MODE_BUFFER),
188 &LongModeBuffer
189 );
190 ASSERT_EFI_ERROR (Status);
191
192 //
193 // Close event, so it will not be invoked again.
194 //
195 gBS->CloseEvent (Event);
196
197 return ;
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 VOID *Registration;
210
211 if ((FeaturePcdGet(PcdSupportUpdateCapsuleReset)) && (FeaturePcdGet (PcdDxeIplSwitchToLongMode))) {
212 //
213 // Register event to get ACPI NVS pages reserved from lock box, these pages will be used by
214 // Capsule IA32 PEI to transfer to long mode to access capsule above 4GB.
215 //
216 EfiCreateProtocolNotifyEvent (
217 &gEfiDxeSmmReadyToLockProtocolGuid,
218 TPL_CALLBACK,
219 DxeSmmReadyToLockNotification,
220 NULL,
221 &Registration
222 );
223 }
224 }