/*++ | |
Copyright (c) 2006, Intel Corporation | |
All rights reserved. This program and the accompanying materials | |
are licensed and made available under the terms and conditions of the BSD License | |
which accompanies this distribution. The full text of the license may be found at | |
http://opensource.org/licenses/bsd-license.php | |
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
Module Name: | |
Runtime.c | |
Abstract: | |
Runtime Architectural Protocol as defined in the DXE CIS | |
This code is used to produce the EFI runtime virtual switch over | |
THIS IS VERY DANGEROUS CODE BE VERY CAREFUL IF YOU CHANGE IT | |
The transition for calling EFI Runtime functions in physical mode to calling | |
them in virtual mode is very very complex. Every pointer in needs to be | |
converted from physical mode to virtual mode. Be very careful walking linked | |
lists! Then to make it really hard the code it's self needs be relocated into | |
the new virtual address space. | |
So here is the concept. The code in this module will never ever be called in | |
virtual mode. This is the code that collects the information needed to convert | |
to virtual mode (DXE core registers runtime stuff with this code). Since this | |
code is used to fixup all runtime images, it CAN NOT fix it's self up. So some | |
code has to stay behind and that is us. | |
Also you need to be careful about when you allocate memory, as once we are in | |
runtime (including our EVT_SIGNAL_EXIT_BOOT_SERVICES event) you can no longer | |
allocate memory. | |
Any runtime driver that gets loaded before us will not be callable in virtual | |
mode. This is due to the fact that the DXE core can not register the info | |
needed with us. This is good, since it keeps the code in this file from | |
getting registered. | |
Revision History: | |
- Move the CalculateCrc32 function from Runtime Arch Protocol to Boot Service. | |
Runtime Arch Protocol definition no longer contains CalculateCrc32. Boot Service | |
Table now contains an item named CalculateCrc32. | |
--*/ | |
#include "Runtime.h" | |
// | |
// This is a only short term solution. | |
// There is a change coming to the Runtime AP that | |
// will make it so the Runtime driver will not have to allocate any buffers. | |
// | |
#define MAX_RUNTIME_IMAGE_NUM (64) | |
#define MAX_RUNTIME_EVENT_NUM (64) | |
RUNTIME_IMAGE_RELOCATION_DATA mRuntimeImageBuffer[MAX_RUNTIME_IMAGE_NUM]; | |
RUNTIME_NOTIFY_EVENT_DATA mRuntimeEventBuffer[MAX_RUNTIME_EVENT_NUM]; | |
UINTN mRuntimeImageNumber; | |
UINTN mRuntimeEventNumber; | |
// | |
// The handle onto which the Runtime Architectural Protocol instance is installed | |
// | |
EFI_HANDLE mRuntimeHandle = NULL; | |
// | |
// The Runtime Architectural Protocol instance produced by this driver | |
// | |
EFI_RUNTIME_ARCH_PROTOCOL mRuntime = { | |
RuntimeDriverRegisterImage, | |
RuntimeDriverRegisterEvent | |
}; | |
// | |
// Global Variables | |
// | |
LIST_ENTRY mRelocationList = INITIALIZE_LIST_HEAD_VARIABLE(mRelocationList); | |
LIST_ENTRY mEventList = INITIALIZE_LIST_HEAD_VARIABLE(mEventList); | |
BOOLEAN mEfiVirtualMode = FALSE; | |
EFI_GUID mLocalEfiUgaIoProtocolGuid = EFI_UGA_IO_PROTOCOL_GUID; | |
EFI_MEMORY_DESCRIPTOR *mVirtualMap = NULL; | |
UINTN mVirtualMapDescriptorSize; | |
UINTN mVirtualMapMaxIndex; | |
EFI_LOADED_IMAGE_PROTOCOL *mMyLoadedImage; | |
// | |
// Worker Functions | |
// | |
VOID | |
RuntimeDriverCalculateEfiHdrCrc ( | |
IN OUT EFI_TABLE_HEADER *Hdr | |
) | |
/*++ | |
Routine Description: | |
Calcualte the 32-bit CRC in a EFI table using the Runtime Drivers | |
internal function. The EFI Boot Services Table can not be used because | |
the EFI Boot Services Table was destroyed at ExitBootServices() | |
Arguments: | |
Hdr - Pointer to an EFI standard header | |
Returns: | |
None | |
--*/ | |
{ | |
UINT32 Crc; | |
Hdr->CRC32 = 0; | |
Crc = 0; | |
RuntimeDriverCalculateCrc32 ((UINT8 *) Hdr, Hdr->HeaderSize, &Crc); | |
Hdr->CRC32 = Crc; | |
} | |
EFI_STATUS | |
EFIAPI | |
RuntimeDriverRegisterImage ( | |
IN EFI_RUNTIME_ARCH_PROTOCOL *This, | |
IN EFI_PHYSICAL_ADDRESS ImageBase, | |
IN UINTN ImageSize, | |
IN VOID *RelocationData | |
) | |
/*++ | |
Routine Description: | |
When a SetVirtualAddressMap() is performed all the runtime images loaded by | |
DXE must be fixed up with the new virtual address map. To facilitate this the | |
Runtime Architectural Protocol needs to be informed of every runtime driver | |
that is registered. All the runtime images loaded by DXE should be registered | |
with this service by the DXE Core when ExitBootServices() is called. The | |
images that are registered with this service must have successfully been | |
loaded into memory with the Boot Service LoadImage(). As a result, no | |
parameter checking needs to be performed. | |
Arguments: | |
This - The EFI_RUNTIME_ARCH_PROTOCOL instance. | |
ImageBase - Start of image that has been loaded in memory. It is either | |
a pointer to the DOS or PE header of the image. | |
ImageSize - Size of the image in bytes. | |
RelocationData - Information about the fixups that were performed on ImageBase | |
when it was loaded into memory. This information is needed | |
when the virtual mode fix-ups are reapplied so that data that | |
has been programmatically updated will not be fixed up. If | |
code updates a global variable the code is responsible for | |
fixing up the variable for virtual mode. | |
Returns: | |
EFI_SUCCESS - The ImageBase has been registered. | |
--*/ | |
{ | |
RUNTIME_IMAGE_RELOCATION_DATA *RuntimeImage; | |
if (mMyLoadedImage->ImageBase == (VOID *) (UINTN) ImageBase) { | |
// | |
// We don't want to relocate our selves, as we only run in physical mode. | |
// | |
return EFI_SUCCESS; | |
} | |
RuntimeImage = &mRuntimeImageBuffer[mRuntimeImageNumber]; | |
mRuntimeImageNumber++; | |
ASSERT (mRuntimeImageNumber < MAX_RUNTIME_IMAGE_NUM); | |
RuntimeImage->Valid = TRUE; | |
RuntimeImage->ImageBase = ImageBase; | |
RuntimeImage->ImageSize = ImageSize; | |
RuntimeImage->RelocationData = RelocationData; | |
InsertTailList (&mRelocationList, &RuntimeImage->Link); | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
RuntimeDriverRegisterEvent ( | |
IN EFI_RUNTIME_ARCH_PROTOCOL *This, | |
IN UINT32 Type, | |
IN EFI_TPL NotifyTpl, | |
IN EFI_EVENT_NOTIFY NotifyFunction, | |
IN VOID *NotifyContext, | |
IN EFI_EVENT *Event | |
) | |
/*++ | |
Routine Description: | |
This function is used to support the required runtime events. Currently only | |
runtime events of type EFI_EVENT_SIGNAL_VIRTUAL_ADDRESS_CHANGE needs to be | |
registered with this service. All the runtime events that exist in the DXE | |
Core should be registered with this service when ExitBootServices() is called. | |
All the events that are registered with this service must have been created | |
with the Boot Service CreateEvent(). As a result, no parameter checking needs | |
to be performed. | |
Arguments: | |
This - The EFI_RUNTIME_ARCH_PROTOCOL instance. | |
Type - The same as Type passed into CreateEvent(). | |
NotifyTpl - The same as NotifyTpl passed into CreateEvent(). | |
NotifyFunction - The same as NotifyFunction passed into CreateEvent(). | |
NotifyContext - The same as NotifyContext passed into CreateEvent(). | |
Event - The EFI_EVENT returned by CreateEvent(). Event must be in | |
runtime memory. | |
Returns: | |
EFI_SUCCESS - The Event has been registered. | |
--*/ | |
{ | |
RUNTIME_NOTIFY_EVENT_DATA *RuntimeEvent; | |
RuntimeEvent = &mRuntimeEventBuffer[mRuntimeEventNumber]; | |
mRuntimeEventNumber++; | |
ASSERT (mRuntimeEventNumber < MAX_RUNTIME_EVENT_NUM); | |
RuntimeEvent->Type = Type; | |
RuntimeEvent->NotifyTpl = NotifyTpl; | |
RuntimeEvent->NotifyFunction = NotifyFunction; | |
RuntimeEvent->NotifyContext = NotifyContext; | |
RuntimeEvent->Event = Event; | |
InsertTailList (&mEventList, &RuntimeEvent->Link); | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
RuntimeDriverConvertPointer ( | |
IN UINTN DebugDisposition, | |
IN OUT VOID **ConvertAddress | |
) | |
{ | |
UINTN Address; | |
VOID *PlabelConvertAddress; | |
UINT64 VirtEndOfRange; | |
EFI_MEMORY_DESCRIPTOR *VirtEntry; | |
UINTN Index; | |
// | |
// Make sure ConvertAddress is a valid pointer | |
// | |
if (ConvertAddress == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Get the address to convert | |
// | |
Address = (UINTN) *ConvertAddress; | |
// | |
// If this is a null pointer, return if it's allowed | |
// | |
if (Address == 0) { | |
if (DebugDisposition & EFI_OPTIONAL_POINTER) { | |
return EFI_SUCCESS; | |
} | |
return EFI_INVALID_PARAMETER; | |
} | |
PlabelConvertAddress = NULL; | |
VirtEntry = mVirtualMap; | |
for (Index = 0; Index < mVirtualMapMaxIndex; Index++) { | |
// | |
// To prevent the inclusion of 64-bit math functions a UINTN was placed in | |
// front of VirtEntry->NumberOfPages to cast it to a 32-bit thing on IA-32 | |
// platforms. If you get this ASSERT remove the UINTN and do a 64-bit | |
// multiply. | |
// | |
ASSERT ((VirtEntry->NumberOfPages < 0xffffffff) || (sizeof (UINTN) > 4)); | |
if ((VirtEntry->Attribute & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME) { | |
if (Address >= VirtEntry->PhysicalStart) { | |
VirtEndOfRange = VirtEntry->PhysicalStart + (((UINTN) VirtEntry->NumberOfPages) * EFI_PAGE_SIZE); | |
if (Address < VirtEndOfRange) { | |
// | |
// Compute new address | |
// | |
*ConvertAddress = (VOID *) (Address - (UINTN) VirtEntry->PhysicalStart + (UINTN) VirtEntry->VirtualStart); | |
return EFI_SUCCESS; | |
} else if (Address < (VirtEndOfRange + 0x200000)) { | |
// | |
// On Itanium GP defines a window +/- 2 MB inside an image. | |
// The compiler may asign a GP value outside of the image. Thus | |
// it could fall out side of any of our valid regions | |
// | |
PlabelConvertAddress = (VOID *) (Address - (UINTN) VirtEntry->PhysicalStart + (UINTN) VirtEntry->VirtualStart); | |
} | |
} | |
} | |
VirtEntry = NextMemoryDescriptor (VirtEntry, mVirtualMapDescriptorSize); | |
} | |
if (DebugDisposition & EFI_IPF_GP_POINTER) { | |
// | |
// If it's an IPF GP and the GP was outside the image handle that case. | |
// | |
if (PlabelConvertAddress != NULL) { | |
*ConvertAddress = PlabelConvertAddress; | |
return EFI_SUCCESS; | |
} | |
} | |
return EFI_NOT_FOUND; | |
} | |
EFI_STATUS | |
RuntimeDriverConvertInternalPointer ( | |
IN OUT VOID **ConvertAddress | |
) | |
{ | |
return RuntimeDriverConvertPointer (0x0, ConvertAddress); | |
} | |
EFI_STATUS | |
EFIAPI | |
RuntimeDriverSetVirtualAddressMap ( | |
IN UINTN MemoryMapSize, | |
IN UINTN DescriptorSize, | |
IN UINT32 DescriptorVersion, | |
IN EFI_MEMORY_DESCRIPTOR *VirtualMap | |
) | |
{ | |
RUNTIME_NOTIFY_EVENT_DATA *RuntimeEvent; | |
RUNTIME_IMAGE_RELOCATION_DATA *RuntimeImage; | |
LIST_ENTRY *Link; | |
UINTN Index; | |
UINTN Index1; | |
EFI_DRIVER_OS_HANDOFF_HEADER *DriverOsHandoffHeader; | |
EFI_DRIVER_OS_HANDOFF *DriverOsHandoff; | |
// | |
// Can only switch to virtual addresses once the memory map is locked down, | |
// and can only set it once | |
// | |
if (!EfiAtRuntime () || mEfiVirtualMode) { | |
return EFI_UNSUPPORTED; | |
} | |
// | |
// Only understand the original descriptor format | |
// | |
if (DescriptorVersion != EFI_MEMORY_DESCRIPTOR_VERSION || DescriptorSize < sizeof (EFI_MEMORY_DESCRIPTOR)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// BugBug: Add code here to verify the memory map. We should | |
// cache a copy of the system memory map in the EFI System Table | |
// as a GUID pointer pair. | |
// | |
// | |
// Make sure all virtual translations are satisfied | |
// | |
mVirtualMapMaxIndex = MemoryMapSize / DescriptorSize; | |
// | |
// BugBug :The following code does not work hence commented out. | |
// Need to be replaced by something that works. | |
// | |
// VirtEntry = VirtualMap; | |
// for (Index = 0; Index < mVirtualMapMaxIndex; Index++) { | |
// if (((VirtEntry->Attribute & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME) && | |
// (VirtEntry->VirtualStart != 0) ) { | |
// return EFI_NO_MAPPING; | |
// } | |
// VirtEntry = NextMemoryDescriptor(VirtEntry, DescriptorSize); | |
// } | |
// | |
// We are now committed to go to virtual mode, so lets get to it! | |
// | |
mEfiVirtualMode = TRUE; | |
// | |
// ConvertPointer() needs this mVirtualMap to do the conversion. So set up | |
// globals we need to parse the virtual address map. | |
// | |
mVirtualMapDescriptorSize = DescriptorSize; | |
mVirtualMap = VirtualMap; | |
// | |
// Signal all the EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE events. | |
// The core call RuntimeDriverRegisterEvent() for | |
// every runtime event and we stored them in the mEventList | |
// | |
// | |
// Currently the bug in StatusCode/RuntimeLib has been fixed, it will | |
// check whether in Runtime or not (this is judged by looking at | |
// mEfiAtRuntime global So this ReportStatusCode will work | |
// | |
REPORT_STATUS_CODE ( | |
EFI_PROGRESS_CODE, | |
(EFI_SOFTWARE_EFI_BOOT_SERVICE | EFI_SW_RS_PC_SET_VIRTUAL_ADDRESS_MAP) | |
); | |
// | |
// BugBug - Commented out for now because the status code driver is not | |
// ready for runtime yet. The Status Code driver calls data hub with does | |
// a bunch of Boot Service things (locks, AllocatePool) and hangs somewhere | |
// on the way. | |
// | |
// ReportStatusCode ( | |
// EfiProgressCode, EfiMaxErrorSeverity, | |
// 0x03, 0x01, 12, // ReadyToBoot Progress code | |
// 0x00, 30, L"ConvertPointer" | |
// ); | |
// | |
for (Link = mEventList.ForwardLink; Link != &mEventList; Link = Link->ForwardLink) { | |
RuntimeEvent = _CR (Link, RUNTIME_NOTIFY_EVENT_DATA, Link); | |
if ((RuntimeEvent->Type & EFI_EVENT_SIGNAL_VIRTUAL_ADDRESS_CHANGE) == EFI_EVENT_SIGNAL_VIRTUAL_ADDRESS_CHANGE) { | |
RuntimeEvent->NotifyFunction ( | |
RuntimeEvent->Event, | |
RuntimeEvent->NotifyContext | |
); | |
} | |
} | |
// | |
// Relocate runtime images. Runtime images loaded before the runtime AP was | |
// started will not be relocated, since they would not have gotten registered. | |
// This includes the code in this file. | |
// | |
for (Link = mRelocationList.ForwardLink; Link != &mRelocationList; Link = Link->ForwardLink) { | |
RuntimeImage = _CR (Link, RUNTIME_IMAGE_RELOCATION_DATA, Link); | |
if (RuntimeImage->Valid) { | |
RelocatePeImageForRuntime (RuntimeImage); | |
} | |
} | |
// | |
// Convert all the Runtime Services except ConvertPointer() and SetVirtualAddressMap() | |
// and recompute the CRC-32 | |
// | |
RuntimeDriverConvertInternalPointer ((VOID **) &gRT->GetTime); | |
RuntimeDriverConvertInternalPointer ((VOID **) &gRT->SetTime); | |
RuntimeDriverConvertInternalPointer ((VOID **) &gRT->GetWakeupTime); | |
RuntimeDriverConvertInternalPointer ((VOID **) &gRT->SetWakeupTime); | |
RuntimeDriverConvertInternalPointer ((VOID **) &gRT->ResetSystem); | |
RuntimeDriverConvertInternalPointer ((VOID **) &gRT->GetNextHighMonotonicCount); | |
RuntimeDriverConvertInternalPointer ((VOID **) &gRT->GetVariable); | |
RuntimeDriverConvertInternalPointer ((VOID **) &gRT->SetVariable); | |
RuntimeDriverConvertInternalPointer ((VOID **) &gRT->GetNextVariableName); | |
RuntimeDriverCalculateEfiHdrCrc (&gRT->Hdr); | |
// | |
// Convert the UGA OS Handoff Table if it is present in the Configuration Table | |
// | |
for (Index = 0; Index < gST->NumberOfTableEntries; Index++) { | |
if (CompareGuid (&mLocalEfiUgaIoProtocolGuid, &(gST->ConfigurationTable[Index].VendorGuid))) { | |
DriverOsHandoffHeader = gST->ConfigurationTable[Index].VendorTable; | |
for (Index1 = 0; Index1 < DriverOsHandoffHeader->NumberOfEntries; Index1++) { | |
DriverOsHandoff = (EFI_DRIVER_OS_HANDOFF *) | |
( | |
(UINTN) DriverOsHandoffHeader + | |
DriverOsHandoffHeader->HeaderSize + | |
Index1 * | |
DriverOsHandoffHeader->SizeOfEntries | |
); | |
RuntimeDriverConvertPointer (EFI_OPTIONAL_POINTER, (VOID **) &DriverOsHandoff->DevicePath); | |
RuntimeDriverConvertPointer (EFI_OPTIONAL_POINTER, (VOID **) &DriverOsHandoff->PciRomImage); | |
} | |
RuntimeDriverConvertPointer (EFI_OPTIONAL_POINTER, (VOID **) &(gST->ConfigurationTable[Index].VendorTable)); | |
} | |
} | |
// | |
// Convert the runtime fields of the EFI System Table and recompute the CRC-32 | |
// | |
RuntimeDriverConvertInternalPointer ((VOID **) &gST->FirmwareVendor); | |
RuntimeDriverConvertInternalPointer ((VOID **) &gST->ConfigurationTable); | |
RuntimeDriverConvertInternalPointer ((VOID **) &gST->RuntimeServices); | |
RuntimeDriverCalculateEfiHdrCrc (&gST->Hdr); | |
// | |
// At this point, gRT and gST are physical pointers, but the contents of these tables | |
// have been converted to runtime. | |
// | |
// | |
// mVirtualMap is only valid during SetVirtualAddressMap() call | |
// | |
mVirtualMap = NULL; | |
return EFI_SUCCESS; | |
} | |
EFI_STATUS | |
EFIAPI | |
RuntimeDriverInitialize ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
/*++ | |
Routine Description: | |
Install Runtime AP. This code includes the EfiDriverLib, but it functions at | |
RT in physical mode. The only Lib services are gBS, gRT, and the DEBUG and | |
ASSERT macros (they do ReportStatusCode). | |
Arguments: | |
(Standard EFI Image entry - EFI_IMAGE_ENTRY_POINT) | |
Returns: | |
EFI_SUCEESS - Runtime Driver Architectural Protocol Installed | |
Other - Return value from gBS->InstallMultipleProtocolInterfaces | |
--*/ | |
{ | |
EFI_STATUS Status; | |
// | |
// This image needs to be exclued from relocation for virtual mode, so cache | |
// a copy of the Loaded Image protocol to test later. | |
// | |
Status = gBS->HandleProtocol ( | |
ImageHandle, | |
&gEfiLoadedImageProtocolGuid, | |
(VOID **) &mMyLoadedImage | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Initialize the table used to compute 32-bit CRCs | |
// | |
RuntimeDriverInitializeCrc32Table (); | |
// | |
// Fill in the entries of the EFI Boot Services and EFI Runtime Services Tables | |
// | |
gBS->CalculateCrc32 = RuntimeDriverCalculateCrc32; | |
gRT->SetVirtualAddressMap = RuntimeDriverSetVirtualAddressMap; | |
gRT->ConvertPointer = RuntimeDriverConvertPointer; | |
// | |
// Install the Runtime Architectural Protocol onto a new handle | |
// | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&mRuntimeHandle, | |
&gEfiRuntimeArchProtocolGuid, | |
&mRuntime, | |
NULL | |
); | |
ASSERT_EFI_ERROR (Status); | |
mRuntimeImageNumber = 0; | |
mRuntimeEventNumber = 0; | |
return Status; | |
} |