Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1253 lines
43 KiB

#include "bldr.h"
#include "sal.h"
#include "efi.h"
#include "efip.h"
#include "bootia64.h"
#include "smbios.h"
#include "extern.h"
extern EFI_SYSTEM_TABLE *EfiST;
extern EFI_BOOT_SERVICES *EfiBS;
extern EFI_HANDLE EfiImageHandle;
extern TR_INFO Sal;
extern TR_INFO SalGP;
extern TR_INFO Pal;
extern ULONGLONG PalProcPhysical;
extern ULONGLONG PalPhysicalBase;
extern ULONGLONG PalTrPs;
extern ULONGLONG IoPortPhysicalBase;
extern ULONGLONG IoPortTrPs;
#if DBG
#define DBG_TRACE(_X) EfiPrint(_X)
#else
#define DBG_TRACE(_X)
#endif
// M E M O R Y D E S C R I P T O R
//
// Memory Descriptor - each contiguous block of physical memory is
// described by a Memory Descriptor. The descriptors are a table, with
// the last entry having a BlockBase and BlockSize of zero. A pointer
// to the beginning of this table is passed as part of the BootContext
// Record to the OS Loader.
//
//
// define the number of Efi Pages in an OS page
//
#define EFI_PAGES_PER_OS_PAGE ((EFI_PAGE_SHIFT < PAGE_SHIFT) ? (1 << (PAGE_SHIFT - EFI_PAGE_SHIFT)) : 1)
//
// global values associated with global Efi memory descriptor array
//
ULONGLONG MemoryMapKey;
ULONG BlPlatformPropertiesEfiFlags = 0;
//
// values to make sure we only get sal,pal,salgp,ioport info once
//
BOOLEAN SalFound = FALSE;
BOOLEAN PalFound = FALSE;
BOOLEAN SalGpFound = FALSE;
BOOLEAN IoPortFound = FALSE;
//
// prototypes for local routines
//
BOOLEAN
pDescriptorContainsAddress(
EFI_MEMORY_DESCRIPTOR *EfiMd,
ULONGLONG PhysicalAddress
);
BOOLEAN
pCoalesceDescriptor(
MEMORY_DESCRIPTOR *PrevEntry,
MEMORY_DESCRIPTOR *CurrentEntry
);
//
// function definitions for routines exported outside
// this file
//
MEMORY_TYPE
EfiToArcType (
UINT32 Type
)
/*++
Routine Description:
Maps an EFI memory type to an Arc memory type. We only care about a few
kinds of memory, so this list is incomplete.
Arguments:
Type - an EFI memory type
Returns:
A MEMORY_TYPE enumerated type.
--*/
{
MEMORY_TYPE typeRet=MemoryFirmwarePermanent;
switch (Type) {
case EfiLoaderCode:
{
typeRet=MemoryLoadedProgram; // This gets claimed later
break;
}
case EfiLoaderData:
case EfiBootServicesCode:
case EfiBootServicesData:
{
typeRet=MemoryFirmwareTemporary;
break;
}
case EfiConventionalMemory:
{
typeRet=MemoryFree;
break;
}
case EfiUnusableMemory:
{
typeRet=MemoryBad;
break;
}
default:
//
// all others are memoryfirmwarepermanent
//
break;
}
return typeRet;
}
VOID
ConstructArcMemoryDescriptorsWithAllocation(
ULONGLONG LowBoundary,
ULONGLONG HighBoundary
)
/*++
Routine Description:
Builds up memory descriptors for the OS loader. This routine queries EFI
for it's memory map (a variable sized array of EFI_MEMORY_DESCRIPTOR's).
It then allocates sufficient space for the MDArray global (a variable sized
array of ARC-based MEMORY_DESCRIPTOR's.) The routine then maps the EFI
memory map to the ARC memory map, carving out all of the conventional
memory space for the EFI loader to help keep the memory map intact. We
must leave behind some amount of memory for the EFI boot services to use,
which we allocate as conventional memory in our map.
This routine will allocate the memory (using EFI) for the
EfiMemoryDescriptors
Arguments:
LowBoundary - The entire Efi Memory descriptor list does not need be
HigBoundary - translated to Arc descriptors. Low/High Boundary map
the desired region to be mapped. Their values are
addresses (not pages)
Returns:
NOTHING: If this routine encounters an error, it is treated as fatal
and the program exits.
--*/
{
EFI_STATUS Status;
ULONG i;
EFI_MEMORY_DESCRIPTOR *MDEfi = NULL;
ULONGLONG MemoryMapSize = 0;
ULONGLONG DescriptorSize;
UINT32 DescriptorVersion;
//
// Get memory map info from EFI firmware
//
// To do this, we first find out how much space we need by calling
// with an empty buffer.
//
Status = EfiBS->GetMemoryMap (
&MemoryMapSize,
NULL,
&MemoryMapKey,
&DescriptorSize,
&DescriptorVersion
);
if (Status != EFI_BUFFER_TOO_SMALL) {
EfiPrint(L"ConstructArcMemoryDescriptors: GetMemoryMap failed\n");
EfiBS->Exit(EfiImageHandle, Status, 0, 0);
}
//
// We are going to make a few extra allocations before we call GetMemoryMap
// again, so add some extra space.
//
// 1. EfiMD (a descriptor for us, if we can't fit)
// 2. MDarray (a descriptor for the MDArray, if not already allocated)
// 3. Split out memory above and below the desired boundaries
// and add a few more so we hopefully don't have to reallocate memory
// for the descriptor
//
MemoryMapSize += 3*DescriptorSize;
#if DBG_MEMORY
wsprintf(DebugBuffer,
L"ConstructArcMemoryDescriptor: Allocated 0x%x bytes for MDEfi\r\n",
(ULONG)MemoryMapSize
);
EfiPrint(DebugBuffer);
#endif
//
// now allocate space for the EFI-based memory map and assign it to the loader
//
Status = EfiAllocateAndZeroMemory(EfiLoaderData,
(ULONG) MemoryMapSize,
&MDEfi);
if (EFI_ERROR(Status)) {
DBG_TRACE( L"ConstructArcMemoryDescriptors: AllocatePool failed\r\n");
EfiBS->Exit(EfiImageHandle, Status, 0, 0);
}
//
// now Allocate and zero the MDArray, which is the native loader memory
// map which we need to map the EFI memory map to.
//
// The MDArray has one entry for each EFI_MEMORY_DESCRIPTOR, and each entry
// is MEMORY_DESCRIPTOR large.
//
if (MDArray == NULL) {
i=((ULONG)(MemoryMapSize / DescriptorSize)+1)*sizeof (MEMORY_DESCRIPTOR);
#if DBG_MEMORY
wsprintf(DebugBuffer,
L"ConstructArcMemoryDescriptor: Allocated 0x%x bytes for MDArray\r\n",
i
);
EfiPrint(DebugBuffer);
#endif
Status = EfiAllocateAndZeroMemory(EfiLoaderData,i,&MDArray);
if (EFI_ERROR(Status)) {
DBG_TRACE (L"ConstructArcMemoryDescriptors: AllocatePool failed\r\n");
EfiBS->Exit(EfiImageHandle, Status, 0, 0);
}
}
//
// we have all of the memory allocated at this point, so retreive the
// memory map again, which should succeed the second time.
//
Status = EfiBS->GetMemoryMap (
&MemoryMapSize,
MDEfi,
&MemoryMapKey,
&DescriptorSize,
&DescriptorVersion
);
if (EFI_ERROR(Status)) {
DBG_TRACE(L"ConstructArcMemoryDescriptors: GetMemoryMap failed\r\n");
EfiBS->Exit(EfiImageHandle, Status, 0, 0);
}
//
// initialize global variables
//
MaxDescriptors = (ULONG)((MemoryMapSize / DescriptorSize)+1); // zero-based
//
// construct the arc memory descriptors for the
// efi descriptors
//
ConstructArcMemoryDescriptors(MDEfi,
MDArray,
MemoryMapSize,
DescriptorSize,
LowBoundary,
HighBoundary);
#if DBG_MEMORY
PrintArcMemoryDescriptorList(MDArray,
NumberDescriptors
);
#endif
}
#define MASK_16MB (ULONGLONG) 0xffffffffff000000I64
#define MASK_16KB (ULONGLONG) 0xffffffffffffc000I64
#define SIZE_IN_BYTES_16KB 16384
VOID
ConstructArcMemoryDescriptors(
EFI_MEMORY_DESCRIPTOR *EfiMd,
MEMORY_DESCRIPTOR *ArcMd,
ULONGLONG MemoryMapSize,
ULONGLONG EfiDescriptorSize,
ULONGLONG LowBoundary,
ULONGLONG HighBoundary
)
/*++
Routine Description:
Builds up memory descriptors for the OS loader. The routine maps the EFI
memory map to the ARC memory map, carving out all of the conventional
memory space for the EFI loader to help keep the memory map intact. We
must leave behind some amount of memory for the EFI boot services to use,
which we allocate as conventional memory in our map.
If consecutive calls are made into ConstructArcMemoryDescriptors,
the efi routine GetMemoryMap must be called before each instance,
since we may allocate new pages inside of this routine. therefore
a new efi memory map may be required to illustrate these changes.
Arguments:
EfiMd - a pointer to the efi memory descriptor list that we will be
constructing arc memory descriptors for.
ArcMd - (must be allocated). This will be populated with ARC-based memory
descriptors corresponding to the EFI Memory Descriptors
MemoryMapSize - Size of the Efi Memory Map
EfiDescriptorSize - size of Efi Memory Descriptor
LowBoundary - The entire Efi Memory descriptor list does not need be
HigBoundary - translated to Arc descriptors. Low/High Boundary map
the desired region to be mapped. Their values are
addresses (not pages)
Returns:
Nothing. Fills in the MDArray global variable. If this routine encounters
an error, it is treated as fatal and the program exits.
--*/
{
EFI_STATUS Status;
ULONG i;
ULONGLONG IoPortSize;
ULONGLONG MdPhysicalStart;
ULONGLONG MdPhysicalEnd;
ULONGLONG MdPhysicalSize;
MEMORY_DESCRIPTOR *OldMDArray = NULL; // initialize for w4 compiler
ULONG OldNumberDescriptors = 0; // initialize for w4 compiler
ULONG OldMaxDescriptors = 0; // initialize for w4 compiler
BOOLEAN ReplaceMDArray = FALSE;
ULONG efiMemMapIOWriteCombining = 0;
ULONG efiMemMapIO = 0;
ULONG efiMainMemoryWC = 0;
ULONG efiMainMemoryUC = 0;
//
// check to make sure ArcMd is allocated
//
if (ArcMd == NULL) {
EfiPrint(L"ConstructArcMemoryDescriptors: Invalid parameter\r\n");
EfiBS->Exit(EfiImageHandle, 0, 0, 0);
return;
}
//
// this is a bit hacky, but instead of changing the functionality of
// all the other functions (insertdescriptor, adjustarcmemorydescriptor, etc)
// just have MDArray point to the new ArcMd (if we want to write a new descriptor
// list. when we are done, switch it back
//
if (MDArray != ArcMd) {
ReplaceMDArray = TRUE;
OldMDArray = MDArray;
OldNumberDescriptors = NumberDescriptors;
OldMaxDescriptors = MaxDescriptors;
MDArray = ArcMd;
#if DBG_MEMORY
EfiPrint(L"ConstructArcMemoryDescriptors: Using alternate Memory Descriptor List\r\n");
#endif
//
// perhaps change this functionality.
// right now a descriptor list besides the global
// one will always expect to create a whole new list
// each time
//
NumberDescriptors = 0;
MaxDescriptors = (ULONG)((MemoryMapSize / EfiDescriptorSize)+1); // zero-based
RtlZeroMemory(MDArray, MemoryMapSize);
}
#if DBG_MEMORY
wsprintf( DebugBuffer,
L"unaligned (LowBoundary, HighBoundary) = (0x%x, 0x%x)\r\n",
LowBoundary, HighBoundary
);
EfiPrint( DebugBuffer );
#endif
//
// align boundaries (with OS page size)
// having this be os page size makes alignment issues easier later
//
LowBoundary = (LowBoundary >> PAGE_SHIFT) << PAGE_SHIFT; // this should alway round down
if (HighBoundary % PAGE_SIZE) {
// this should round up
HighBoundary = ((HighBoundary + PAGE_SIZE) >> PAGE_SHIFT << PAGE_SHIFT);
}
// make sure that we did not wrap around ...
if (HighBoundary == 0) {
HighBoundary = (ULONGLONG)-1;
}
#if DBG_MEMORY
wsprintf( DebugBuffer,
L"aligned (LowBoundary, HighBoundary) = (0x%x, 0x%x)\r\n",
LowBoundary, HighBoundary
);
EfiPrint( DebugBuffer );
#endif
//
// now walk the EFI_MEMORY_DESCRIPTOR array, mapping each
// entry to an arc-based MEMORY_DESCRIPTOR.
//
// MemoryMapSize contains actual size of the memory descriptor array.
//
for (i = 0; MemoryMapSize > 0; i++) {
#if DBG_MEMORY
wsprintf( DebugBuffer,
L"PageStart (%x), Size (%x), Type (%x)\r\n",
(EfiMd->PhysicalStart >> EFI_PAGE_SHIFT),
EfiMd->NumberOfPages,
EfiMd->Type);
EfiPrint(DebugBuffer);
//DBG_EFI_PAUSE();
#endif
if (EfiMd->NumberOfPages > 0) {
MdPhysicalStart = EfiMd->PhysicalStart;
MdPhysicalEnd = EfiMd->PhysicalStart + (EfiMd->NumberOfPages << EFI_PAGE_SHIFT);
MdPhysicalSize = MdPhysicalEnd - MdPhysicalStart;
#if DBG_MEMORY
wsprintf( DebugBuffer,
L"PageStart %x (%x), PageEnd %x (%x), Type (%x)\r\n",
MdPhysicalStart, (EfiMd->PhysicalStart >> EFI_PAGE_SHIFT),
MdPhysicalEnd, (EfiMd->PhysicalStart >>EFI_PAGE_SHIFT) + EfiMd->NumberOfPages,
EfiMd->Type);
EfiPrint(DebugBuffer);
//DBG_EFI_PAUSE();
#endif
//
// only create arc memory descriptors for the desired region.
// but always look for efi memory mapped io space. this is not
// included in the arc descripter list, but is needed for the
// tr's to enable virtual mode.
// also save off any pal information for later.
//
if (EfiMd->Type == EfiMemoryMappedIOPortSpace) {
if (!IoPortFound) {
//
// save off the Io stuff for later
//
IoPortPhysicalBase = EfiMd->PhysicalStart;
IoPortSize = EfiMd->NumberOfPages << EFI_PAGE_SHIFT;
MEM_SIZE_TO_PS(IoPortSize, IoPortTrPs);
IoPortFound = TRUE;
}
#if DBG_MEMORY
else {
EfiPrint(L"Ignoring IoPort.\r\n");
}
#endif
}
else if (EfiMd->Type == EfiPalCode) {
if (!PalFound) {
BOOLEAN LargerThan16Mb;
ULONGLONG PalTrMask;
ULONGLONG PalTrSize;
ULONGLONG PalSize;
//
// save off the Pal stuff for later
//
PalPhysicalBase = Pal.PhysicalAddressMemoryDescriptor = EfiMd->PhysicalStart;
PalSize = EfiMd->NumberOfPages << EFI_PAGE_SHIFT;
Pal.PageSizeMemoryDescriptor = (ULONG)EfiMd->NumberOfPages;
MEM_SIZE_TO_PS(PalSize, PalTrPs);
Pal.PageSize = (ULONG)PalTrPs;
//
// Copied from Halia64\ia64\i64efi.c
//
PalTrSize = SIZE_IN_BYTES_16KB;
PalTrMask = MASK_16KB;
LargerThan16Mb = TRUE;
while (PalTrMask >= MASK_16MB) {
if ( (Pal.PhysicalAddressMemoryDescriptor + PalSize) <=
((Pal.PhysicalAddressMemoryDescriptor & PalTrMask) + PalTrSize)) {
LargerThan16Mb = FALSE;
break;
}
PalTrMask <<= 2;
PalTrSize <<= 2;
}
//
// This is the virtual base adddress of the PAL
//
Pal.VirtualAddress = VIRTUAL_PAL_BASE + (Pal.PhysicalAddressMemoryDescriptor & ~PalTrMask);
//
// We've done this already. Remember that
//
PalFound = TRUE;
}
#if DBG_MEMORY
else {
EfiPrint(L"Ignoring Pal.\r\n");
}
#endif
}
else if (EfiMd->Type == EfiMemoryMappedIO) {
//
// In terms of arc descriptor entry, just ignore this type since
// it's not real memory -- the system can use ACPI tables to get
// at this later on.
//
efiMemMapIO++;
//
// Simply check the attribute for the PlatformProperties global.
//
if ( EfiMd->Attribute & EFI_MEMORY_WC ) {
efiMemMapIOWriteCombining++;
}
}
//
// don't insert all memory into the arc descriptor table
// just the descriptors between the Low/High Boundaries
// so before we even continue, make sure that some part
// of the descriptor is in the desired boundary.
// there are three cases for crossing a boundary
// 1. the start page (not the end page) is in the boundary
// 2. the end page (not the start page) is in the boundary
// 3. the range covers the entire boundary (neither start/end page) in boundary
//
// these can be written more simply as:
// A descriptor is NOT within a region if the entire
// descriptor is LESS than the region ( MdStart <= MdEnd <= LowBoundary )
// OR is GREATER than the region ( HighBoundary <= MdStart <= MdEnd )
// Therefore we simplify this futher to say
// Start < HighBoundary && LowBoundary < MdEnd
else if ( (MdPhysicalStart < HighBoundary) && (LowBoundary < MdPhysicalEnd) ) {
//
// Insert conventional memory descriptors with WB flag set
// into NT loader memory descriptor list.
//
if ( (EfiMd->Type == EfiConventionalMemory) &&
((EfiMd->Attribute & EFI_MEMORY_WB) == EFI_MEMORY_WB) ) {
ULONGLONG AmountOfMemory;
ULONGLONG NumberOfEfiPages;
BOOLEAN BrokeRange = FALSE;
EFI_PHYSICAL_ADDRESS AllocationAddress = 0;
//
// adjust the descriptor if not completely in
// desired boundary (we know that if we are here at least
// part of descriptor is in boundary
//
if (MdPhysicalStart < LowBoundary) {
#if DBG_MEMORY
wsprintf(DebugBuffer,
L"Broke Low (start=0x%x) LowBoundary=0x%x\r\n",
MdPhysicalStart,
LowBoundary);
EfiPrint(DebugBuffer);
#endif
MdPhysicalStart = LowBoundary;
BrokeRange = TRUE;
}
if (HighBoundary < MdPhysicalEnd) {
#if DBG_MEMORY
wsprintf(DebugBuffer,
L"Broke High (end=0x%x) HighBoundary=0x%x\r\n",
MdPhysicalEnd,
HighBoundary);
EfiPrint(DebugBuffer);
#endif
MdPhysicalEnd = HighBoundary;
BrokeRange = TRUE;
}
//
// determine the memory range for this descriptor
//
AmountOfMemory = MdPhysicalEnd - MdPhysicalStart;
NumberOfEfiPages = AmountOfMemory >> EFI_PAGE_SHIFT;
if (BrokeRange) {
//
// allocate pages for the new region
//
// note: we have 1 descriptor describing up to 3 new regions.
// we can change the descriptor but still may have to allocate 2
// new regions. but if we allocate with the same type, they will just
// coellesce.
// one way to get around this is to allocate the middle page (in this
// boundary region) of a different efi memory type (this will cause
// efi to break the descriptor into three. then we can keep
// a better record of it in our arc descriptor list
//
// the possible regions are
// start address - low boundary
// low boundar - high boundary
// high boundary - end address
//
// have the current descriptor modified for this boundary
// region, allocating pages for that below and above us
//
AllocationAddress = MdPhysicalStart;
Status = EfiBS->AllocatePages ( AllocateAddress,
EfiLoaderData,
NumberOfEfiPages,
&AllocationAddress );
#if DBG_MEMORY
wsprintf( DebugBuffer,
L"allocate pages @ %x size = %x\r\n",
(MdPhysicalStart >> EFI_PAGE_SHIFT),
NumberOfEfiPages );
EfiPrint(DebugBuffer);
//DBG_EFI_PAUSE();
#endif
if (EFI_ERROR(Status)) {
EfiPrint(L"SuMain: AllocPages failed\n");
EfiBS->Exit(EfiImageHandle, Status, 0, 0);
}
}
//
// now we can finally insert this (possibly modified) descriptor
//
InsertDescriptor( (ULONG)(MdPhysicalStart >> EFI_PAGE_SHIFT),
(ULONG)((ULONGLONG)AmountOfMemory >> EFI_PAGE_SHIFT),
EfiToArcType(EfiMd->Type)
);
}
else {
//
// some other type -- just insert it without any changes
//
InsertDescriptor( (ULONG)(MdPhysicalStart >> EFI_PAGE_SHIFT),
(ULONG)(MdPhysicalSize >> EFI_PAGE_SHIFT),
EfiToArcType(EfiMd->Type));
}
}
//
// try to get the size of the sal.
//
if (pDescriptorContainsAddress(EfiMd,Sal.PhysicalAddress)) {
if (!SalFound) {
Sal.PhysicalAddressMemoryDescriptor = EfiMd->PhysicalStart;
Sal.PageSizeMemoryDescriptor = (ULONG)EfiMd->NumberOfPages;
SalFound = TRUE;
}
#if DBG_MEMORY
else {
EfiPrint(L"Ignoring Sal.\r\n");
}
#endif
}
//
// try to get the size of the sal gp.
//
if (pDescriptorContainsAddress(EfiMd,SalGP.PhysicalAddress)) {
if (!SalGpFound) {
SalGP.PhysicalAddressMemoryDescriptor = EfiMd->PhysicalStart;
SalGP.PageSizeMemoryDescriptor = (ULONG)EfiMd->NumberOfPages;
SalGpFound = TRUE;
}
#if DBG_MEMORY
else {
EfiPrint(L"Ignoring SalGP.\r\n");
}
#endif
}
//
// try to get the cacheability attribute of ACPI Tables. Remember
// that the ACPI tables must be cached in the exceptional case where
// the firmware doesn't map the acpi tables non-cached.
//
if ( AcpiTable && pDescriptorContainsAddress(EfiMd,(ULONG_PTR)AcpiTable)) {
if ( (EfiMd->Attribute & EFI_MEMORY_WB) && !(EfiMd->Attribute & EFI_MEMORY_UC) ) {
BlPlatformPropertiesEfiFlags |= HAL_PLATFORM_ACPI_TABLES_CACHED;
}
}
//
// if none of EFI MD Main Memory does present Uncacheable or WriteCombining,
// it's assumed this platform doesn't support Uncacheable or Write Combining
// in Main Memory.
//
if ( EfiMd->Type == EfiConventionalMemory ) {
if ( EfiMd->Attribute & EFI_MEMORY_WC ) {
efiMainMemoryWC++;
}
if (EfiMd->Attribute & EFI_MEMORY_UC ) {
efiMainMemoryUC++;
}
}
}
EfiMd = (EFI_MEMORY_DESCRIPTOR *) ( (PUCHAR) EfiMd + EfiDescriptorSize );
MemoryMapSize -= EfiDescriptorSize;
}
//
// If this platform supports at least 1 MMIO Write Combining EFI MD or if there are
// no memory mapped I/O entries, then indicate the platform can support write
// combined accesses to the I/O space.
//
if ( efiMemMapIOWriteCombining || (efiMemMapIO == 0)) {
BlPlatformPropertiesEfiFlags |= HAL_PLATFORM_ENABLE_WRITE_COMBINING_MMIO;
}
//
// If this platform doesn't support Main Memory Uncacheable or Write Combining
// from a point of view of EFI MDs, let's flag the PlatformProperties global.
//
if ( !efiMainMemoryUC ) {
BlPlatformPropertiesEfiFlags |= HAL_PLATFORM_DISABLE_UC_MAIN_MEMORY;
}
if ( !efiMainMemoryWC ) {
BlPlatformPropertiesEfiFlags |= HAL_PLATFORM_DISABLE_WRITE_COMBINING;
}
//
// reset globals
//
if (ReplaceMDArray) {
MDArray = OldMDArray;
NumberDescriptors = OldNumberDescriptors;
MaxDescriptors = OldMaxDescriptors;
}
}
VOID
InsertDescriptor (
ULONG BasePage,
ULONG NumberOfPages,
MEMORY_TYPE MemoryType
)
/*++
Routine Description:
This routine inserts a descriptor into the correct place in the
memory descriptor list.
The descriptors come in in EFI_PAGE_SIZE pages and must be
converted to PAGE_SIZE pages. This significantly complicates things,
as we must page align the start of descriptors and hte lengths of the
descriptors.
pCoalesceDescriptor does the necessary work for coalescing descriptors
plus converting from EF_PAGE_SIZE to PAGE_SIZE.
Arguments:
BasePage - Base page that the memory starts at.
NumberOfPages - The number of pages starting at memory block to be inserted.
MemoryType - An arc memory type describing the memory.
Return Value:
None. Updates MDArray global memory array.
--*/
{
MEMORY_DESCRIPTOR *CurrentEntry, *PriorEntry;
//
// grab the pointers for CurrentEntry and PriorEntry.
// fill in the efi values for CurrentEntry
//
PriorEntry = (NumberDescriptors == 0) ? NULL : (MEMORY_DESCRIPTOR *)&MDArray[NumberDescriptors-1];
CurrentEntry = (MEMORY_DESCRIPTOR *)&MDArray[NumberDescriptors];
//
// The last entry had better be empty or the descriptor list is
// somehow corrupted.
//
if (CurrentEntry->PageCount != 0) {
wsprintf(DebugBuffer,
L"InsertDescriptor: Inconsistent Descriptor count(0x%x) (PageCount=0x%x)\r\n",
NumberDescriptors,
CurrentEntry->PageCount
);
EfiPrint(DebugBuffer);
EfiBS->Exit(EfiImageHandle, 0, 0, 0);
}
//
// fill in values for this entry
//
CurrentEntry->BasePage = BasePage;
CurrentEntry->PageCount = NumberOfPages;
CurrentEntry->MemoryType = MemoryType;
//
// call pCoalesceDescriptor. this will do all the basepage/pagecount manipulation
// for EFI_PAGES -> OS_PAGES.
// the return value is TRUE if the current entry merged with the previous entry,
// otherwise the descriptor should be added.
//
if (pCoalesceDescriptor(PriorEntry, CurrentEntry) == FALSE) {
NumberDescriptors++;
}
else {
CurrentEntry->BasePage = CurrentEntry->PageCount = CurrentEntry->MemoryType = 0;
}
#if DBG_MEMORY
wsprintf( DebugBuffer,
L"insert new descriptor #%x of %x, BasePage %x, NumberOfPages %x, Type (%x)\r\n",
NumberDescriptors,
MaxDescriptors,
CurrentEntry->BasePage,
CurrentEntry->PageCount,
CurrentEntry->MemoryType);
EfiPrint(DebugBuffer);
//DBG_EFI_PAUSE();
#endif
return;
}
#ifdef DBG
VOID
PrintArcMemoryDescriptorList(
MEMORY_DESCRIPTOR *ArcMd,
ULONG MaxDesc
)
{
ULONG i;
for (i = 0; i < MaxDesc; i++) {
//
// print to console BasePage, EndPage, PageCount, MemoryType
//
wsprintf( DebugBuffer,
L"#%x BasePage:0x%x EndPage:0x%x PageCount:0x%x MemoryType:0x%x\r\n",
i,
ArcMd[i].BasePage,
ArcMd[i].BasePage + ArcMd[i].PageCount,
ArcMd[i].PageCount,
ArcMd[i].MemoryType
);
EfiPrint(DebugBuffer);
}
}
#endif
//
// private function definitions
//
BOOLEAN
pDescriptorContainsAddress(
EFI_MEMORY_DESCRIPTOR *EfiMd,
ULONGLONG PhysicalAddress
)
{
ULONGLONG MdPhysicalStart, MdPhysicalEnd;
MdPhysicalStart = (ULONGLONG)EfiMd->PhysicalStart;
MdPhysicalEnd = MdPhysicalStart + ((ULONGLONG)EfiMd->NumberOfPages << EFI_PAGE_SHIFT);
if ((PhysicalAddress >= MdPhysicalStart) &&
(PhysicalAddress < MdPhysicalEnd)) {
#if DBG_MEMORY
EfiPrint(L"DescriptorContainsAddress: returning TRUE\r\n");
#endif
return(TRUE);
}
return(FALSE);
}
BOOLEAN
pCoalesceDescriptor(
MEMORY_DESCRIPTOR *PrevEntry,
MEMORY_DESCRIPTOR *CurrentEntry
)
/*++
Routine Description:
This routine attempts to coalesce a memory descriptor with the
previous descriptor. Note: the arc memory descriptor table
keeps track of everything in OS pages, and we are getting information
from the EFI memory descriptor table which is in EFI pages.
So conversions will be made for PriorEntry since this
will be in OS pages.
there are two options, either shrink the previous entry (from the end)
or shrink the current entry from the begining. this will be determined
by each's memory type.
This routine will align all blocks (they need to be aligned at the end)
Note: the memory descriptor's memory types need to be in ARC type
Arguments:
PriorEntry - Previous memory descriptor in MDArray (in OS pages)
CurrentEntry - Entry we are working on to place in MDArray (in EFI pages)
Return Value:
BOOLEAN value indicating if coalescing occurred (merged with previous
entry. TRUE if so, FALSE otherwise. Therefore, if TRUE
is returned, do not add to the descriptor table
--*/
{
ULONG NumPagesToShrink;
ULONG NumPagesToExtend;
BOOLEAN RetVal = FALSE;
MEMORY_DESCRIPTOR PriorEntry;
MEMORY_DESCRIPTOR *MemoryDescriptor;
BOOLEAN ShrinkPrior = FALSE;
//
// convert prev entry's page information to be in efi pages
//
if (PrevEntry != NULL) {
#if DBG_MEMORY
wsprintf(DebugBuffer,
L"PriorEntry(OsPages): BasePage=0x%x PageCount=0x%x MemoryType=0x%x\r\n",
PrevEntry->BasePage,
PrevEntry->PageCount,
PrevEntry->MemoryType
);
EfiPrint(DebugBuffer);
#endif
PriorEntry.BasePage = (ULONG)(((ULONGLONG)PrevEntry->BasePage << PAGE_SHIFT) >> EFI_PAGE_SHIFT);
PriorEntry.PageCount = (ULONG)(((ULONGLONG)PrevEntry->PageCount << PAGE_SHIFT) >> EFI_PAGE_SHIFT);
PriorEntry.MemoryType = PrevEntry->MemoryType;
}
else {
//
// initialize for w4 compiler... even though the below if statement
// will already be true by the time we look at PriorEntry
//
PriorEntry.BasePage = PriorEntry.PageCount = PriorEntry.MemoryType = 0;
}
#if DBG_MEMORY
wsprintf(DebugBuffer,
L"PriorEntry(EfiPages): BasePage=0x%x PageCount=0x%x MemoryType=0x%x\r\n",
PriorEntry.BasePage,
PriorEntry.PageCount,
PriorEntry.MemoryType
);
EfiPrint(DebugBuffer);
wsprintf(DebugBuffer,
L"CurrentEntry(EfiPages): BasePage=0x%x PageCount=0x%x MemoryType=0x%x\r\n",
CurrentEntry->BasePage,
CurrentEntry->PageCount,
CurrentEntry->MemoryType
);
EfiPrint(DebugBuffer);
#endif
//
// calculate the number of pages we have to move to be on an OS page
// boundary
//
NumPagesToShrink = CurrentEntry->BasePage % EFI_PAGES_PER_OS_PAGE;
NumPagesToExtend = (NumPagesToShrink != 0) ? (EFI_PAGES_PER_OS_PAGE - NumPagesToShrink) : 0;
#if DBG_MEMORY
wsprintf(DebugBuffer,
L"NumPagesToShrink=0x%x, NumPagesToExtend=0x%x\r\n",
NumPagesToShrink,
NumPagesToExtend
);
EfiPrint(DebugBuffer);
#endif
//
// do the simple cases where coealescing is easy or non-existent
// case 1: No pages to Shrink
// case 2: this is the first memory descriptor
// case 3: previous page does not extend to new page
// case 4: previous descriptor extends past current page... something is messed up
// or list is not ordered. assume we are suppose to just insert this entry
//
if ( (PrevEntry == NULL) ||
(NumPagesToShrink == 0 && (PriorEntry.MemoryType != CurrentEntry->MemoryType)) ||
(PriorEntry.BasePage + PriorEntry.PageCount < CurrentEntry->BasePage - NumPagesToShrink) ||
(PriorEntry.BasePage + PriorEntry.PageCount > CurrentEntry->BasePage + NumPagesToExtend)
) {
CurrentEntry->BasePage -= NumPagesToShrink;
CurrentEntry->PageCount += NumPagesToShrink;
}
//
// if none of the previous cases were met, we are going to have to try
// to coellesce with the previous entry
//
else {
#if DBG_MEMORY
wsprintf(
DebugBuffer,
L"must coellesce because the BasePage isn't module PAGE_SIZE (%x mod %x = %x).\r\n",
CurrentEntry->BasePage,
EFI_PAGES_PER_OS_PAGE,
NumPagesToShrink );
EfiPrint(DebugBuffer);
#endif
if (CurrentEntry->MemoryType == PriorEntry.MemoryType) {
//
// same memory type... coalesce the entire region
//
// we are only changing the base page, so we can reuse the code below
// that converts efi pages to os pages
//
RetVal = TRUE;
PrevEntry->PageCount = CurrentEntry->BasePage + CurrentEntry->PageCount - PriorEntry.BasePage;
PrevEntry->BasePage = (ULONG)(((ULONGLONG)PrevEntry->BasePage << PAGE_SHIFT) >> EFI_PAGE_SHIFT);
#if DBG_MEMORY
wsprintf(DebugBuffer,
L"Merge with previous entry (basepage=0x%x, pagecount= 0x%x, memorytype=0x%x\r\n",
PrevEntry->BasePage,
PrevEntry->PageCount,
PrevEntry->MemoryType
);
EfiPrint(DebugBuffer);
#endif
}
else {
//
// determine which end we will shrink
//
switch( CurrentEntry->MemoryType ) {
case MemoryFirmwarePermanent:
//
// if the current type is permanent, we must steal from the prior entry
//
ShrinkPrior = TRUE;
break;
case MemoryLoadedProgram:
if (PriorEntry.MemoryType == MemoryFirmwarePermanent) {
ShrinkPrior = FALSE;
} else {
ShrinkPrior = TRUE;
}
break;
case MemoryFirmwareTemporary:
if (PriorEntry.MemoryType == MemoryFirmwarePermanent ||
PriorEntry.MemoryType == MemoryLoadedProgram) {
ShrinkPrior = FALSE;
} else {
ShrinkPrior = TRUE;
}
break;
case MemoryFree:
ShrinkPrior = FALSE;
break;
case MemoryBad:
ShrinkPrior = TRUE;
break;
default:
EfiPrint(L"SuMain: bad memory type in InsertDescriptor\r\n");
EfiBS->Exit(EfiImageHandle, 0, 0, 0);
}
if (ShrinkPrior) {
//
// shrink the previous descriptor (from the end)
//
// we can just subtrace 1 OS page from the previous entry,
// since we can never Shrink more than one OS page.
//
PrevEntry->PageCount--;
CurrentEntry->BasePage -= NumPagesToShrink;
CurrentEntry->PageCount += NumPagesToShrink;
//
// if we shrunk this to nothing, get rid of it
//
if (PrevEntry->PageCount == 0) {
PrevEntry->BasePage = CurrentEntry->BasePage;
PrevEntry->PageCount = CurrentEntry->PageCount;
PrevEntry->MemoryType = CurrentEntry->MemoryType;
CurrentEntry->BasePage = CurrentEntry->PageCount = CurrentEntry->MemoryType = 0;
RetVal = TRUE;
}
#if DBG_MEMORY
wsprintf(DebugBuffer,
L"Shrink previous descriptor by 0x%x EFI pages\r\n",
NumPagesToShrink
);
EfiPrint(DebugBuffer);
#endif
}
else {
//
// shrink the current descriptor (from the start)
//
// don't have to touch the previous entry, just
// shift the current entry to the next os page
// boundary
CurrentEntry->BasePage += NumPagesToExtend;
CurrentEntry->PageCount -= NumPagesToExtend;
//
// if we shrunk this to nothing, get rid of it
//
if (CurrentEntry->PageCount == 0) {
//
// need to convert previous entry back to efi pages, so checks work
// at the bottome
//
PrevEntry->BasePage = (ULONG)(((ULONGLONG)PrevEntry->BasePage << PAGE_SHIFT) >> EFI_PAGE_SHIFT);
PrevEntry->PageCount = (ULONG)(((ULONGLONG)PrevEntry->PageCount << PAGE_SHIFT) >> EFI_PAGE_SHIFT);
CurrentEntry->BasePage = CurrentEntry->PageCount = CurrentEntry->MemoryType = 0;
RetVal = TRUE;
}
#if DBG_MEMORY
wsprintf(DebugBuffer,
L"Shrink this descriptor by 0x%x EFI pages\r\n",
NumPagesToExtend
);
EfiPrint(DebugBuffer);
#endif
}
}
}
//
// set one variable to the entry we modified. that way we can use the same
// code to convert efi pages to os pages + extend the page count
//
MemoryDescriptor = (RetVal == TRUE) ? PrevEntry : CurrentEntry;
#if DBG_MEMORY
wsprintf(DebugBuffer,
L"MemoryDescriptor: BasePage=0x%x PageCount=0x%x MemoryType=0x%x\r\n",
MemoryDescriptor->BasePage,
MemoryDescriptor->PageCount,
MemoryDescriptor->MemoryType
);
EfiPrint(DebugBuffer);
#endif
//
// we think we are done coalescing, and have done so successfully
// make sure this is indeed the case
//
ASSERT( MemoryDescriptor->BasePage % EFI_PAGES_PER_OS_PAGE == 0 );
if ( MemoryDescriptor->BasePage % EFI_PAGES_PER_OS_PAGE != 0 ) {
EfiPrint(L"CoalesceDescriptor: BasePage not on OS page boundary\r\n");
EfiBS->Exit(EfiImageHandle, 0, 0, 0);
}
//
// great... we did this right.
// now extend then end of a region to an os page boundary, and
// set the values to os pages (instead of efi pages)
//
NumPagesToExtend = EFI_PAGES_PER_OS_PAGE - (MemoryDescriptor->PageCount % EFI_PAGES_PER_OS_PAGE);
MemoryDescriptor->PageCount += (NumPagesToExtend == EFI_PAGES_PER_OS_PAGE) ? 0 : NumPagesToExtend;
ASSERT( MemoryDescriptor->PageCount % EFI_PAGES_PER_OS_PAGE == 0 );
if ( MemoryDescriptor->PageCount % EFI_PAGES_PER_OS_PAGE != 0 ) {
EfiPrint(L"CoalesceDescriptor: PageCount not on OS page boundary\r\n");
EfiBS->Exit(EfiImageHandle, 0, 0, 0);
}
//
// convert to os pages
//
MemoryDescriptor->PageCount = (ULONG)(((ULONGLONG)MemoryDescriptor->PageCount << EFI_PAGE_SHIFT) >> PAGE_SHIFT);
MemoryDescriptor->BasePage = (ULONG)(((ULONGLONG)MemoryDescriptor->BasePage << EFI_PAGE_SHIFT) >> PAGE_SHIFT);
#if DBG_MEMORY
wsprintf( DebugBuffer,
L"descriptor value #%x of %x, BasePage %x, NumberOfPages %x, Type (%x)\r\n",
NumberDescriptors, MaxDescriptors, MemoryDescriptor->BasePage,
MemoryDescriptor->PageCount, MemoryDescriptor->MemoryType);
EfiPrint(DebugBuffer);
#endif
return RetVal;
}