• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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