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