1 /** @file
2   This file implements Runtime Architectural Protocol as defined in the
3   Platform Initialization specification 1.0 VOLUME 2 DXE Core Interface.
4 
5   This code is used to produce the EFI runtime virtual switch over
6 
7   THIS IS VERY DANGEROUS CODE BE VERY CAREFUL IF YOU CHANGE IT
8 
9   The transition for calling EFI Runtime functions in physical mode to calling
10   them in virtual mode is very very complex. Every pointer in needs to be
11   converted from physical mode to virtual mode. Be very careful walking linked
12   lists! Then to make it really hard the code it's self needs be relocated into
13   the new virtual address space.
14 
15   So here is the concept. The code in this module will never ever be called in
16   virtual mode. This is the code that collects the information needed to convert
17   to virtual mode (DXE core registers runtime stuff with this code). Since this
18   code is used to fix up all runtime images, it CAN NOT fix it's self up. So some
19   code has to stay behind and that is us.
20 
21   Also you need to be careful about when you allocate memory, as once we are in
22   runtime (including our EVT_SIGNAL_EXIT_BOOT_SERVICES event) you can no longer
23   allocate memory.
24 
25   Any runtime driver that gets loaded before us will not be callable in virtual
26   mode. This is due to the fact that the DXE core can not register the info
27   needed with us. This is good, since it keeps the code in this file from
28   getting registered.
29 
30 
31 Revision History:
32 
33   - Move the CalculateCrc32 function from Runtime Arch Protocol to Boot Service.
34   Runtime Arch Protocol definition no longer contains CalculateCrc32. Boot Service
35   Table now contains an item named CalculateCrc32.
36 
37 
38 Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
39 This program and the accompanying materials
40 are licensed and made available under the terms and conditions of the BSD License
41 which accompanies this distribution.  The full text of the license may be found at
42 http://opensource.org/licenses/bsd-license.php
43 
44 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
45 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
46 
47 **/
48 
49 #include "Runtime.h"
50 
51 //
52 // Global Variables
53 //
54 EFI_MEMORY_DESCRIPTOR         *mVirtualMap                = NULL;
55 UINTN                         mVirtualMapDescriptorSize;
56 UINTN                         mVirtualMapMaxIndex;
57 VOID                          *mMyImageBase;
58 
59 //
60 // The handle onto which the Runtime Architectural Protocol instance is installed
61 //
62 EFI_HANDLE                    mRuntimeHandle = NULL;
63 
64 //
65 // The Runtime Architectural Protocol instance produced by this driver
66 //
67 EFI_RUNTIME_ARCH_PROTOCOL     mRuntime = {
68   INITIALIZE_LIST_HEAD_VARIABLE (mRuntime.ImageHead),
69   INITIALIZE_LIST_HEAD_VARIABLE (mRuntime.EventHead),
70 
71   //
72   // Make sure Size != sizeof (EFI_MEMORY_DESCRIPTOR). This will
73   // prevent people from having pointer math bugs in their code.
74   // now you have to use *DescriptorSize to make things work.
75   //
76   sizeof (EFI_MEMORY_DESCRIPTOR) + sizeof (UINT64) - (sizeof (EFI_MEMORY_DESCRIPTOR) % sizeof (UINT64)),
77   EFI_MEMORY_DESCRIPTOR_VERSION,
78   0,
79   NULL,
80   NULL,
81   FALSE,
82   FALSE
83 };
84 
85 //
86 // Worker Functions
87 //
88 /**
89 
90   Calculate the 32-bit CRC in a EFI table using the Runtime Drivers
91   internal function.  The EFI Boot Services Table can not be used because
92   the EFI Boot Services Table was destroyed at ExitBootServices().
93   This is a internal function.
94 
95 
96   @param Hdr             Pointer to an EFI standard header
97 
98 **/
99 VOID
RuntimeDriverCalculateEfiHdrCrc(IN OUT EFI_TABLE_HEADER * Hdr)100 RuntimeDriverCalculateEfiHdrCrc (
101   IN OUT EFI_TABLE_HEADER  *Hdr
102   )
103 {
104   UINT32  Crc;
105 
106   Hdr->CRC32  = 0;
107 
108   Crc         = 0;
109   RuntimeDriverCalculateCrc32 ((UINT8 *) Hdr, Hdr->HeaderSize, &Crc);
110   Hdr->CRC32 = Crc;
111 }
112 
113 /**
114 
115   Determines the new virtual address that is to be used on subsequent memory accesses.
116 
117 
118   @param DebugDisposition Supplies type information for the pointer being converted.
119   @param ConvertAddress  A pointer to a pointer that is to be fixed to be the value needed
120                          for the new virtual address mappings being applied.
121 
122   @retval  EFI_SUCCESS              The pointer pointed to by Address was modified.
123   @retval  EFI_NOT_FOUND            The pointer pointed to by Address was not found to be part
124                                     of the current memory map. This is normally fatal.
125   @retval  EFI_INVALID_PARAMETER    One of the parameters has an invalid value.
126 
127 **/
128 EFI_STATUS
129 EFIAPI
RuntimeDriverConvertPointer(IN UINTN DebugDisposition,IN OUT VOID ** ConvertAddress)130 RuntimeDriverConvertPointer (
131   IN     UINTN  DebugDisposition,
132   IN OUT VOID   **ConvertAddress
133   )
134 {
135   UINTN                 Address;
136   UINT64                VirtEndOfRange;
137   EFI_MEMORY_DESCRIPTOR *VirtEntry;
138   UINTN                 Index;
139 
140   //
141   // Make sure ConvertAddress is a valid pointer
142   //
143   if (ConvertAddress == NULL) {
144     return EFI_INVALID_PARAMETER;
145   }
146   //
147   // Get the address to convert
148   //
149   Address = (UINTN) *ConvertAddress;
150 
151   //
152   // If this is a null pointer, return if it's allowed
153   //
154   if (Address == 0) {
155     if ((DebugDisposition & EFI_OPTIONAL_PTR) != 0) {
156       return EFI_SUCCESS;
157     }
158 
159     return EFI_INVALID_PARAMETER;
160   }
161 
162   VirtEntry = mVirtualMap;
163   for (Index = 0; Index < mVirtualMapMaxIndex; Index++) {
164     //
165     //  To prevent the inclusion of 64-bit math functions a UINTN was placed in
166     //  front of VirtEntry->NumberOfPages to cast it to a 32-bit thing on IA-32
167     //  platforms. If you get this ASSERT remove the UINTN and do a 64-bit
168     //  multiply.
169     //
170     ASSERT (((UINTN) VirtEntry->NumberOfPages < 0xffffffff) || (sizeof (UINTN) > 4));
171 
172     if ((VirtEntry->Attribute & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME) {
173       if (Address >= VirtEntry->PhysicalStart) {
174         VirtEndOfRange = VirtEntry->PhysicalStart + (((UINTN) VirtEntry->NumberOfPages) * EFI_PAGE_SIZE);
175         if (Address < VirtEndOfRange) {
176           //
177           // Compute new address
178           //
179           *ConvertAddress = (VOID *) (Address - (UINTN) VirtEntry->PhysicalStart + (UINTN) VirtEntry->VirtualStart);
180           return EFI_SUCCESS;
181         }
182       }
183     }
184 
185     VirtEntry = NEXT_MEMORY_DESCRIPTOR (VirtEntry, mVirtualMapDescriptorSize);
186   }
187 
188   return EFI_NOT_FOUND;
189 }
190 
191 /**
192 
193   Determines the new virtual address that is to be used on subsequent memory accesses
194   for internal pointers.
195   This is a internal function.
196 
197 
198   @param ConvertAddress  A pointer to a pointer that is to be fixed to be the value needed
199                          for the new virtual address mappings being applied.
200 
201   @retval  EFI_SUCCESS              The pointer pointed to by Address was modified.
202   @retval  EFI_NOT_FOUND            The pointer pointed to by Address was not found to be part
203                                     of the current memory map. This is normally fatal.
204   @retval  EFI_INVALID_PARAMETER    One of the parameters has an invalid value.
205 
206 **/
207 EFI_STATUS
RuntimeDriverConvertInternalPointer(IN OUT VOID ** ConvertAddress)208 RuntimeDriverConvertInternalPointer (
209   IN OUT VOID   **ConvertAddress
210   )
211 {
212   return RuntimeDriverConvertPointer (0x0, ConvertAddress);
213 }
214 
215 /**
216 
217   Changes the runtime addressing mode of EFI firmware from physical to virtual.
218 
219 
220   @param MemoryMapSize   The size in bytes of VirtualMap.
221   @param DescriptorSize  The size in bytes of an entry in the VirtualMap.
222   @param DescriptorVersion The version of the structure entries in VirtualMap.
223   @param VirtualMap      An array of memory descriptors which contain new virtual
224                          address mapping information for all runtime ranges.
225 
226   @retval  EFI_SUCCESS            The virtual address map has been applied.
227   @retval  EFI_UNSUPPORTED        EFI firmware is not at runtime, or the EFI firmware is already in
228                                   virtual address mapped mode.
229   @retval  EFI_INVALID_PARAMETER  DescriptorSize or DescriptorVersion is invalid.
230   @retval  EFI_NO_MAPPING         A virtual address was not supplied for a range in the memory
231                                   map that requires a mapping.
232   @retval  EFI_NOT_FOUND          A virtual address was supplied for an address that is not found
233                                   in the memory map.
234 
235 **/
236 EFI_STATUS
237 EFIAPI
RuntimeDriverSetVirtualAddressMap(IN UINTN MemoryMapSize,IN UINTN DescriptorSize,IN UINT32 DescriptorVersion,IN EFI_MEMORY_DESCRIPTOR * VirtualMap)238 RuntimeDriverSetVirtualAddressMap (
239   IN UINTN                  MemoryMapSize,
240   IN UINTN                  DescriptorSize,
241   IN UINT32                 DescriptorVersion,
242   IN EFI_MEMORY_DESCRIPTOR  *VirtualMap
243   )
244 {
245   EFI_STATUS                    Status;
246   EFI_RUNTIME_EVENT_ENTRY       *RuntimeEvent;
247   EFI_RUNTIME_IMAGE_ENTRY       *RuntimeImage;
248   LIST_ENTRY                    *Link;
249   EFI_PHYSICAL_ADDRESS          VirtImageBase;
250 
251   //
252   // Can only switch to virtual addresses once the memory map is locked down,
253   // and can only set it once
254   //
255   if (!mRuntime.AtRuntime || mRuntime.VirtualMode) {
256     return EFI_UNSUPPORTED;
257   }
258   //
259   // Only understand the original descriptor format
260   //
261   if (DescriptorVersion != EFI_MEMORY_DESCRIPTOR_VERSION || DescriptorSize < sizeof (EFI_MEMORY_DESCRIPTOR)) {
262     return EFI_INVALID_PARAMETER;
263   }
264   //
265   // We are now committed to go to virtual mode, so lets get to it!
266   //
267   mRuntime.VirtualMode = TRUE;
268 
269   //
270   // ConvertPointer() needs this mVirtualMap to do the conversion. So set up
271   // globals we need to parse the virtual address map.
272   //
273   mVirtualMapDescriptorSize = DescriptorSize;
274   mVirtualMapMaxIndex       = MemoryMapSize / DescriptorSize;
275   mVirtualMap               = VirtualMap;
276 
277   //
278   // ReporstStatusCodeLib will check and make sure this service can be called in runtime mode.
279   //
280   REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_EFI_RUNTIME_SERVICE | EFI_SW_RS_PC_SET_VIRTUAL_ADDRESS_MAP));
281 
282   //
283   // Report Status Code here since EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event will be signalled.
284   //
285   REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_VIRTUAL_ADDRESS_CHANGE_EVENT));
286 
287   //
288   // Signal all the EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE events.
289   // All runtime events are stored in a list in Runtime AP.
290   //
291   for (Link = mRuntime.EventHead.ForwardLink; Link != &mRuntime.EventHead; Link = Link->ForwardLink) {
292     RuntimeEvent = BASE_CR (Link, EFI_RUNTIME_EVENT_ENTRY, Link);
293     if ((RuntimeEvent->Type & EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE) == EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE) {
294       RuntimeEvent->NotifyFunction (
295                       RuntimeEvent->Event,
296                       RuntimeEvent->NotifyContext
297                       );
298     }
299   }
300 
301   //
302   // Relocate runtime images. All runtime images are stored in a list in Runtime AP.
303   //
304   for (Link = mRuntime.ImageHead.ForwardLink; Link != &mRuntime.ImageHead; Link = Link->ForwardLink) {
305     RuntimeImage = BASE_CR (Link, EFI_RUNTIME_IMAGE_ENTRY, Link);
306     //
307     // We don't want to relocate our selves, as we only run in physical mode.
308     //
309     if (mMyImageBase != RuntimeImage->ImageBase) {
310 
311       VirtImageBase = (EFI_PHYSICAL_ADDRESS) (UINTN) RuntimeImage->ImageBase;
312       Status  = RuntimeDriverConvertPointer (0, (VOID **) &VirtImageBase);
313       ASSERT_EFI_ERROR (Status);
314 
315       PeCoffLoaderRelocateImageForRuntime (
316         (EFI_PHYSICAL_ADDRESS) (UINTN) RuntimeImage->ImageBase,
317         VirtImageBase,
318         (UINTN) RuntimeImage->ImageSize,
319         RuntimeImage->RelocationData
320         );
321 
322       InvalidateInstructionCacheRange (RuntimeImage->ImageBase, (UINTN) RuntimeImage->ImageSize);
323     }
324   }
325 
326   //
327   // Convert all the Runtime Services except ConvertPointer() and SetVirtualAddressMap()
328   // and recompute the CRC-32
329   //
330   RuntimeDriverConvertInternalPointer ((VOID **) &gRT->GetTime);
331   RuntimeDriverConvertInternalPointer ((VOID **) &gRT->SetTime);
332   RuntimeDriverConvertInternalPointer ((VOID **) &gRT->GetWakeupTime);
333   RuntimeDriverConvertInternalPointer ((VOID **) &gRT->SetWakeupTime);
334   RuntimeDriverConvertInternalPointer ((VOID **) &gRT->ResetSystem);
335   RuntimeDriverConvertInternalPointer ((VOID **) &gRT->GetNextHighMonotonicCount);
336   RuntimeDriverConvertInternalPointer ((VOID **) &gRT->GetVariable);
337   RuntimeDriverConvertInternalPointer ((VOID **) &gRT->SetVariable);
338   RuntimeDriverConvertInternalPointer ((VOID **) &gRT->GetNextVariableName);
339   RuntimeDriverConvertInternalPointer ((VOID **) &gRT->QueryVariableInfo);
340   RuntimeDriverConvertInternalPointer ((VOID **) &gRT->UpdateCapsule);
341   RuntimeDriverConvertInternalPointer ((VOID **) &gRT->QueryCapsuleCapabilities);
342   RuntimeDriverCalculateEfiHdrCrc (&gRT->Hdr);
343 
344   //
345   // UEFI don't require System Configuration Tables Conversion.
346   //
347 
348   //
349   // Convert the runtime fields of the EFI System Table and recompute the CRC-32
350   //
351   RuntimeDriverConvertInternalPointer ((VOID **) &gST->FirmwareVendor);
352   RuntimeDriverConvertInternalPointer ((VOID **) &gST->ConfigurationTable);
353   RuntimeDriverConvertInternalPointer ((VOID **) &gST->RuntimeServices);
354   RuntimeDriverCalculateEfiHdrCrc (&gST->Hdr);
355 
356   //
357   // At this point, gRT and gST are physical pointers, but the contents of these tables
358   // have been converted to runtime.
359   //
360   //
361   // mVirtualMap is only valid during SetVirtualAddressMap() call
362   //
363   mVirtualMap = NULL;
364 
365   return EFI_SUCCESS;
366 }
367 
368 /**
369   Entry Point for Runtime driver.
370 
371   This function installs Runtime Architectural Protocol and registers CalculateCrc32 boot services table,
372   SetVirtualAddressMap & ConvertPointer runtime services table.
373 
374   @param ImageHandle     Image handle of this driver.
375   @param SystemTable     a Pointer to the EFI System Table.
376 
377   @retval  EFI_SUCEESS  Runtime Driver Architectural Protocol is successfully installed
378   @return  Others       Some error occurs when installing Runtime Driver Architectural Protocol.
379 
380 **/
381 EFI_STATUS
382 EFIAPI
RuntimeDriverInitialize(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)383 RuntimeDriverInitialize (
384   IN EFI_HANDLE                            ImageHandle,
385   IN EFI_SYSTEM_TABLE                      *SystemTable
386   )
387 {
388   EFI_STATUS                Status;
389   EFI_LOADED_IMAGE_PROTOCOL *MyLoadedImage;
390 
391   //
392   // This image needs to be excluded from relocation for virtual mode, so cache
393   // a copy of the Loaded Image protocol to test later.
394   //
395   Status = gBS->HandleProtocol (
396                   ImageHandle,
397                   &gEfiLoadedImageProtocolGuid,
398                   (VOID**)&MyLoadedImage
399                   );
400   ASSERT_EFI_ERROR (Status);
401   mMyImageBase = MyLoadedImage->ImageBase;
402 
403   //
404   // Initialize the table used to compute 32-bit CRCs
405   //
406   RuntimeDriverInitializeCrc32Table ();
407 
408   //
409   // Fill in the entries of the EFI Boot Services and EFI Runtime Services Tables
410   //
411   gBS->CalculateCrc32       = RuntimeDriverCalculateCrc32;
412   gRT->SetVirtualAddressMap = RuntimeDriverSetVirtualAddressMap;
413   gRT->ConvertPointer       = RuntimeDriverConvertPointer;
414 
415   //
416   // Install the Runtime Architectural Protocol onto a new handle
417   //
418   Status = gBS->InstallMultipleProtocolInterfaces (
419                   &mRuntimeHandle,
420                   &gEfiRuntimeArchProtocolGuid,
421                   &mRuntime,
422                   NULL
423                   );
424   ASSERT_EFI_ERROR (Status);
425 
426   return Status;
427 }
428