]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/CapsuleRuntimeDxe/X64/SaveLongModeContext.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[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 - 2018, Intel Corporation. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
10 #include <Uefi.h>
11
12 #include <Protocol/Capsule.h>
13 #include <Protocol/DxeSmmReadyToLock.h>
14 #include <Protocol/VariableLock.h>
15
16 #include <Guid/CapsuleVendor.h>
17 #include <Guid/AcpiS3Context.h>
18
19 #include <Library/DebugLib.h>
20 #include <Library/PcdLib.h>
21 #include <Library/UefiBootServicesTableLib.h>
22 #include <Library/UefiRuntimeServicesTableLib.h>
23 #include <Library/UefiRuntimeLib.h>
24 #include <Library/BaseLib.h>
25 #include <Library/UefiLib.h>
26 #include <Library/BaseMemoryLib.h>
27
28 //
29 // 8 extra pages for PF handler.
30 //
31 #define EXTRA_PAGE_TABLE_PAGES 8
32
33 /**
34 Allocate EfiReservedMemoryType below 4G memory address.
35
36 This function allocates EfiReservedMemoryType below 4G memory address.
37
38 @param Size Size of memory to allocate.
39
40 @return Allocated Address for output.
41
42 **/
43 VOID*
44 AllocateReservedMemoryBelow4G (
45 IN UINTN Size
46 )
47 {
48 UINTN Pages;
49 EFI_PHYSICAL_ADDRESS Address;
50 EFI_STATUS Status;
51 VOID* Buffer;
52
53 Pages = EFI_SIZE_TO_PAGES (Size);
54 Address = 0xffffffff;
55
56 Status = gBS->AllocatePages (
57 AllocateMaxAddress,
58 EfiReservedMemoryType,
59 Pages,
60 &Address
61 );
62 ASSERT_EFI_ERROR (Status);
63
64 Buffer = (VOID *) (UINTN) Address;
65 ZeroMem (Buffer, Size);
66
67 return Buffer;
68 }
69
70 /**
71 Register callback function upon VariableLockProtocol
72 to lock EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to avoid malicious code to update it.
73
74 @param[in] Event Event whose notification function is being invoked.
75 @param[in] Context Pointer to the notification function's context.
76 **/
77 VOID
78 EFIAPI
79 VariableLockCapsuleLongModeBufferVariable (
80 IN EFI_EVENT Event,
81 IN VOID *Context
82 )
83 {
84 EFI_STATUS Status;
85 EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
86 //
87 // Mark EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to read-only if the Variable Lock protocol exists
88 //
89 Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock);
90 if (!EFI_ERROR (Status)) {
91 Status = VariableLock->RequestToLock (VariableLock, EFI_CAPSULE_LONG_MODE_BUFFER_NAME, &gEfiCapsuleVendorGuid);
92 ASSERT_EFI_ERROR (Status);
93 }
94 }
95
96 /**
97 1. Allocate Reserved memory for capsule PEIM to establish a 1:1 Virtual to Physical mapping.
98 2. Allocate Reserved memroy as a stack for capsule PEIM to transfer from 32-bit mdoe to 64-bit mode.
99
100 **/
101 VOID
102 EFIAPI
103 PrepareContextForCapsulePei (
104 VOID
105 )
106 {
107 UINTN ExtraPageTablePages;
108 UINT32 RegEax;
109 UINT32 RegEdx;
110 UINTN TotalPagesNum;
111 UINT8 PhysicalAddressBits;
112 UINT32 NumberOfPml4EntriesNeeded;
113 UINT32 NumberOfPdpEntriesNeeded;
114 BOOLEAN Page1GSupport;
115 EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer;
116 EFI_STATUS Status;
117 VOID *Registration;
118
119 //
120 // Calculate the size of page table, allocate the memory.
121 //
122 Page1GSupport = FALSE;
123 if (PcdGetBool(PcdUse1GPageTable)) {
124 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);
125 if (RegEax >= 0x80000001) {
126 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);
127 if ((RegEdx & BIT26) != 0) {
128 Page1GSupport = TRUE;
129 }
130 }
131 }
132
133 //
134 // Create 4G page table by default,
135 // and let PF handler to handle > 4G request.
136 //
137 PhysicalAddressBits = 32;
138 ExtraPageTablePages = EXTRA_PAGE_TABLE_PAGES;
139
140 //
141 // Calculate the table entries needed.
142 //
143 if (PhysicalAddressBits <= 39 ) {
144 NumberOfPml4EntriesNeeded = 1;
145 NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));
146 } else {
147 NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));
148 NumberOfPdpEntriesNeeded = 512;
149 }
150
151 if (!Page1GSupport) {
152 TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1;
153 } else {
154 TotalPagesNum = NumberOfPml4EntriesNeeded + 1;
155 }
156 TotalPagesNum += ExtraPageTablePages;
157 DEBUG ((DEBUG_INFO, "CapsuleRuntimeDxe X64 TotalPagesNum - 0x%x pages\n", TotalPagesNum));
158
159 LongModeBuffer.PageTableAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateReservedMemoryBelow4G (EFI_PAGES_TO_SIZE (TotalPagesNum));
160 ASSERT (LongModeBuffer.PageTableAddress != 0);
161
162 //
163 // Allocate stack
164 //
165 LongModeBuffer.StackSize = PcdGet32 (PcdCapsulePeiLongModeStackSize);
166 LongModeBuffer.StackBaseAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateReservedMemoryBelow4G (PcdGet32 (PcdCapsulePeiLongModeStackSize));
167 ASSERT (LongModeBuffer.StackBaseAddress != 0);
168
169 Status = gRT->SetVariable (
170 EFI_CAPSULE_LONG_MODE_BUFFER_NAME,
171 &gEfiCapsuleVendorGuid,
172 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
173 sizeof (EFI_CAPSULE_LONG_MODE_BUFFER),
174 &LongModeBuffer
175 );
176 if (!EFI_ERROR (Status)) {
177 //
178 // Register callback function upon VariableLockProtocol
179 // to lock EFI_CAPSULE_LONG_MODE_BUFFER_NAME variable to avoid malicious code to update it.
180 //
181 EfiCreateProtocolNotifyEvent (
182 &gEdkiiVariableLockProtocolGuid,
183 TPL_CALLBACK,
184 VariableLockCapsuleLongModeBufferVariable,
185 NULL,
186 &Registration
187 );
188 } else {
189 DEBUG ((EFI_D_ERROR, "FATAL ERROR: CapsuleLongModeBuffer cannot be saved: %r. Capsule in PEI may fail!\n", Status));
190 gBS->FreePages (LongModeBuffer.StackBaseAddress, EFI_SIZE_TO_PAGES (LongModeBuffer.StackSize));
191 }
192 }
193
194 /**
195 Create the variable to save the base address of page table and stack
196 for transferring into long mode in IA32 capsule PEI.
197 **/
198 VOID
199 SaveLongModeContext (
200 VOID
201 )
202 {
203 if ((FeaturePcdGet(PcdSupportUpdateCapsuleReset)) && (FeaturePcdGet (PcdDxeIplSwitchToLongMode))) {
204 //
205 // Allocate memory for Capsule IA32 PEIM, it will create page table to transfer to long mode to access capsule above 4GB.
206 //
207 PrepareContextForCapsulePei ();
208 }
209 }