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.
 
 
 
 
 
 

4169 lines
99 KiB

/*++
Copyright (c) 1991-2000 Microsoft Corporation
Module Name:
amd64x86.c
Abstract:
This module contains routines necessary to support loading and
transitioning into an AMD64 kernel. The code in this module has
access to x86-specific defines found in i386.h but not to amd64-
specific declarations found in amd64.h.
Author:
Forrest Foltz (forrestf) 20-Apr-2000
Environment:
Revision History:
--*/
#include "amd64prv.h"
#include <pcmp.inc>
#include <ntapic.inc>
#if defined(ROUND_UP)
#undef ROUND_UP
#endif
#include "cmp.h"
#include "arc.h"
#define WANT_BLDRTHNK_FUNCTIONS
#define COPYBUF_MALLOC BlAllocateHeap
#include <amd64thk.h>
#define IMAGE_DEFINITIONS 0
#include <ximagdef.h>
//
// Warning 4152 is "nonstandard extension, function/data pointer conversion
//
#pragma warning(disable:4152)
//
// Private, tempory memory descriptor type
//
#define LoaderAmd64MemoryData (LoaderMaximum + 10)
//
// Array of 64-bit memory descriptors
//
PMEMORY_ALLOCATION_DESCRIPTOR_64 BlAmd64DescriptorArray;
LONG BlAmd64DescriptorArraySize;
//
// Forward declarations for functions local to this module
//
ARC_STATUS
BlAmd64AllocateMemoryAllocationDescriptors(
VOID
);
ARC_STATUS
BlAmd64BuildLdrDataTableEntry64(
IN PLDR_DATA_TABLE_ENTRY DataTableEntry32,
OUT PLDR_DATA_TABLE_ENTRY_64 *DataTableEntry64
);
ARC_STATUS
BlAmd64BuildLoaderBlock64(
VOID
);
ARC_STATUS
BlAmd64BuildLoaderBlockExtension64(
VOID
);
ARC_STATUS
BlAmd64BuildMappingPhase1(
VOID
);
ARC_STATUS
BlAmd64BuildMappingPhase2(
VOID
);
ARC_STATUS
BlAmd64BuildMappingWorker(
VOID
);
BOOLEAN
BlAmd64ContainsResourceList(
IN PCONFIGURATION_COMPONENT_DATA ComponentData32,
OUT PULONG ResourceListSize64
);
ARC_STATUS
BlAmd64FixSharedUserPage(
VOID
);
BOOLEAN
BlAmd64IsPageMapped(
IN ULONG Va,
OUT PFN_NUMBER *Pfn,
OUT PBOOLEAN PageTableMapped
);
ARC_STATUS
BlAmd64PrepareSystemStructures(
VOID
);
VOID
BlAmd64ReplaceMemoryDescriptorType(
IN TYPE_OF_MEMORY Target,
IN TYPE_OF_MEMORY Replacement,
IN BOOLEAN Coallesce
);
VOID
BlAmd64ResetPageTableHeap(
VOID
);
VOID
BlAmd64SwitchToLongMode(
VOID
);
ARC_STATUS
BlAmd64TransferArcDiskInformation(
VOID
);
ARC_STATUS
BlAmd64TransferBootDriverNodes(
VOID
);
ARC_STATUS
BlAmd64TransferConfigurationComponentData(
VOID
);
PCONFIGURATION_COMPONENT_DATA_64
BlAmd64TransferConfigWorker(
IN PCONFIGURATION_COMPONENT_DATA ComponentData32,
IN PCONFIGURATION_COMPONENT_DATA_64 ComponentDataParent64
);
ARC_STATUS
BlAmd64TransferHardwareIdList(
IN PPNP_HARDWARE_ID HardwareId,
OUT POINTER64 *HardwareIdDatabaseList64
);
ARC_STATUS
BlAmd64TransferLoadedModuleState(
VOID
);
ARC_STATUS
BlAmd64TransferMemoryAllocationDescriptors(
VOID
);
ARC_STATUS
BlAmd64TransferNlsData(
VOID
);
VOID
BlAmd64TransferResourceList(
IN PCONFIGURATION_COMPONENT_DATA ComponentData32,
OUT PCONFIGURATION_COMPONENT_DATA_64 ComponentData64
);
ARC_STATUS
BlAmd64TransferSetupLoaderBlock(
VOID
);
#if DBG
PCHAR BlAmd64MemoryDescriptorText[] = {
"LoaderExceptionBlock",
"LoaderSystemBlock",
"LoaderFree",
"LoaderBad",
"LoaderLoadedProgram",
"LoaderFirmwareTemporary",
"LoaderFirmwarePermanent",
"LoaderOsloaderHeap",
"LoaderOsloaderStack",
"LoaderSystemCode",
"LoaderHalCode",
"LoaderBootDriver",
"LoaderConsoleInDriver",
"LoaderConsoleOutDriver",
"LoaderStartupDpcStack",
"LoaderStartupKernelStack",
"LoaderStartupPanicStack",
"LoaderStartupPcrPage",
"LoaderStartupPdrPage",
"LoaderRegistryData",
"LoaderMemoryData",
"LoaderNlsData",
"LoaderSpecialMemory",
"LoaderBBTMemory",
"LoaderReserve"
};
#endif
VOID
NSUnmapFreeDescriptors(
IN PLIST_ENTRY ListHead
);
//
// Data declarations
//
PLOADER_PARAMETER_BLOCK BlAmd64LoaderBlock32;
PLOADER_PARAMETER_BLOCK_64 BlAmd64LoaderBlock64;
//
// Pointer to the top of the 64-bit stack frame to use upon transition
// to long mode.
//
POINTER64 BlAmd64IdleStack64;
//
// GDT and IDT pseudo-descriptor for use with LGDT/LIDT
//
DESCRIPTOR_TABLE_DESCRIPTOR BlAmd64GdtDescriptor;
DESCRIPTOR_TABLE_DESCRIPTOR BlAmd64IdtDescriptor;
DESCRIPTOR_TABLE_DESCRIPTOR BlAmd32GdtDescriptor;
//
// 64-bit pointers to the loader parameter block and kernel
// entry routine
//
POINTER64 BlAmd64LoaderParameterBlock;
POINTER64 BlAmd64KernelEntry;
//
// A private list of page tables used to build the long mode paging
// structures is kept. This is in order to avoid memory allocations while
// the structures are being assembled.
//
// The PT_NODE type as well as the BlAmd64FreePfnList and BlAmd64BusyPfnList
// globals are used to that end.
//
typedef struct _PT_NODE *PPT_NODE;
typedef struct _PT_NODE {
PPT_NODE Next;
PAMD64_PAGE_TABLE PageTable;
} PT_NODE;
PPT_NODE BlAmd64FreePfnList = NULL;
PPT_NODE BlAmd64BusyPfnList = NULL;
//
// Indicate if the system is waking up from hibernate
//
ULONG HiberInProgress = 0;
//
// External data
//
extern ULONG64 BlAmd64_LOCALAPIC;
ARC_STATUS
BlAmd64MapMemoryRegion(
IN ULONG RegionVa,
IN ULONG RegionSize
)
/*++
Routine Description:
This function creates long mode mappings for all valid x86 mappings
within the region described by RegionVa and RegionSize.
Arguments:
RegionVa - Supplies the starting address of the VA region.
RegionSize - Supplies the size of the VA region.
Return Value:
ARC_STATUS - Status of operation.
--*/
{
ULONG va32;
ULONG va32End;
POINTER64 va64;
ARC_STATUS status;
PFN_NUMBER pfn;
BOOLEAN pageMapped;
BOOLEAN pageTableMapped;
ULONG increment;
va32 = RegionVa;
va32End = va32 + RegionSize;
while (va32 < va32End) {
pageMapped = BlAmd64IsPageMapped( va32, &pfn, &pageTableMapped );
if (pageTableMapped != FALSE) {
//
// The page table corresponding to this address is present.
//
if (pageMapped != FALSE) {
//
// The page corresponding to this address is present.
//
if ((va32 & KSEG0_BASE_X86) != 0) {
//
// The address lies within the X86 KSEG0 region. Map
// it to the corresponding address within the AMD64
// KSEG0 region.
//
va64 = PTR_64( (PVOID)va32 );
} else {
//
// Map the VA directly.
//
va64 = (POINTER64)va32;
}
//
// Now create the mapping in the AMD64 page table structure.
//
status = BlAmd64CreateMapping( va64, pfn );
if (status != ESUCCESS) {
return status;
}
}
//
// Check the next page.
//
increment = PAGE_SIZE;
} else {
//
// Not only is the page not mapped but neither is the page table.
// Skip to the next page table address boundary.
//
increment = 1 << PDI_SHIFT;
}
//
// Advance to the next VA to check, checking for overflow.
//
va32 = (va32 + increment) & ~(increment - 1);
if (va32 == 0) {
break;
}
}
return ESUCCESS;
}
BOOLEAN
BlAmd64IsPageMapped(
IN ULONG Va,
OUT PFN_NUMBER *Pfn,
OUT PBOOLEAN PageTableMapped
)
/*++
Routine Description:
This function accepts a 32-bit virtual address, determines whether it
is a valid address, and if so returns the Pfn associated with it.
Addresses that are within the recursive mapping are treated as NOT
mapped.
Arguments:
None.
Return Value:
ARC_STATUS - Status of operation.
--*/
{
ULONG pdeIndex;
ULONG pteIndex;
PHARDWARE_PTE pde;
PHARDWARE_PTE pte;
BOOLEAN dummy;
PBOOLEAN pageTableMapped;
//
// Point the output parameter pointer as appropriate.
//
if (ARGUMENT_PRESENT(PageTableMapped)) {
pageTableMapped = PageTableMapped;
} else {
pageTableMapped = &dummy;
}
//
// Pages that are a part of the X86 32-bit mapping structure ARE
// IGNORED.
//
if (Va >= PTE_BASE && Va <= PTE_TOP) {
*pageTableMapped = TRUE;
return FALSE;
}
//
// Determine whether the mapping PDE is present
//
pdeIndex = Va >> PDI_SHIFT;
pde = &((PHARDWARE_PTE)PDE_BASE)[ pdeIndex ];
if (pde->Valid == 0) {
*pageTableMapped = FALSE;
return FALSE;
}
//
// Indicate that the page table for this address is mapped.
//
*pageTableMapped = TRUE;
//
// It is, now get the page present status
//
pteIndex = Va >> PTI_SHIFT;
pte = &((PHARDWARE_PTE)PTE_BASE)[ pteIndex ];
if (pte->Valid == 0) {
return FALSE;
}
*Pfn = pte->PageFrameNumber;
return TRUE;
}
PAMD64_PAGE_TABLE
BlAmd64AllocatePageTable(
VOID
)
/*++
Routine Description:
This function allocates and initializes a PAGE_TABLE structure.
Arguments:
None.
Return Value:
Returns a pointer to the allocated page table structure, or NULL
if the allocation failed.
--*/
{
ARC_STATUS status;
ULONG descriptor;
PPT_NODE ptNode;
PAMD64_PAGE_TABLE pageTable;
//
// Pull a page table off of the free list, if one exists
//
ptNode = BlAmd64FreePfnList;
if (ptNode != NULL) {
BlAmd64FreePfnList = ptNode->Next;
} else {
//
// The free page table list is empty, allocate a new
// page table and node to track it with.
//
status = BlAllocateDescriptor( LoaderAmd64MemoryData,
0,
1,
&descriptor );
if (status != ESUCCESS) {
return NULL;
}
ptNode = BlAllocateHeap( sizeof(PT_NODE) );
if (ptNode == NULL) {
return NULL;
}
ptNode->PageTable = (PAMD64_PAGE_TABLE)(descriptor << PAGE_SHIFT);
}
ptNode->Next = BlAmd64BusyPfnList;
BlAmd64BusyPfnList = ptNode;
pageTable = ptNode->PageTable;
RtlZeroMemory( pageTable, PAGE_SIZE );
return pageTable;
}
ARC_STATUS
BlAmd64TransferToKernel(
IN PTRANSFER_ROUTINE SystemEntry,
IN PLOADER_PARAMETER_BLOCK BlLoaderBlock
)
/*++
Routine Description:
This routine prepares the AMD64 data structures required for kernel
execution, including page table structures and 64-bit loader block,
and transfers control to the kernel.
This routine returns only upon an error.
Arguments:
SystemEntry - Pointer to the kernel entry point.
BlLoaderBlock - Pointer to the 32-bit loader block structure.
Return Value:
No return on success. On failure, returns the status of the operation.
--*/
{
UNREFERENCED_PARAMETER( BlLoaderBlock );
BlAmd64LoaderParameterBlock = PTR_64(BlAmd64LoaderBlock64);
BlAmd64KernelEntry = PTR_64(SystemEntry);
BlAmd64SwitchToLongMode();
return EINVAL;
}
ARC_STATUS
BlAmd64PrepForTransferToKernelPhase1(
IN PLOADER_PARAMETER_BLOCK BlLoaderBlock
)
/*++
Routine Description:
This routine prepares the AMD64 data structures required for kernel
execution, including page table structures and 64-bit loader block.
This is the first of two phases of preperation. This phase is executed
while heap and descriptor allocations are still permitted.
Arguments:
BlLoaderBlock - Pointer to the 32-bit loader block structure.
Return Value:
No return on success. On failure, returns the status of the operation.
--*/
{
ARC_STATUS status;
//
// This is the main routine called to do preperatory work before
// transitioning into the AMD64 kernel.
//
BlAmd64LoaderBlock32 = BlLoaderBlock;
//
// Build a 64-bit copy of the loader parameter block.
//
status = BlAmd64BuildLoaderBlock64();
if (status != ESUCCESS) {
return status;
}
//
// Process the loaded modules.
//
status = BlAmd64TransferLoadedModuleState();
if (status != ESUCCESS) {
return status;
}
//
// Next the boot driver nodes
//
status = BlAmd64TransferBootDriverNodes();
if (status != ESUCCESS) {
return status;
}
//
// NLS data
//
status = BlAmd64TransferNlsData();
if (status != ESUCCESS) {
return status;
}
//
// Configuration component data tree
//
status = BlAmd64TransferConfigurationComponentData();
if (status != ESUCCESS) {
return status;
}
//
// ARC disk information
//
status = BlAmd64TransferArcDiskInformation();
if (status != ESUCCESS) {
return status;
}
//
// Setup loader block
//
status = BlAmd64TransferSetupLoaderBlock();
if (status != ESUCCESS) {
return status;
}
//
// Allocate structures needed by the kernel: TSS, stacks etc.
//
status = BlAmd64PrepareSystemStructures();
if (status != ESUCCESS) {
return status;
}
//
// Mark the descriptor for the shared user page so that it will
// not be freed by the kernel.
//
status = BlAmd64FixSharedUserPage();
if (status != ESUCCESS) {
return status;
}
//
// Pre-allocate any pages needed for the long mode paging structures.
//
status = BlAmd64BuildMappingPhase1();
if (status != ESUCCESS) {
return status;
}
//
// Pre-allocate the 64-bit memory allocation descriptors that will be
// used by BlAmd64TransferMemoryAllocationDescriptors().
//
status = BlAmd64AllocateMemoryAllocationDescriptors();
if (status != ESUCCESS) {
return status;
}
return status;
}
VOID
BlAmd64PrepForTransferToKernelPhase2(
IN PLOADER_PARAMETER_BLOCK BlLoaderBlock
)
/*++
Routine Description:
This routine prepares the AMD64 data structures required for kernel
execution, including page table structures and 64-bit loader block.
This is the second of two phases of preperation. This phase is executed
after the 32-bit page tables have been purged of any unused mappings.
Note that descriptor and heap allocations are not permitted at this
point. Any necessary storage must have been preallocated during phase 1.
Arguments:
BlLoaderBlock - Pointer to the 32-bit loader block structure.
Return Value:
No return on success. On failure, returns the status of the operation.
--*/
{
PLOADER_PARAMETER_EXTENSION_64 extension;
ARC_STATUS status;
UNREFERENCED_PARAMETER( BlLoaderBlock );
//
// At this point everything has been preallocated, nothing can fail.
//
status = BlAmd64BuildMappingPhase2();
ASSERT(status == ESUCCESS);
//
// Transfer the memory descriptor state.
//
status = BlAmd64TransferMemoryAllocationDescriptors();
ASSERT(status == ESUCCESS);
//
// Set LoaderPagesSpanned in the 64-bit loader block.
//
extension = PTR_32(BlAmd64LoaderBlock64->Extension);
extension->LoaderPagesSpanned = BlHighestPage+1;
}
ARC_STATUS
BlAmd64BuildMappingPhase1(
VOID
)
/*++
Routine Description:
This routine performs the first of the two-phase long mode mapping
structure creation process now, while memory allocations are still
possible. It simply calls BlAmd64BuilMappingWorker() which in fact
creates the mapping structures, and (more importantly) allocates all
of the page tables required to do so.
Arguments:
None.
Return Value:
None.
--*/
{
ARC_STATUS status;
//
// While it is possible to perform memory allocations, reserve enough
// page tables to build the AMD64 paging structures.
//
// The easiest way to calculate the maximum number of pages needed is
// to actually build the structures. We do that now with the first of
// two calls to BlAmd64BuildMappingWorker().
//
status = BlAmd64BuildMappingWorker();
if (status != ESUCCESS) {
return status;
}
return ESUCCESS;
}
ARC_STATUS
BlAmd64BuildMappingPhase2(
VOID
)
/*++
Routine Description:
This routine performs the second of the two-phase long mode mapping
structure creation process. All page tables will have been preallocated
as a result of the work performed by BlAmd64BuildMappingPhase1().
Arguments:
None.
Return Value:
None.
--*/
{
ARC_STATUS status;
//
// Reset the Amd64 paging structures
//
BlAmd64ResetPageTableHeap();
//
// All necessary page tables can now be found on BlAmd64FreePfnList.
// On this, the second call to BlAmd64BuildMappingWorker(), those are the
// pages that will be used to perform the mapping.
//
status = BlAmd64BuildMappingWorker();
if (status != ESUCCESS) {
return status;
}
return ESUCCESS;
}
ARC_STATUS
BlAmd64BuildMappingWorker(
VOID
)
/*++
Routine Description:
This routine creates any necessary memory mappings in the long-mode
page table structure. It is called twice, once from
BlAmd64BuildMappingPhase1() and again from BlAmd64BuildMappingPhase2().
Any additional memory mapping that must be carried out should go in
this routine.
Arguments:
None.
Return Value:
None.
--*/
{
ARC_STATUS status;
PFN_NUMBER pfn;
//
// Any long mode mapping code goes here. This routine is called twice:
// once from BlAmd64BuildMappingPhase1(), and again from
// BlAmd64BuildMappingPhase2().
//
//
// Transfer any mappings in the first 32MB of identity mapping.
//
status = BlAmd64MapMemoryRegion( 0,
32 * 1024 * 1024 );
if (status != ESUCCESS) {
return status;
}
//
// Transfer any mappings in the 1GB region starting at KSEG0_BASE_X86.
//
status = BlAmd64MapMemoryRegion( KSEG0_BASE_X86,
0x40000000 );
if (status != ESUCCESS) {
return status;
}
//
// "Map" the HAL va
//
status = BlAmd64MapHalVaSpace();
if (status != ESUCCESS) {
return status;
}
//
// Map the shared user data page
//
BlAmd64IsPageMapped( KI_USER_SHARED_DATA, &pfn, NULL );
status = BlAmd64CreateMapping( KI_USER_SHARED_DATA_64, pfn );
if (status != ESUCCESS) {
return status;
}
return ESUCCESS;
}
VOID
BlAmd64ResetPageTableHeap(
VOID
)
/*++
Routine Description:
This function is called as part of the two-phase page table creation
process. Its purpose is to move all of the PFNs required to build
the long mode page tables back to the free list, and to otherwise
initialize the long mode paging structure.
Arguments:
None.
Return Value:
None.
--*/
{
PPT_NODE ptNodeLast;
//
// Move the page table nodes from the busy list to the free list.
//
if (BlAmd64BusyPfnList != NULL) {
//
// A tail pointer is not kept, so find the tail node here.
//
ptNodeLast = BlAmd64BusyPfnList;
while (ptNodeLast->Next != NULL) {
ptNodeLast = ptNodeLast->Next;
}
ptNodeLast->Next = BlAmd64FreePfnList;
BlAmd64FreePfnList = BlAmd64BusyPfnList;
BlAmd64BusyPfnList = NULL;
}
//
// Zero the top-level pte declared in amd64.c
//
BlAmd64ClearTopLevelPte();
}
ARC_STATUS
BlAmd64TransferHardwareIdList(
IN PPNP_HARDWARE_ID HardwareId,
OUT POINTER64 *HardwareIdDatabaseList64
)
/*++
Routine Description:
This routine walks the singly-linked list of PNP_HARDWARE_ID structures
and for each one found, creates a 64-bit PNP_HARDWARE_ID_64 structure and
inserts it on a list of same.
The resultant 64-bit list is in the same order as the supplied 32-bit
list.
Arguments:
HardwareId - Supplies a pointer to the head of the singly-linked list of
PNP_HARDWARE_ID structures.
HardwareIdDatabaseList64 -
Supplies a pointer to a POINTER64 which upon successful
completion of this routine will contain a 64-bit KSEG0
pointer to the created 64-bit PNP_HARDWARE_ID_64 list.
Return Value:
ARC_STATUS - Status of operation.
--*/
{
PPNP_HARDWARE_ID_64 hardwareId64;
ARC_STATUS status;
//
// Walk the id list backwards. To do this we call ourselves
// recursively until we find the end of the list, then process the nodes
// on the way back up.
//
if (HardwareId == NULL) {
return ESUCCESS;
}
status = BlAmd64TransferHardwareIdList( HardwareId->Next,
HardwareIdDatabaseList64 );
if (status != ESUCCESS) {
return status;
}
hardwareId64 = BlAllocateHeap(sizeof(PNP_HARDWARE_ID_64));
if (hardwareId64 == NULL) {
return ENOMEM;
}
status = Copy_PNP_HARDWARE_ID( HardwareId, hardwareId64 );
if (status != ESUCCESS) {
return status;
}
//
// Link it into the front of the 64-bit list.
//
hardwareId64->Next = *HardwareIdDatabaseList64;
*HardwareIdDatabaseList64 = PTR_64(hardwareId64);
return ESUCCESS;
}
ARC_STATUS
BlAmd64TransferDeviceRegistryList(
IN PDETECTED_DEVICE_REGISTRY DetectedDeviceRegistry32,
OUT POINTER64 *DetectedDeviceRegistry64
)
/*++
Routine Description:
This routine walks the singly-linked list of DETECTED_DEVICE_REGISTRY
structures and for each one found, creates a 64-bit
DETECTED_DEVICE_REGISTRY_64 structure and inserts it on a list of same.
The resultant 64-bit list is in the same order as the supplied 32-bit
list.
Arguments:
DetectedDeviceRegistry32 - Supplies a pointer to the head of the singly-linked list of
DETECTED_DEVICE_REGISTRY structures.
DetectedDeviceRegistry64 -
Supplies a pointer to a POINTER64 which upon successful
completion of this routine will contain a 64-bit KSEG0
pointer to the created 64-bit DETECTED_DEVICE_REGISTRY_64
list.
Return Value:
ARC_STATUS - Status of operation.
--*/
{
PDETECTED_DEVICE_REGISTRY_64 registry64;
ARC_STATUS status;
//
// Walk the registry list backwards. To do this we call ourselves
// recursively until we find the end of the list, then process the nodes
// on the way back up.
//
if (DetectedDeviceRegistry32 == NULL) {
return ESUCCESS;
}
status = BlAmd64TransferDeviceRegistryList( DetectedDeviceRegistry32->Next,
DetectedDeviceRegistry64 );
if (status != ESUCCESS) {
return status;
}
//
// Allocate a 64-bit registry structure and copy the contents
// of the 32-bit one in.
//
registry64 = BlAllocateHeap(sizeof(DETECTED_DEVICE_REGISTRY_64));
if (registry64 == NULL) {
return ENOMEM;
}
status = Copy_DETECTED_DEVICE_REGISTRY( DetectedDeviceRegistry32, registry64 );
if (status != ESUCCESS) {
return status;
}
//
// Link it into the front of the 64-bit list.
//
registry64->Next = *DetectedDeviceRegistry64;
*DetectedDeviceRegistry64 = PTR_64(registry64);
return ESUCCESS;
}
ARC_STATUS
BlAmd64TransferDeviceFileList(
IN PDETECTED_DEVICE_FILE DetectedDeviceFile32,
OUT POINTER64 *DetectedDeviceFile64
)
/*++
Routine Description:
This routine walks the singly-linked list of DETECTED_DEVICE_FILE
structures and for each one found, creates a 64-bit
DETECTED_DEVICE_FILE_64 structure and inserts it on a list of same.
The resultant 64-bit list is in the same order as the supplied 32-bit
list.
Arguments:
DetectedDeviceFile32 - Supplies a pointer to the head of the singly-linked
list of DETECTED_DEVICE_FILE structures.
DetectedDeviceFile64 -
Supplies a pointer to a POINTER64 which upon successful
completion of this routine will contain a 64-bit KSEG0
pointer to the created 64-bit DETECTED_DEVICE_FILE_64
list.
Return Value:
ARC_STATUS - Status of operation.
--*/
{
PDETECTED_DEVICE_FILE_64 file64;
ARC_STATUS status;
//
// Walk the file list backwards. To do this we call ourselves
// recursively until we find the end of the list, then process the nodes
// on the way back up.
//
if (DetectedDeviceFile32 == NULL) {
return ESUCCESS;
}
status = BlAmd64TransferDeviceFileList( DetectedDeviceFile32->Next,
DetectedDeviceFile64 );
if (status != ESUCCESS) {
return status;
}
//
// Allocate a 64-bit file structure and copy the contents
// of the 32-bit one in.
//
file64 = BlAllocateHeap(sizeof(DETECTED_DEVICE_FILE_64));
if (file64 == NULL) {
return ENOMEM;
}
status = Copy_DETECTED_DEVICE_FILE( DetectedDeviceFile32, file64 );
if (status != ESUCCESS) {
return status;
}
//
// Transfer the singly-linked list of DETECTED_DEVICE_REGISTRY structures
// linked to this DETECTED_DEVICE_FILE structure.
//
status = BlAmd64TransferDeviceRegistryList(
DetectedDeviceFile32->RegistryValueList,
&file64->RegistryValueList );
if (status != ESUCCESS) {
return status;
}
//
// Link it into the front of the 64-bit list.
//
file64->Next = *DetectedDeviceFile64;
*DetectedDeviceFile64 = PTR_64(file64);
return ESUCCESS;
}
ARC_STATUS
BlAmd64TransferDeviceList(
IN PDETECTED_DEVICE DetectedDevice32,
OUT POINTER64 *DetectedDeviceList64
)
/*++
Routine Description:
This routine walks the singly-linked list of DETECTED_DEVICE
structures and for each one found, creates a 64-bit
DETECTED_DEVICE_64 structure and inserts it on a list of same.
The resultant 64-bit list is in the same order as the supplied 32-bit
list.
Arguments:
DetectedDevice32 - Supplies a pointer to the head of the singly-linked
list of DETECTED_DEVICE structures.
DetectedDeviceList64 -
Supplies a pointer to a POINTER64 which upon successful
completion of this routine will contain a 64-bit KSEG0
pointer to the created 64-bit DETECTED_DEVICE_64
list.
Return Value:
ARC_STATUS - Status of operation.
--*/
{
PDETECTED_DEVICE_64 device64;
ARC_STATUS status;
//
// Walk the device list backwards. To do this we call ourselves
// recursively until we find the end of the list, then process the nodes
// on the way back up.
//
if (DetectedDevice32 == NULL) {
return ESUCCESS;
}
status = BlAmd64TransferDeviceList( DetectedDevice32->Next,
DetectedDeviceList64 );
if (status != ESUCCESS) {
return status;
}
//
// Allocate a 64-bit device structure and copy the contents
// of the 32-bit one in.
//
device64 = BlAllocateHeap(sizeof(DETECTED_DEVICE_64));
if (device64 == NULL) {
return ENOMEM;
}
status = Copy_DETECTED_DEVICE( DetectedDevice32, device64 );
if (status != ESUCCESS) {
return status;
}
//
// Transfer any PROTECTED_DEVICE_FILE structures
//
status = BlAmd64TransferDeviceFileList( DetectedDevice32->Files,
&device64->Files );
if (status != ESUCCESS) {
return status;
}
//
// Link it into the front of the 64-bit list.
//
device64->Next = *DetectedDeviceList64;
*DetectedDeviceList64 = PTR_64(device64);
return ESUCCESS;
}
ARC_STATUS
BlAmd64TransferSetupLoaderBlock(
VOID
)
/*++
Routine Description:
This routine creates a SETUP_LOADER_BLOCK_64 structure that is the
equivalent of the 32-bit SETUP_LOADER_BLOCK structure referenced within
the 32-bit setup loader block.
Arguments:
None.
Return Value:
ARC_STATUS - Status of operation.
--*/
{
PSETUP_LOADER_BLOCK setupBlock32;
PSETUP_LOADER_BLOCK_64 setupBlock64;
ARC_STATUS status;
setupBlock32 = BlAmd64LoaderBlock32->SetupLoaderBlock;
if (setupBlock32 == NULL) {
return ESUCCESS;
}
setupBlock64 = BlAllocateHeap(sizeof(SETUP_LOADER_BLOCK_64));
if (setupBlock64 == NULL) {
return ENOMEM;
}
status = Copy_SETUP_LOADER_BLOCK( setupBlock32, setupBlock64 );
if (status != ESUCCESS) {
return status;
}
{
#define TRANSFER_DEVICE_LIST(x) \
setupBlock64->x = PTR_64(NULL); \
status = BlAmd64TransferDeviceList( setupBlock32->x, \
&setupBlock64->x ); \
if (status != ESUCCESS) return status;
TRANSFER_DEVICE_LIST(KeyboardDevices);
TRANSFER_DEVICE_LIST(ScsiDevices);
TRANSFER_DEVICE_LIST(BootBusExtenders);
TRANSFER_DEVICE_LIST(BusExtenders);
TRANSFER_DEVICE_LIST(InputDevicesSupport);
#undef TRANSFER_DEVICE_LIST
}
setupBlock64->HardwareIdDatabase = PTR_64(NULL);
status = BlAmd64TransferHardwareIdList( setupBlock32->HardwareIdDatabase,
&setupBlock64->HardwareIdDatabase );
if (status != ESUCCESS) {
return status;
}
BlAmd64LoaderBlock64->SetupLoaderBlock = PTR_64(setupBlock64);
return status;
}
ARC_STATUS
BlAmd64TransferArcDiskInformation(
VOID
)
/*++
Routine Description:
This routine creates an ARC_DISK_INFORMATION_64 structure that is the
equivalent of the 32-bit ARC_DISK_INFORMATION structure referenced within
the 32-bit loader block.
Arguments:
None.
Return Value:
ARC_STATUS - Status of operation.
--*/
{
ARC_STATUS status;
PLIST_ENTRY listHead;
PLIST_ENTRY listEntry;
PARC_DISK_INFORMATION diskInfo32;
PARC_DISK_INFORMATION_64 diskInfo64;
PARC_DISK_SIGNATURE diskSignature32;
PARC_DISK_SIGNATURE_64 diskSignature64;
//
// Create a 64-bit ARC_DISK_INFORMATION structure
//
diskInfo32 = BlAmd64LoaderBlock32->ArcDiskInformation;
if (diskInfo32 == NULL) {
return ESUCCESS;
}
diskInfo64 = BlAllocateHeap(sizeof(ARC_DISK_INFORMATION_64));
if (diskInfo64 == NULL) {
return ENOMEM;
}
status = Copy_ARC_DISK_INFORMATION( diskInfo32, diskInfo64 );
if (status != ESUCCESS) {
return status;
}
InitializeListHead64( &diskInfo64->DiskSignatures );
//
// Walk the 32-bit list of ARC_DISK_SIGNATURE nodes and create
// a 64-bit version of each
//
listHead = &diskInfo32->DiskSignatures;
listEntry = listHead->Flink;
while (listEntry != listHead) {
diskSignature32 = CONTAINING_RECORD( listEntry,
ARC_DISK_SIGNATURE,
ListEntry );
diskSignature64 = BlAllocateHeap(sizeof(ARC_DISK_SIGNATURE_64));
if (diskSignature64 == NULL) {
return ENOMEM;
}
status = Copy_ARC_DISK_SIGNATURE( diskSignature32, diskSignature64 );
if (status != ESUCCESS) {
return status;
}
InsertTailList64( &diskInfo64->DiskSignatures,
&diskSignature64->ListEntry );
listEntry = listEntry->Flink;
}
BlAmd64LoaderBlock64->ArcDiskInformation = PTR_64(diskInfo64);
return ESUCCESS;
}
ARC_STATUS
BlAmd64TransferConfigurationComponentData(
VOID
)
/*++
Routine Description:
This routine creates a CONFIGURATION_COMPONENT_DATA_64 structure tree
that is the equivalent of the 32-bit CONFIGURATION_COMPONENT_DATA
structure tree referenced within the 32-bit loader block.
Arguments:
None.
Return Value:
ARC_STATUS - Status of operation.
--*/
{
PCONFIGURATION_COMPONENT_DATA_64 rootComponent64;
if (BlAmd64LoaderBlock32->ConfigurationRoot == NULL) {
return ESUCCESS;
}
rootComponent64 =
BlAmd64TransferConfigWorker( BlAmd64LoaderBlock32->ConfigurationRoot,
NULL );
if (rootComponent64 == NULL) {
return ENOMEM;
}
BlAmd64LoaderBlock64->ConfigurationRoot = PTR_64(rootComponent64);
return ESUCCESS;
}
PCONFIGURATION_COMPONENT_DATA_64
BlAmd64TransferConfigWorker(
IN PCONFIGURATION_COMPONENT_DATA ComponentData32,
IN PCONFIGURATION_COMPONENT_DATA_64 ComponentDataParent64
)
/*++
Routine Description:
Given a 32-bit CONFIGURATION_COMPONENT_DATA structure, this routine
creates an equivalent 64-bit CONFIGURATION_COMPONENT_DATA structure
for the supplied structure, as well as for all of its children and
siblings.
This routine calls itself recursively for each sibling and child.
Arguments:
ComponentData32 - Supplies a pointer to the 32-bit structure to transfer.
ComponentDataParent64 - Supplies a pointer to the current 64-bit parent
structure.
Return Value:
Returns a pointer to the created 64-bit structure, or NULL if a failure
was encountered.
--*/
{
ARC_STATUS status;
ULONG componentDataSize64;
ULONG partialResourceListSize64;
BOOLEAN thunkResourceList;
PCONFIGURATION_COMPONENT_DATA_64 componentData64;
PCONFIGURATION_COMPONENT_DATA_64 newCompData64;
//
// Create and copy configuration component data node
//
componentDataSize64 = sizeof(CONFIGURATION_COMPONENT_DATA_64);
thunkResourceList = BlAmd64ContainsResourceList(ComponentData32,
&partialResourceListSize64);
if (thunkResourceList != FALSE) {
//
// This node contains a CM_PARTIAL_RESOURCE_LIST structure.
// partialResourceListSize64 contains the number of bytes beyond the
// CONFIGURATION_COMPONENT_DATA header that must be allocated in order to
// thunk the CM_PARTIAL_RESOURCE_LIST into a 64-bit version.
//
componentDataSize64 += partialResourceListSize64;
}
componentData64 = BlAllocateHeap(componentDataSize64);
if (componentData64 == NULL) {
return NULL;
}
status = Copy_CONFIGURATION_COMPONENT_DATA( ComponentData32,
componentData64 );
if (status != ESUCCESS) {
return NULL;
}
if (thunkResourceList != FALSE) {
//
// Update the configuration component data size
//
componentData64->ComponentEntry.ConfigurationDataLength =
partialResourceListSize64;
}
componentData64->Parent = PTR_64(ComponentDataParent64);
if (thunkResourceList != FALSE) {
//
// Now transfer the resource list.
//
BlAmd64TransferResourceList(ComponentData32,componentData64);
}
//
// Process the child (and recursively, all children)
//
if (ComponentData32->Child != NULL) {
newCompData64 = BlAmd64TransferConfigWorker( ComponentData32->Child,
componentData64 );
if (newCompData64 == NULL) {
return newCompData64;
}
componentData64->Child = PTR_64(newCompData64);
}
//
// Process the sibling (and recursively, all siblings)
//
if (ComponentData32->Sibling != NULL) {
newCompData64 = BlAmd64TransferConfigWorker( ComponentData32->Sibling,
ComponentDataParent64 );
if (newCompData64 == NULL) {
return newCompData64;
}
componentData64->Sibling = PTR_64(newCompData64);
}
return componentData64;
}
VOID
BlAmd64TransferResourceList(
IN PCONFIGURATION_COMPONENT_DATA ComponentData32,
OUT PCONFIGURATION_COMPONENT_DATA_64 ComponentData64
)
/*++
Routine Description:
This routine transfers the 32-bit CM_PARTIAL_RESOURCE_LIST structure that
immediately follows ComponentData32 to the memory immediately after
ComponentData64.
Arguments:
ComponentData32 - Supplies a pointer to the 32-bit structure to transfer from.
ComponentData64 - Supplies a pointer to the 64-bit structure to transfer to.
Return Value:
None.
--*/
{
PCM_PARTIAL_RESOURCE_LIST resourceList32;
PCM_PARTIAL_RESOURCE_LIST_64 resourceList64;
PCM_PARTIAL_RESOURCE_DESCRIPTOR resourceDesc32;
PCM_PARTIAL_RESOURCE_DESCRIPTOR_64 resourceDesc64;
PVOID descBody32;
PVOID descBody64;
PUCHAR tail32;
PUCHAR tail64;
ULONG tailSize;
ULONG descriptorCount;
//
// Calculate pointers to the source and target descriptor lists.
//
resourceList32 = (PCM_PARTIAL_RESOURCE_LIST)ComponentData32->ConfigurationData;
resourceList64 = (PCM_PARTIAL_RESOURCE_LIST_64)(ComponentData64 + 1);
//
// Update ComponentData64 to refer to it's new data area, which will be immediately
// following the component data structure.
//
ComponentData64->ConfigurationData = PTR_64(resourceList64);
//
// Copy the resource list header information
//
Copy_CM_PARTIAL_RESOURCE_LIST(resourceList32,resourceList64);
//
// Now thunk each of the resource descriptors
//
descriptorCount = resourceList32->Count;
resourceDesc32 = resourceList32->PartialDescriptors;
resourceDesc64 = &resourceList64->PartialDescriptors;
while (descriptorCount > 0) {
//
// Transfer the common header information
//
Copy_CM_PARTIAL_RESOURCE_DESCRIPTOR(resourceDesc32,resourceDesc64);
descBody32 = &resourceDesc32->u;
descBody64 = &resourceDesc64->u;
RtlZeroMemory(descBody64,
sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR_64) -
FIELD_OFFSET(CM_PARTIAL_RESOURCE_DESCRIPTOR_64,u));
//
// Transfer the body according to the type
//
switch(resourceDesc32->Type) {
case CmResourceTypeNull:
break;
case CmResourceTypePort:
Copy_CM_PRD_PORT(descBody32,descBody64);
break;
case CmResourceTypeInterrupt:
Copy_CM_PRD_INTERRUPT(descBody32,descBody64);
break;
case CmResourceTypeMemory:
Copy_CM_PRD_MEMORY(descBody32,descBody64);
break;
case CmResourceTypeDma:
Copy_CM_PRD_DMA(descBody32,descBody64);
break;
case CmResourceTypeDeviceSpecific:
Copy_CM_PRD_DEVICESPECIFICDATA(descBody32,descBody64);
break;
case CmResourceTypeBusNumber:
Copy_CM_PRD_BUSNUMBER(descBody32,descBody64);
break;
default:
Copy_CM_PRD_GENERIC(descBody32,descBody64);
break;
}
resourceDesc32 += 1;
resourceDesc64 += 1;
descriptorCount -= 1;
}
//
// Calculate how much data, if any, is appended to the resource list.
//
tailSize = ComponentData32->ComponentEntry.ConfigurationDataLength +
(PUCHAR)resourceList32 -
(PUCHAR)resourceDesc32;
if (tailSize > 0) {
//
// Some data is there, append it as-is to the 64-bit structure.
//
tail32 = (PUCHAR)resourceDesc32;
tail64 = (PUCHAR)resourceDesc64;
RtlCopyMemory(tail64,tail32,tailSize);
}
}
BOOLEAN
BlAmd64ContainsResourceList(
IN PCONFIGURATION_COMPONENT_DATA ComponentData32,
OUT PULONG ResourceListSize64
)
/*++
Routine Description:
Given a 32-bit CONFIGURATION_COMPONENT_DATA structure, this routine
determines whether the data associated with the structure contains a
CM_PARTIAL_RESOURCE_LIST structure.
If it does, the size of the 64-bit representation of this structure is calculated,
added to any data that might be appended to the resource list structure, and
returned in ResourceListSize64.
Arguments:
ComponentData32 - Supplies a pointer to the 32-bit structure to transfer.
ResourceListSize64 - Supplies a pointer to a ULONG in which the necessary
additional data size is returned.
Return Value:
Returns TRUE if the CONFIGURATION_COMPONENT_DATA stucture refers to a
CM_PARTIAL_RESOURCE_LIST structure, FALSE otherwise.
--*/
{
ULONG configDataLen;
PCM_PARTIAL_RESOURCE_LIST resourceList;
ULONG resourceCount;
PCM_PARTIAL_RESOURCE_DESCRIPTOR resourceDescriptor;
PCM_PARTIAL_RESOURCE_DESCRIPTOR lastResourceDescriptor;
configDataLen = ComponentData32->ComponentEntry.ConfigurationDataLength;
if (configDataLen < sizeof(CM_PARTIAL_RESOURCE_LIST)) {
//
// Data not large enough to contain the smallest possible resource list
//
return FALSE;
}
resourceList = (PCM_PARTIAL_RESOURCE_LIST)ComponentData32->ConfigurationData;
if (resourceList->Version != 0 || resourceList->Revision != 0) {
//
// Unrecognized version.
//
return FALSE;
}
configDataLen -= FIELD_OFFSET(CM_PARTIAL_RESOURCE_LIST,PartialDescriptors);
resourceCount = resourceList->Count;
if (configDataLen < sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * resourceCount) {
//
// Config data len is not large enough to contain a CM_PARTIAL_RESOURCE_LIST
// as large as this one claims to be.
//
return FALSE;
}
//
// Validate each of the CM_PARTIAL_RESOURCE_DESCRIPTOR structures in the list
//
resourceDescriptor = resourceList->PartialDescriptors;
lastResourceDescriptor = resourceDescriptor + resourceCount;
while (resourceDescriptor < lastResourceDescriptor) {
if (resourceDescriptor->Type > CmResourceTypeMaximum) {
return FALSE;
}
resourceDescriptor += 1;
}
//
// Looks like this is an actual resource list. Calculate the size of any remaining
// data after the CM_PARTIAL_RESOURCE_LIST structure.
//
configDataLen -= sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * resourceCount;
*ResourceListSize64 = sizeof(CM_PARTIAL_RESOURCE_LIST_64) +
sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR_64) * (resourceCount - 1) +
configDataLen;
return TRUE;
}
ARC_STATUS
BlAmd64TransferNlsData(
VOID
)
/*++
Routine Description:
This routine creates an NLS_DATA_BLOCK64 structure that is the
equivalent of the 32-bit NLS_DATA_BLOCK structure referenced within
the 32-bit loader block.
Arguments:
None.
Return Value:
ARC_STATUS - Status of operation.
--*/
{
ARC_STATUS status;
PNLS_DATA_BLOCK nlsDataBlock32;
PNLS_DATA_BLOCK_64 nlsDataBlock64;
nlsDataBlock32 = BlAmd64LoaderBlock32->NlsData;
if (nlsDataBlock32 == NULL) {
return ESUCCESS;
}
nlsDataBlock64 = BlAllocateHeap(sizeof(NLS_DATA_BLOCK_64));
if (nlsDataBlock64 == NULL) {
return ENOMEM;
}
status = Copy_NLS_DATA_BLOCK( nlsDataBlock32, nlsDataBlock64 );
if (status != ESUCCESS) {
return status;
}
BlAmd64LoaderBlock64->NlsData = PTR_64( nlsDataBlock64 );
return ESUCCESS;
}
ARC_STATUS
BlAmd64BuildLoaderBlock64(
VOID
)
/*++
Routine Description:
This routine allocates a 64-bit loader parameter block and copies the
contents of the 32-bit loader parameter block into it.
Arguments:
None.
Return Value:
The status of the operation.
--*/
{
ARC_STATUS status;
//
// Allocate the loader block and extension
//
BlAmd64LoaderBlock64 = BlAllocateHeap(sizeof(LOADER_PARAMETER_BLOCK_64));
if (BlAmd64LoaderBlock64 == NULL) {
return ENOMEM;
}
//
// Copy the contents of the 32-bit loader parameter block to the
// 64-bit version
//
status = Copy_LOADER_PARAMETER_BLOCK( BlAmd64LoaderBlock32, BlAmd64LoaderBlock64 );
if (status != ESUCCESS) {
return status;
}
//
// Build the loader block extension
//
status = BlAmd64BuildLoaderBlockExtension64();
if (status != ESUCCESS) {
return status;
}
return ESUCCESS;
}
ARC_STATUS
BlAmd64TransferMemoryAllocationDescriptors(
VOID
)
/*++
Routine Description:
This routine transfers all of the 32-bit memory allocation descriptors
to a 64-bit list.
The storage for the 64-bit memory allocation descriptors has been
preallocated by a previous call to
BlAmd64AllocateMemoryAllocationDescriptors(). This memory is described
by BlAmd64DescriptorArray and BlAmd64DescriptorArraySize.
Arguments:
None.
Return Value:
The status of the operation.
--*/
{
ARC_STATUS status;
PMEMORY_ALLOCATION_DESCRIPTOR memDesc32;
PMEMORY_ALLOCATION_DESCRIPTOR_64 memDesc64;
PLIST_ENTRY listHead;
PLIST_ENTRY listEntry;
LONG descriptorCount;
//
// Modify some descriptor types. All of the descriptors of type
// LoaderMemoryData really contain things that won't be used in 64-bit
// mode, such as 32-bit page tables and the like.
//
// The descriptors that we really want to stick around are allocated with
// LoaderAmd64MemoryData.
//
// Perform two memory descriptor list search-and-replacements:
//
// LoaderMemoryData -> LoaderOSLoaderHeap
//
// These desriptors will be freed during kernel init phase 1
//
// LoaderAmd64MemoryData -> LoaderMemoryData
//
// This stuff will be kept around
//
//
// All existing LoaderMemoryData refers to structures that are not useful
// once running in long mode. However, we're using some of the structures
// now (32-bit page tables for example), so convert them to
// type LoaderOsloaderHeap, which will be eventually freed by the kernel.
//
BlAmd64ReplaceMemoryDescriptorType(LoaderMemoryData,
LoaderOsloaderHeap,
TRUE);
//
// Same for LoaderStartupPcrPage
//
BlAmd64ReplaceMemoryDescriptorType(LoaderStartupPcrPage,
LoaderOsloaderHeap,
TRUE);
//
// All of the permanent structures that need to be around for longmode
// were temporarily allocated with LoaderAmd64MemoryData. Convert all
// of those to LoaderMemoryData now.
//
BlAmd64ReplaceMemoryDescriptorType(LoaderAmd64MemoryData,
LoaderMemoryData,
TRUE);
//
// Now walk the 32-bit memory descriptors, filling in and inserting a
// 64-bit version into BlAmd64LoaderBlock64.
//
InitializeListHead64( &BlAmd64LoaderBlock64->MemoryDescriptorListHead );
memDesc64 = BlAmd64DescriptorArray;
descriptorCount = BlAmd64DescriptorArraySize;
listHead = &BlAmd64LoaderBlock32->MemoryDescriptorListHead;
listEntry = listHead->Flink;
while (listEntry != listHead && descriptorCount > 0) {
memDesc32 = CONTAINING_RECORD( listEntry,
MEMORY_ALLOCATION_DESCRIPTOR,
ListEntry );
status = Copy_MEMORY_ALLOCATION_DESCRIPTOR( memDesc32, memDesc64 );
if (status != ESUCCESS) {
return status;
}
#if DBG
DbgPrint("Base 0x%08x size 0x%02x %s\n",
memDesc32->BasePage,
memDesc32->PageCount,
BlAmd64MemoryDescriptorText[memDesc32->MemoryType]);
#endif
InsertTailList64( &BlAmd64LoaderBlock64->MemoryDescriptorListHead,
&memDesc64->ListEntry );
listEntry = listEntry->Flink;
memDesc64 = memDesc64 + 1;
descriptorCount -= 1;
}
ASSERT( descriptorCount >= 0 && listEntry == listHead );
return ESUCCESS;
}
ARC_STATUS
BlAmd64AllocateMemoryAllocationDescriptors(
VOID
)
/*++
Routine Description:
This routine preallocates a quantity of memory sufficient to contain
a 64-bit version of each memory allocation descriptor.
The resultant memory is described in two globals: BlAmd64DescriptorArray
and BlAmd64DescriptorArrayCount.
Arguments:
None.
Return Value:
The status of the operation.
--*/
{
PLIST_ENTRY listHead;
PLIST_ENTRY listEntry;
ULONG descriptorCount;
ULONG arraySize;
PMEMORY_ALLOCATION_DESCRIPTOR_64 descriptorArray;
//
// Count the number of descriptors needed.
//
descriptorCount = 0;
listHead = &BlAmd64LoaderBlock32->MemoryDescriptorListHead;
listEntry = listHead->Flink;
while (listEntry != listHead) {
descriptorCount += 1;
listEntry = listEntry->Flink;
}
//
// Allocate memory sufficient to contain them all in 64-bit form.
//
arraySize = descriptorCount *
sizeof(MEMORY_ALLOCATION_DESCRIPTOR_64);
descriptorArray = BlAllocateHeap(arraySize);
if (descriptorArray == NULL) {
return ENOMEM;
}
BlAmd64DescriptorArray = descriptorArray;
BlAmd64DescriptorArraySize = descriptorCount;
return ESUCCESS;
}
ARC_STATUS
BlAmd64TransferLoadedModuleState(
VOID
)
/*++
Routine Description:
This routine transfers the 32-bit list of LDR_DATA_TABLE_ENTRY structures
to an equivalent 64-bit list.
Arguments:
None.
Return Value:
The status of the operation.
--*/
{
PLDR_DATA_TABLE_ENTRY dataTableEntry32;
PLDR_DATA_TABLE_ENTRY_64 dataTableEntry64;
PLIST_ENTRY listEntry;
PLIST_ENTRY listHead;
ARC_STATUS status;
InitializeListHead64( &BlAmd64LoaderBlock64->LoadOrderListHead );
//
// For each of the LDR_DATA_TABLE_ENTRY structures in the 32-bit
// loader parameter block, create a 64-bit LDR_DATA_TABLE_ENTRY
// and queue it on the 64-bit loader parameter block.
//
listHead = &BlAmd64LoaderBlock32->LoadOrderListHead;
listEntry = listHead->Flink;
while (listEntry != listHead) {
dataTableEntry32 = CONTAINING_RECORD( listEntry,
LDR_DATA_TABLE_ENTRY,
InLoadOrderLinks );
status = BlAmd64BuildLdrDataTableEntry64( dataTableEntry32,
&dataTableEntry64 );
if (status != ESUCCESS) {
return status;
}
//
// Insert it into the 64-bit loader block's data table queue.
//
InsertTailList64( &BlAmd64LoaderBlock64->LoadOrderListHead,
&dataTableEntry64->InLoadOrderLinks );
listEntry = listEntry->Flink;
}
return ESUCCESS;
}
ARC_STATUS
BlAmd64BuildLdrDataTableEntry64(
IN PLDR_DATA_TABLE_ENTRY DataTableEntry32,
OUT PLDR_DATA_TABLE_ENTRY_64 *DataTableEntry64
)
/*++
Routine Description:
This routine transfers the contents of a single 32-bit
LDR_DATA_TABLE_ENTRY structure to the 64-bit equivalent.
Arguments:
DataTableEntry32 - Supplies a pointer to the source structure.
DataTableEntry64 - Supplies a pointer to the destination pointer to
the created structure.
Return Value:
The status of the operation.
--*/
{
ARC_STATUS status;
PLDR_DATA_TABLE_ENTRY_64 dataTableEntry64;
//
// Allocate a 64-bit data table entry and transfer the 32-bit
// contents
//
dataTableEntry64 = BlAllocateHeap( sizeof(LDR_DATA_TABLE_ENTRY_64) );
if (dataTableEntry64 == NULL) {
return ENOMEM;
}
status = Copy_LDR_DATA_TABLE_ENTRY( DataTableEntry32, dataTableEntry64 );
if (status != ESUCCESS) {
return status;
}
*DataTableEntry64 = dataTableEntry64;
//
// Later on, we'll need to determine the 64-bit copy of this data
// table entry. Store the 64-bit pointer to the copy here.
//
*((POINTER64 *)&DataTableEntry32->DllBase) = PTR_64(dataTableEntry64);
return ESUCCESS;
}
ARC_STATUS
BlAmd64BuildLoaderBlockExtension64(
VOID
)
/*++
Routine Description:
This routine transfers the contents of the 32-bit loader block
extension to a 64-bit equivalent.
Arguments:
None.
Return Value:
The status of the operation.
--*/
{
PLOADER_PARAMETER_EXTENSION_64 loaderExtension;
ARC_STATUS status;
//
// Allocate the 64-bit extension and transfer the contents of the
// 32-bit block.
//
loaderExtension = BlAllocateHeap( sizeof(LOADER_PARAMETER_EXTENSION_64) );
if (loaderExtension == NULL) {
return ENOMEM;
}
//
// Perform automatic copy of most fields
//
status = Copy_LOADER_PARAMETER_EXTENSION( BlLoaderBlock->Extension,
loaderExtension );
if (status != ESUCCESS) {
return status;
}
//
// Initialize Symbol list head properly
//
InitializeListHead64( &loaderExtension->FirmwareDescriptorListHead );
//
// Manually fix up remaining fields
//
loaderExtension->Size = sizeof(LOADER_PARAMETER_EXTENSION_64);
BlAmd64LoaderBlock64->Extension = PTR_64(loaderExtension);
return ESUCCESS;
}
ARC_STATUS
BlAmd64TransferBootDriverNodes(
VOID
)
/*++
Routine Description:
This routine transfers the 32-bit list of BOOT_DRIVER_NODE structures
to an equivalent 64-bit list.
Arguments:
None.
Return Value:
The status of the operation.
--*/
{
PBOOT_DRIVER_LIST_ENTRY driverListEntry32;
PBOOT_DRIVER_NODE driverNode32;
PBOOT_DRIVER_NODE_64 driverNode64;
POINTER64 dataTableEntry64;
PKLDR_DATA_TABLE_ENTRY dataTableEntry;
PLIST_ENTRY listEntry;
PLIST_ENTRY listHead;
ARC_STATUS status;
InitializeListHead64( &BlAmd64LoaderBlock64->BootDriverListHead );
//
// For each of the BOOT_DRIVER_NODE structures in the 32-bit
// loader parameter block, create a 64-bit BOOT_DRIVER_NODE
// and (possibly) associated LDR_DATA_TABLE_ENTRY structure.
//
listHead = &BlAmd64LoaderBlock32->BootDriverListHead;
listEntry = listHead->Flink;
while (listEntry != listHead) {
driverListEntry32 = CONTAINING_RECORD( listEntry,
BOOT_DRIVER_LIST_ENTRY,
Link );
driverNode32 = CONTAINING_RECORD( driverListEntry32,
BOOT_DRIVER_NODE,
ListEntry );
driverNode64 = BlAllocateHeap( sizeof(BOOT_DRIVER_NODE_64) );
if (driverNode64 == NULL) {
return ENOMEM;
}
status = Copy_BOOT_DRIVER_NODE( driverNode32, driverNode64 );
if (status != ESUCCESS) {
return status;
}
dataTableEntry = driverNode32->ListEntry.LdrEntry;
if (dataTableEntry != NULL) {
//
// There is already a 64-bit copy of this table entry, and we
// stored a pointer to it at DllBase.
//
dataTableEntry64 = *((POINTER64 *)&dataTableEntry->DllBase);
driverNode64->ListEntry.LdrEntry = dataTableEntry64;
}
//
// Now insert the driver list entry into the 64-bit loader block.
//
InsertTailList64( &BlAmd64LoaderBlock64->BootDriverListHead,
&driverNode64->ListEntry.Link );
listEntry = listEntry->Flink;
}
return ESUCCESS;
}
ARC_STATUS
BlAmd64CheckForLongMode(
IN ULONG LoadDeviceId,
IN OUT PCHAR KernelPath,
IN PCHAR KernelFileName
)
/*++
Routine Description:
This routine examines a kernel image and determines whether it was
compiled for AMD64. The global BlAmd64UseLongMode is set to non-zero
if a long-mode kernel is discovered.
Arguments:
LoadDeviceId - Supplies the load device identifier.
KernelPath - Supplies a pointer to the path to the kernel directory.
Upon successful return, KernelFileName will be appended
to this path.
KernelFileName - Supplies a pointer to the name of the kernel file.
Note: If KernelPath already contains the full path and filename of
the kernel image to check, pass a pointer to "\0" for
KernelFileName.
Return Value:
The status of the operation. Upon successful completion ESUCCESS
is returned, whether long mode capability was detected or not.
--*/
{
CHAR localBufferSpace[ SECTOR_SIZE * 2 + SECTOR_SIZE - 1 ];
PCHAR localBuffer;
ULONG fileId;
PIMAGE_NT_HEADERS32 ntHeaders;
ARC_STATUS status;
ULONG bytesRead;
PCHAR kernelNameTarget;
//
// File I/O here must be sector-aligned.
//
localBuffer = (PCHAR)
(((ULONG)localBufferSpace + SECTOR_SIZE - 1) & ~(SECTOR_SIZE - 1));
//
// Build the path to the kernel and open it.
//
kernelNameTarget = KernelPath + strlen(KernelPath);
strcpy(kernelNameTarget, KernelFileName);
status = BlOpen( LoadDeviceId, KernelPath, ArcOpenReadOnly, &fileId );
*kernelNameTarget = '\0'; // Restore the kernel path, assuming
// failure.
if (status != ESUCCESS) {
return status;
}
//
// Read the PE image header
//
status = BlRead( fileId, localBuffer, SECTOR_SIZE * 2, &bytesRead );
BlClose( fileId );
//
// Determine whether the image header is valid, and if so whether
// the image is AMD64, I386 or something else.
//
ntHeaders = RtlImageNtHeader( localBuffer );
if (ntHeaders == NULL) {
return EBADF;
}
if (IMAGE_64BIT(ntHeaders)) {
//
// Return with the kernel name appended to the path
//
if (BlIsAmd64Supported() != FALSE) {
strcpy(kernelNameTarget, KernelFileName);
BlAmd64UseLongMode = TRUE;
status = ESUCCESS;
} else {
//
// We have an AMD64 image, but the processor does not support
// AMD64. There is nothing we can do.
//
status = EBADF;
}
} else if (IMAGE_32BIT(ntHeaders)) {
ASSERT( BlAmd64UseLongMode == FALSE );
status = ESUCCESS;
} else {
status = EBADF;
}
return status;
}
ARC_STATUS
BlAmd64PrepareSystemStructures(
VOID
)
/*++
Routine Description:
This routine allocates and initializes several structures necessary
for transfer to an AMD64 kernel. These structures include:
GDT
IDT
KTSS64
Idle thread stack
DPC stack
Double fault stack
MCA exception stack
Arguments:
None.
Return Value:
The status of the operation.
--*/
{
PCHAR processorData;
ULONG dataSize;
ULONG descriptor;
ULONG stackOffset;
PKTSS64_64 sysTss64;
PCHAR idleStack;
PCHAR dpcStack;
PCHAR doubleFaultStack;
PCHAR mcaStack;
PVOID gdt64;
PVOID idt64;
ARC_STATUS status;
//
// Calculate the cumulative, rounded size of the various structures that
// we need, and allocate a sufficient number of pages.
//
dataSize = ROUNDUP16(GDT_64_SIZE) +
ROUNDUP16(IDT_64_SIZE) +
ROUNDUP16(sizeof(KTSS64_64));
dataSize = ROUNDUP_PAGE(dataSize);
stackOffset = dataSize;
dataSize += KERNEL_STACK_SIZE_64 + // Idle thread stack
KERNEL_STACK_SIZE_64 + // DPC stack
DOUBLE_FAULT_STACK_SIZE_64 + // Double fault stack
MCA_EXCEPTION_STACK_SIZE_64; // MCA exception stack
//
// dataSize is still page aligned.
//
status = BlAllocateDescriptor( LoaderAmd64MemoryData,
0,
dataSize / PAGE_SIZE,
&descriptor );
if (status != ESUCCESS) {
return status;
}
processorData = (PCHAR)(descriptor * PAGE_SIZE | KSEG0_BASE_X86);
//
// Zero the block that was just allocated, then get local pointers to the
// various structures within.
//
RtlZeroMemory( processorData, dataSize );
//
// Assign the stack pointers. Stack pointers start at the TOP of their
// respective stack areas.
//
idleStack = processorData + stackOffset + KERNEL_STACK_SIZE_64;
dpcStack = idleStack + KERNEL_STACK_SIZE_64;
doubleFaultStack = dpcStack + DOUBLE_FAULT_STACK_SIZE_64;
mcaStack = doubleFaultStack + MCA_EXCEPTION_STACK_SIZE_64;
//
// Record the idle stack base so that we can switch to it in amd64s.asm
//
BlAmd64IdleStack64 = PTR_64(idleStack);
//
// Assign pointers to GDT, IDT and KTSS64.
//
gdt64 = (PVOID)processorData;
processorData += ROUNDUP16(GDT_64_SIZE);
idt64 = (PVOID)processorData;
processorData += ROUNDUP16(IDT_64_SIZE);
sysTss64 = (PKTSS64_64)processorData;
processorData += ROUNDUP16(sizeof(KTSS64_64));
//
// Build the GDT. This is done in amd64.c as it involves AMD64
// structure definitions. The IDT remains zeroed.
//
BlAmd64BuildAmd64GDT( sysTss64, gdt64 );
//
// Build the pseudo-descriptors for the GDT and IDT. These will
// be referenced during the long-mode transition in amd64s.asm.
//
BlAmd64GdtDescriptor.Limit = (USHORT)(GDT_64_SIZE - 1);
BlAmd64GdtDescriptor.Base = PTR_64(gdt64);
BlAmd64IdtDescriptor.Limit = (USHORT)(IDT_64_SIZE - 1);
BlAmd64IdtDescriptor.Base = PTR_64(idt64);
//
// Build another GDT pseudo-descriptor, this one with a 32-bit
// base. This base address must be a 32-bit address that is addressible
// from long mode during init, so use the mapping in the identity mapped
// region.
//
BlAmd32GdtDescriptor.Limit = (USHORT)(GDT_64_SIZE - 1);
BlAmd32GdtDescriptor.Base = (ULONG)gdt64 ^ KSEG0_BASE_X86;
//
// Initialize the system TSS
//
sysTss64->Rsp0 = PTR_64(idleStack);
sysTss64->Ist[TSS64_IST_PANIC] = PTR_64(doubleFaultStack);
sysTss64->Ist[TSS64_IST_MCA] = PTR_64(mcaStack);
//
// Fill required fields within the loader block
//
BlAmd64LoaderBlock64->KernelStack = PTR_64(dpcStack);
return ESUCCESS;
}
VOID
BlAmd64ReplaceMemoryDescriptorType(
IN TYPE_OF_MEMORY Target,
IN TYPE_OF_MEMORY Replacement,
IN BOOLEAN Coallesce
)
/*++
Routine Description:
This routine walks the 32-bit memory allocation descriptor list and
performs a "search and replace" of the types therein.
Optionally, it will coallesce each successful replacement with
adjacent descriptors of like type.
Arguments:
Target - The descriptor type to search for
Replacement - The type with which to replace each located Target type.
Coallesce - If !FALSE, indicates that each successful replacement should
be coallesced with any like-typed neighbors.
Return Value:
None.
--*/
{
PMEMORY_ALLOCATION_DESCRIPTOR descriptor;
PMEMORY_ALLOCATION_DESCRIPTOR adjacentDescriptor;
PLIST_ENTRY listHead;
PLIST_ENTRY listEntry;
PLIST_ENTRY adjacentListEntry;
listHead = &BlAmd64LoaderBlock32->MemoryDescriptorListHead;
listEntry = listHead;
while (TRUE) {
listEntry = listEntry->Flink;
if (listEntry == listHead) {
break;
}
descriptor = CONTAINING_RECORD(listEntry,
MEMORY_ALLOCATION_DESCRIPTOR,
ListEntry);
if (descriptor->MemoryType != Target) {
continue;
}
descriptor->MemoryType = Replacement;
if (Coallesce == FALSE) {
//
// Do not attempt to coallesce
//
continue;
}
//
// Now attempt to coallesce the descriptor. First try the
// next descriptor.
//
adjacentListEntry = listEntry->Flink;
if (adjacentListEntry != listHead) {
adjacentDescriptor = CONTAINING_RECORD(adjacentListEntry,
MEMORY_ALLOCATION_DESCRIPTOR,
ListEntry);
if (adjacentDescriptor->MemoryType == descriptor->MemoryType &&
descriptor->BasePage + descriptor->PageCount ==
adjacentDescriptor->BasePage) {
descriptor->PageCount += adjacentDescriptor->PageCount;
BlRemoveDescriptor(adjacentDescriptor);
}
}
//
// Now try the previous descriptor.
//
adjacentListEntry = listEntry->Blink;
if (adjacentListEntry != listHead) {
adjacentDescriptor = CONTAINING_RECORD(adjacentListEntry,
MEMORY_ALLOCATION_DESCRIPTOR,
ListEntry);
if (adjacentDescriptor->MemoryType == descriptor->MemoryType &&
adjacentDescriptor->BasePage + adjacentDescriptor->PageCount ==
descriptor->BasePage) {
descriptor->PageCount += adjacentDescriptor->PageCount;
descriptor->BasePage -= adjacentDescriptor->PageCount;
BlRemoveDescriptor(adjacentDescriptor);
}
}
}
}
ARC_STATUS
BlAmd64FixSharedUserPage(
VOID
)
{
PFN_NUMBER pfn;
PMEMORY_ALLOCATION_DESCRIPTOR descriptor;
ARC_STATUS status;
//
// The shared user page is allocated as LoaderMemoryData. All
// LoaderMemoryData descriptors will be converted to LoaderOsloaderHeap
// during the transition to 64-bit mode, as the assumption is made that
// all of the old structures will no longer be needed.
//
// The shared user page is the exception to this rule, so it must be
// found and placed into an appropriately marked descriptor.
//
//
// Get the pfn of the shared user page, find its descriptor,
// carve out a new descriptor that contains just that page and give
// it a type of LoaderAmd64MemoryData.
//
BlAmd64IsPageMapped( KI_USER_SHARED_DATA, &pfn, NULL );
descriptor = BlFindMemoryDescriptor( pfn );
status = BlGenerateDescriptor(descriptor,
LoaderAmd64MemoryData,
pfn,
1);
return status;
}
BOOLEAN
BlAmd64Setup (
IN PCHAR SetupDevice
)
/*++
Routine Description:
This routine determines whether we are installing an I386 or AMD64 build.
If the directory "\\AMD64" exists at the root of DriveId then it is
assumed that an AMD64 installation is being performed.
Arguments:
SetupDevice - Supplies the ARC path to the setup device. This parameter
need only be supplied on the first invocation of this routine. The
result of the first call is cached for subsequent invocations.
Return Value:
TRUE - An AMD64 installation is being performed.
FALSE - An I386 installation is being performed.
--*/
{
ULONG deviceId;
ULONG dirId;
ARC_STATUS status;
static BOOLEAN alreadyDetected = FALSE;
static BOOLEAN detectedAmd64 = FALSE;
if (alreadyDetected == FALSE) {
ASSERT(SetupDevice != NULL);
status = ArcOpen(SetupDevice, ArcOpenReadOnly, &deviceId);
if (status == ESUCCESS) {
status = BlOpen(deviceId, "\\AMD64", ArcOpenDirectory, &dirId);
if (status == ESUCCESS) {
detectedAmd64 = TRUE;
BlClose(dirId);
}
ArcClose(deviceId);
}
alreadyDetected = TRUE;
}
return detectedAmd64;
}
VOID
BlCheckForAmd64Image(
PPO_MEMORY_IMAGE MemImage
)
/*++
Routine Description:
This routine determines whether a hibernate file was created for
Amd64 platform. BlAmd64UseLongMode will be set accordingly.
Arguments:
MemImage - Header of hibernate image file.
Return Value:
None.
--*/
{
//
// It is assumed that "version" and "LengthSelf" field can be reference
// in same way between a x86 and an Amd64 image header.
//
if((MemImage->Version == 0) &&
(MemImage->LengthSelf == sizeof(PO_MEMORY_IMAGE_64))) {
BlAmd64UseLongMode = TRUE;
}
}
ULONG
BlAmd64FieldOffset_PO_MEMORY_IMAGE(
ULONG offset32
)
/*++
Routine Description:
This routine helps to access 64-bit version of PO_MEMORY_IMAGE from
its 32-bit definition. It calculates the offset of a field in 64-bit
definition from the offset of same field in 32-bit definiation.
Arguments:
offset32 - Field offset of 32-bit definiation.
Return Value:
Field offset of 64-bit definiation.
--*/
{
PCOPY_REC copyRec;
copyRec = cr3264_PO_MEMORY_IMAGE;
while (copyRec->Size32 != 0) {
if (copyRec->Offset32 == offset32) {
return copyRec->Offset64;
}
copyRec++;
}
return 0;
}
ULONG
BlAmd64FieldOffset_PO_MEMORY_RANGE_ARRAY_LINK(
ULONG offset32
)
/*++
Routine Description:
This routine helps to access 64-bit version of PO_MEMORY_RANGE_ARRAY_LINK
from its 32-bit definition. It calculates the offset of a field in 64-bit
definition from the offset of same field in 32-bit definiation.
Arguments:
offset32 - Field offset of 32-bit definiation.
Return Value:
Field offset of 64-bit definiation.
--*/
{
PCOPY_REC copyRec;
copyRec = cr3264_PO_MEMORY_RANGE_ARRAY_LINK;
while (copyRec->Size32 != 0) {
if (copyRec->Offset32 == offset32) {
return copyRec->Offset64;
}
copyRec++;
}
return 0;
}
ULONG
BlAmd64FieldOffset_PO_MEMORY_RANGE_ARRAY_RANGE(
ULONG offset32
)
/*++
Routine Description:
This routine helps to access 64-bit version of PO_MEMORY_RANGE_ARRAY_RANGE
from its 32-bit definition. It calculates the offset of a field in 64-bit
definition from the offset of same field in 32-bit definiation.
Arguments:
offset32 - Field offset of 32-bit definiation.
Return Value:
Field offset of 64-bit definiation.
--*/
{
PCOPY_REC copyRec;
copyRec = cr3264_PO_MEMORY_RANGE_ARRAY_RANGE;
while (copyRec->Size32 != 0) {
if (copyRec->Offset32 == offset32) {
return copyRec->Offset64;
}
copyRec++;
}
return 0;
}
ULONG
BlAmd64ElementOffset_PO_MEMORY_RANGE_ARRAY_LINK(
ULONG index
)
/*++
Routine Description:
This routine calculates the offset of a element in a structure array.
Each element in this array is defined as PO_MORY_RANGE_ARRAY_LINK in
it 64-bit format.
Arguments:
index - Supplies the index of a element.
Return Value:
Offset of the element from base address of the array.
--*/
{
return (ULONG)(&(((PO_MEMORY_RANGE_ARRAY_LINK_64 *)0)[index]));
}
ULONG
BlAmd64ElementOffset_PO_MEMORY_RANGE_ARRAY_RANGE(
ULONG index
)
/*++
Routine Description:
This routine calculates the offset of a element in a structure array.
Each element in this array is defined as PO_MEMORY_RANGE_ARRAY_RANGE
in its 64-bit format.
Arguments:
index - Supplies the index of a element.
Return Value:
Offset of the element from base address of the array.
--*/
{
return (ULONG)(&(((PO_MEMORY_RANGE_ARRAY_RANGE_64 *)0)[index]));
}
#define BL_ENABLE_REMAP
#if defined(BL_ENABLE_REMAP)
#include "hammernb.h"
#include "acpitabl.h"
#include "pci.h"
#include "ntacpi.h"
#if !defined(_4G)
#define _4G (1UI64 << 32)
#endif
extern PRSDP BlRsdp;
extern PRSDT BlRsdt;
extern PXSDT BlXsdt;
BOOLEAN
BlAmd64GetNode1Info (
OUT ULONG *Base,
OUT ULONG *Size
);
BOOLEAN
BlAmd64RelocateAcpi (
ULONG Node0Base,
ULONG Node0Limit,
ULONG Node1Base,
ULONG Node1Limit
);
BOOLEAN
BlAmd64RemapMTRRs (
IN ULONG OldBase,
IN ULONG NewBase,
IN ULONG Size
);
BOOLEAN
BlAmd64RemapNode1Dram (
IN ULONG NewBase
);
ULONG
StringToUlong (
IN PCHAR String
);
BOOLEAN
BlAmd64RemapDram (
IN PCHAR LoaderOptions
)
/*++
Routine Description:
This routine looks for the /RELOCATEPHYSICAL= switch supplied as a loader
option. The option directs the loader to relocate node 1's physical memory
to the address supplied.
The address represents the new physical memory base in 1GB units.
For example, to relocate node 1's physical memory to 128GB, use:
/RELOCATEPHYSICAL=128
Arguments:
LoaderOptions - Supplies a pointer to the loader option string
Return Value:
TRUE if the relocation was performed, FALSE otherwise
--*/
{
BOOLEAN result;
ULONG oldBase;
ULONG oldLimit;
ULONG newBase;
ULONG newBasePage;
ULONG size;
PMEMORY_ALLOCATION_DESCRIPTOR descriptor;
PLIST_ENTRY listHead;
PLIST_ENTRY listEntry;
ULONG descriptorBase;
ULONG descriptorLimit;
ARC_STATUS status;
PCHAR pch;
ULONG type;
newBase = 0;
if (LoaderOptions != NULL) {
pch = strstr(LoaderOptions,"RELOCATEPHYSICAL=");
if (pch != NULL) {
newBase = StringToUlong( pch + strlen( "RELOCATEPHYSICAL=" ));
}
}
if (newBase == 0) {
return FALSE;
}
//
// The parameter is supplied in GB, convert to 16MB chunks for internal
// use
//
newBase *= 64;
//
// Determine the chunk of physical memory associated with node 1.
// Note that this routine will relocate the ACPI tables if a suitable
// node 1 bridge device was found.
//
result = BlAmd64GetNode1Info( &oldBase, &size );
if (result == FALSE) {
return FALSE;
}
newBasePage = newBase << (24 - 12);
oldLimit = oldBase + size - 1;
//
// Make sure that the descriptors describing that physical memory
// haven't already been allocated. Acceptable descriptor
// types are Free, LoaderReserve and SpecialMemory.
//
listHead = &BlLoaderBlock->MemoryDescriptorListHead;
listEntry = listHead->Flink;
while (listEntry != listHead) {
descriptor = CONTAINING_RECORD(listEntry,
MEMORY_ALLOCATION_DESCRIPTOR,
ListEntry);
descriptorBase = descriptor->BasePage;
descriptorLimit = descriptorBase + descriptor->PageCount - 1;
if ((descriptorBase <= oldLimit) && (descriptorLimit >= oldBase)) {
//
// Some or all of this memory descriptor lies within the
// relocated region.
//
if (descriptor->MemoryType != LoaderFree &&
descriptor->MemoryType != LoaderSpecialMemory &&
descriptor->MemoryType != LoaderReserve) {
return FALSE;
}
}
listEntry = listEntry->Flink;
}
//
// From the loader perspective everything looks good, perform the remap.
//
result = BlAmd64RemapNode1Dram( newBase );
if (result == FALSE) {
return FALSE;
}
//
// The bridge(s) have been reprogrammed. Now walk the memory descriptor
// lists, performing necessary relocations.
//
listHead = &BlLoaderBlock->MemoryDescriptorListHead;
listEntry = listHead->Flink;
while (listEntry != listHead) {
descriptor = CONTAINING_RECORD(listEntry,
MEMORY_ALLOCATION_DESCRIPTOR,
ListEntry);
descriptorBase = descriptor->BasePage;
descriptorLimit = descriptorBase + descriptor->PageCount - 1;
if ((descriptorBase <= oldLimit) && (descriptorLimit >= oldBase)) {
//
// Some or all of this memory descriptor lies within the
// relocated region.
//
if (descriptorBase >= oldBase && descriptorLimit <= oldLimit) {
//
// The descriptor lies entirely within the relocation range
// so relocate the whole thing.
//
} else {
//
// Only part of the descriptor lies within the relocation
// range, so a new descriptor must be allocated.
//
if (descriptorBase < oldBase) {
descriptorBase = oldBase;
}
if (descriptorLimit > oldLimit) {
descriptorLimit = oldLimit;
}
type = descriptor->MemoryType;
status = BlGenerateDescriptor( descriptor,
LoaderSpecialMemory,
descriptorBase,
descriptorLimit - descriptorBase + 1 );
ASSERT(status == ESUCCESS);
listEntry = listEntry->Flink;
descriptor = CONTAINING_RECORD(listEntry,
MEMORY_ALLOCATION_DESCRIPTOR,
ListEntry);
descriptor->MemoryType = type;
}
listEntry = listEntry->Flink;
BlRemoveDescriptor( descriptor );
descriptor->BasePage = descriptor->BasePage - oldBase + newBasePage;
BlInsertDescriptor( descriptor );
} else {
listEntry = listEntry->Flink;
}
}
//
// Recalculate BlHighestPage
//
BlHighestPage = 0;
listHead = &BlLoaderBlock->MemoryDescriptorListHead;
listEntry = listHead->Flink;
while (listEntry != listHead) {
descriptor = CONTAINING_RECORD(listEntry,
MEMORY_ALLOCATION_DESCRIPTOR,
ListEntry);
descriptorBase = descriptor->BasePage;
descriptorLimit = descriptorBase + descriptor->PageCount - 1;
if (descriptor->MemoryType != LoaderSpecialMemory &&
descriptor->MemoryType != LoaderReserve &&
descriptorLimit > BlHighestPage) {
BlHighestPage = descriptorLimit;
}
listEntry = listEntry->Flink;
}
//
// Remap the MTRRs
//
result = BlAmd64RemapMTRRs( oldBase, newBase, oldLimit - oldBase + 1 );
if (result == FALSE) {
return FALSE;
}
return TRUE;
}
ULONG
StringToUlong (
IN PCHAR String
)
/*++
Routine Description:
This routine converts a hexadecimal or decimal string to
a 32-bit unsigned integer.
Arguments:
String - Supplies a null-terminated ASCII string in decimal or
hexadecimal format.
01234567 - decimal format
0x01234567 - hexadecimal format
The input string is processed until an invalid character or
the end of the string is encountered.
Return Value:
Returns the value of the parsed string.
--*/
{
CHAR ch;
PCHAR pch;
ULONG result;
int radix;
UCHAR digit;
pch = String;
result = 0;
radix = 10;
while (TRUE) {
ch = (char)toupper(*pch);
if ((ch >= '0' && ch <= '9') ||
(ch >= 'A' && ch <= 'F')) {
if (ch >= '0' && ch <= '9') {
digit = ch - '0';
} else {
digit = ch - 'A' + 10;
}
result = result * radix + digit;
} else if (ch == 'X') {
if (result == 0) {
radix = 16;
} else {
break;
}
} else {
break;
}
pch += 1;
}
return result;
}
BOOLEAN
BlAmd64RemapNode1Dram (
IN ULONG NewBase
)
/*++
Routine Description:
Relocates node 1 memory to a new physical address and reprograms
MSRs related to the physical memory map.
Arguments:
NewBase - Supplies bits [39:24] of the desired new physical base address
of the memory associated with node 1.
Return Value:
TRUE if the operation was successful, FALSE otherwise.
--*/
{
AMD_NB_FUNC1_CONFIG nodeConfigArray[8];
PAMD_NB_FUNC1_CONFIG nodeConfig;
PAMD_NB_DRAM_MAP dramMap;
ULONG length;
PCI_SLOT_NUMBER slotNumber;
ULONG nodeCount;
ULONG nodeIndex;
ULONG span;
ULONG oldBase;
ULONG oldLimit;
ULONG newLimit;
ULONG64 topMem;
ULONG64 topMem4G;
ULONG64 msrValue;
ULONG64 base64;
ULONG64 limit64;
//
// NewBase supplies the new DRAM base[39:24]
//
nodeCount = 0;
nodeConfig = nodeConfigArray;
do {
slotNumber.u.AsULONG = 0;
slotNumber.u.bits.DeviceNumber = NB_DEVICE_BASE + nodeCount;
slotNumber.u.bits.FunctionNumber = 1;
length = HalGetBusDataByOffset( PCIConfiguration,
0,
slotNumber.u.AsULONG,
nodeConfig,
0,
sizeof(*nodeConfig) );
if (length != sizeof(*nodeConfig)) {
break;
}
if (BlAmd64ValidateBridgeDevice( nodeConfig ) == FALSE) {
break;
}
#if 0
for (mapIndex = 0; mapIndex < 8; mapIndex += 1) {
if (nodeConfig->DRAMMap[mapIndex].ReadEnable != 0) {
limit = nodeConfig->DRAMMap[mapIndex].Limit;
if (limit > NewBase) {
//
// The new base was found to conflict with existing
// ram.
//
return FALSE;
}
}
}
#endif
nodeCount += 1;
nodeConfig += 1;
} while (nodeCount <= 8);
if (nodeCount < 2) {
//
// This remap can only be performed on systems with more than
// two nodes.
//
return FALSE;
}
//
// We always remap the second node's memory (node 1).
//
nodeConfig = nodeConfigArray;
dramMap = &nodeConfig->DRAMMap[1];
oldBase = dramMap->Base;
oldLimit = dramMap->Limit;
span = oldLimit - oldBase;
newLimit = NewBase + span;
for (nodeIndex = 0; nodeIndex < nodeCount; nodeIndex += 1) {
ASSERT(dramMap->Base == oldBase);
ASSERT(dramMap->Limit == oldLimit);
dramMap->Base = NewBase;
dramMap->Limit = newLimit;
slotNumber.u.AsULONG = 0;
slotNumber.u.bits.DeviceNumber = NB_DEVICE_BASE + nodeIndex;
slotNumber.u.bits.FunctionNumber = 1;
length = HalSetBusDataByOffset( PCIConfiguration,
0,
slotNumber.u.AsULONG,
dramMap,
FIELD_OFFSET(AMD_NB_FUNC1_CONFIG,DRAMMap[1]),
sizeof(*dramMap) );
if (length != sizeof(*dramMap)) {
//
// We may be severely hosed here, if we have already
// reprogrammed some of the bridges.
//
return FALSE;
}
nodeConfig += 1;
dramMap = &nodeConfig->DRAMMap[1];
}
//
// Determine the address of the last byte of ram under 4G and the last
// byte of ram overall.
//
topMem = 0;
topMem4G = 0;
for (nodeIndex = 0; nodeIndex < nodeCount; nodeIndex += 1) {
base64 = nodeConfigArray[0].DRAMMap[nodeIndex].Base;
base64 <<= 24;
limit64 = nodeConfigArray[0].DRAMMap[nodeIndex].Limit;
limit64 = (limit64 + 1) << 24;
if (base64 < _4G) {
if (topMem4G < limit64) {
topMem4G = limit64;
}
}
if (topMem < limit64) {
topMem = limit64;
}
}
//
// Indicate whether a memory hole exists below 4G.
//
if (topMem4G < _4G) {
msrValue = RDMSR(MSR_TOP_MEM);
WRMSR(MSR_TOP_MEM,topMem4G & MSR_TOP_MEM_MASK);
}
//
// If memory above _4G was located then enable and program TOP_MEM_2
//
if (topMem > _4G) {
msrValue = RDMSR(MSR_SYSCFG);
msrValue |= SYSCFG_MTRRTOM2EN;
WRMSR(MSR_TOP_MEM_2, topMem & MSR_TOP_MEM_MASK);
WRMSR(MSR_SYSCFG,msrValue);
}
return TRUE;
}
BOOLEAN
BlAmd64GetNode1Info (
OUT ULONG *Base,
OUT ULONG *Size
)
/*++
Routine Description:
This routine determines the configuration of the block of physical
memory associated with node 1 (the second northbridge).
It also relocates the ACPI tables in node 1 to memory in node 0.
Arguments:
Base - supplies a pointer to the location in which to store the base
PFN of the node 1 memory block.
Size - supplies a pointer to the location in which to store the size,
in pages, of the node 1 memory block.
Return Value:
TRUE - A suitable second northbridge was found and the ACPI tables therein
were relocated if necessary.
FALSE - A suitable second northbridge was not found.
--*/
{
AMD_NB_FUNC1_CONFIG nodeConfig;
PCI_SLOT_NUMBER slotNumber;
ULONG length;
ULONG base;
ULONG size;
ULONG node0Base;
ULONG node0Size;
//
// Get the configuration of northbridge 1.
//
slotNumber.u.AsULONG = 0;
slotNumber.u.bits.DeviceNumber = NB_DEVICE_BASE + 1;
slotNumber.u.bits.FunctionNumber = 1;
length = HalGetBusDataByOffset( PCIConfiguration,
0,
slotNumber.u.AsULONG,
&nodeConfig,
0,
sizeof(nodeConfig) );
if (length != sizeof(nodeConfig)) {
return FALSE;
}
if (BlAmd64ValidateBridgeDevice( &nodeConfig ) == FALSE) {
return FALSE;
}
//
// A second northbridge exists, the relocation can be performed.
//
base = nodeConfig.DRAMMap[1].Base;
size = nodeConfig.DRAMMap[1].Limit - base + 1;
*Base = base << (24 - 12);
*Size = size << (24 - 12);
node0Base = nodeConfig.DRAMMap[0].Base;
node0Size = nodeConfig.DRAMMap[0].Limit - node0Base + 1;
node0Base <<= (24 - 12);
node0Size <<= (24 - 12);
BlAmd64RelocateAcpi( node0Base,
node0Base + node0Size - 1,
*Base,
*Base + *Size - 1 );
return TRUE;
}
BOOLEAN
BlAmd64RemapMTRRs (
IN ULONG OldBase,
IN ULONG NewBase,
IN ULONG Size
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
//
// All parameters expressed in pages
//
ULONG mtrrCount;
ULONG index;
MTRR_CAPABILITIES mtrrCapabilities;
PMTRR_VARIABLE_BASE baseArray;
PMTRR_VARIABLE_MASK maskArray;
ULONG allocationSize;
UNREFERENCED_PARAMETER(Size);
UNREFERENCED_PARAMETER(OldBase);
UNREFERENCED_PARAMETER(NewBase);
//
// Determine how many variable MTRRs are supported and
// allocate enough storage for all of them
//
mtrrCapabilities.QuadPart = RDMSR(MTRR_MSR_CAPABILITIES);
mtrrCount = (ULONG)mtrrCapabilities.Vcnt;
allocationSize = sizeof(*baseArray) * mtrrCount * 2;
baseArray = _alloca(allocationSize);
maskArray = (PMTRR_VARIABLE_MASK)(baseArray + mtrrCount);
RtlZeroMemory(baseArray,allocationSize);
//
// Read the variable MTRRSs. At the same time, look for the
// MTRR register that contains the old region, and a free
// one as well.
//
for (index = 0; index < mtrrCount; index += 1) {
baseArray[index].QuadPart = RDMSR(MTRR_MSR_VARIABLE_BASE + index * 2);
maskArray[index].QuadPart = RDMSR(MTRR_MSR_VARIABLE_MASK + index * 2);
}
//
// For now just clear the mask bits in MTRR register 0. This expands the
// first MTRR region so that it covers all memory.
//
maskArray[0].Mask = 0;
//
// Now reprogram the modified MTRR table
//
for (index = 0; index < mtrrCount; index += 1) {
WRMSR(MTRR_MSR_VARIABLE_BASE + index * 2,baseArray[index].QuadPart);
WRMSR(MTRR_MSR_VARIABLE_MASK + index * 2,maskArray[index].QuadPart);
}
return TRUE;
}
BOOLEAN
BlAmd64UpdateAcpiConfigurationEntry (
ULONG NewPhysical
)
/*++
Routine Description:
NTDETECT located the physical pointer to the ACPI RSDT table and passed it
up as a configuration node.
This routine finds that configuration node and replaces the physical address
therein with a new address.
This routine would be called after relocating the ACPI tables.
Arguments:
NewPhysical - Supplies the new physical address of the relocated ACPI tables.
Return Value:
TRUE if the relocation was performed, FALSE otherwise.
--*/
{
PCONFIGURATION_COMPONENT_DATA component;
PCONFIGURATION_COMPONENT_DATA resume;
PCM_PARTIAL_RESOURCE_LIST prl;
PCM_PARTIAL_RESOURCE_DESCRIPTOR prd;
PACPI_BIOS_MULTI_NODE rsdp;
resume = NULL;
while (TRUE) {
component = KeFindConfigurationNextEntry( BlLoaderBlock->ConfigurationRoot,
AdapterClass,
MultiFunctionAdapter,
NULL,
&resume );
if (component == NULL) {
return FALSE;
}
if (strcmp(component->ComponentEntry.Identifier,"ACPI BIOS") == 0) {
break;
}
resume = component;
}
prl = (PCM_PARTIAL_RESOURCE_LIST)component->ConfigurationData;
prd = prl->PartialDescriptors;
rsdp = (PACPI_BIOS_MULTI_NODE)(prd + 1);
rsdp->RsdtAddress.QuadPart = NewPhysical;
return TRUE;
}
BOOLEAN
BlAmd64RelocateAcpi (
ULONG Node0Base,
ULONG Node0Limit,
ULONG Node1Base,
ULONG Node1Limit
)
/*++
Routine Description:
This routine looks for ACPI tables within node 1's physical memory and,
if found, relocates them to node 0 memory.
Arguments:
Node0Base - Supplies the lowest PFN of node 0 memory
Node0Limit - Supplies the highest PFN of node 0 memory
Node1Base - Supplies the lowest PFN of node 1 memory (before relocation)
Node1Limit - Supplies the highest PFN of node 1 memory (before relocation)
Return Value:
Returns TRUE if successful, FALSE if a problem was encountered.
--*/
{
ULONG oldRsdtPhysical;
ULONG oldRsdtPhysicalPage;
ULONG newBasePage;
ULONG descriptorSize;
PLIST_ENTRY listHead;
PLIST_ENTRY listEntry;
PMEMORY_ALLOCATION_DESCRIPTOR oldAcpiDescriptor;
PMEMORY_ALLOCATION_DESCRIPTOR newAcpiDescriptor;
PMEMORY_ALLOCATION_DESCRIPTOR descriptor;
PCHAR oldAcpiVa;
PCHAR newAcpiVa;
PHYSICAL_ADDRESS physicalAddress;
ULONG vaBias;
PDESCRIPTION_HEADER descriptionHeader;
ULONG physAddr;
PFADT fadt;
ARC_STATUS status;
ULONG index;
ULONG rsdtPhysical;
ULONG rsdtLength;
//
// Add physicalBias to an ACPI physical pointer to relocate it
//
ULONG physicalBias;
oldRsdtPhysical = BlRsdp->RsdtAddress;
oldRsdtPhysicalPage = oldRsdtPhysical >> PAGE_SHIFT;
//
// Determine whether the descriptor resides in node 1's physical memory.
// If it does not then it does not need to be relocated.
//
if (oldRsdtPhysicalPage < Node1Base ||
oldRsdtPhysicalPage > Node1Limit) {
return TRUE;
}
//
// Find the descriptor that contains the ACPI tables
//
oldAcpiDescriptor = BlFindMemoryDescriptor( oldRsdtPhysicalPage );
//
// Find a descriptor in node 0 memory that is suitable for
// allocating the new ACPI tables from
//
listHead = &BlLoaderBlock->MemoryDescriptorListHead;
listEntry = listHead->Blink;
while (TRUE) {
descriptor = CONTAINING_RECORD(listEntry,
MEMORY_ALLOCATION_DESCRIPTOR,
ListEntry);
if ((descriptor->MemoryType == LoaderReserve) &&
(descriptor->BasePage > Node0Base) &&
((descriptor->BasePage + oldAcpiDescriptor->PageCount) < Node0Limit)) {
break;
}
listEntry = listEntry->Blink;
if (listEntry == listHead) {
return FALSE;
}
}
//
// Carve out the new ACPI descriptor
//
newBasePage = Node0Limit - oldAcpiDescriptor->PageCount + 1;
if ((newBasePage + oldAcpiDescriptor->PageCount) >
(descriptor->BasePage + descriptor->PageCount)) {
newBasePage = descriptor->BasePage +
descriptor->PageCount -
oldAcpiDescriptor->PageCount;
}
status = BlGenerateDescriptor( descriptor,
LoaderSpecialMemory,
newBasePage,
oldAcpiDescriptor->PageCount );
ASSERT( status == ESUCCESS );
newAcpiDescriptor = BlFindMemoryDescriptor( newBasePage );
ASSERT( newAcpiDescriptor != NULL );
//
// Unmap the old RSDT
//
MmUnmapIoSpace( BlRsdt, BlRsdt->Header.Length );
//
// Map both descriptors, copy data from new to old, then unmap
// and free the old descriptor.
//
descriptorSize = oldAcpiDescriptor->PageCount << PAGE_SHIFT;
physicalAddress.QuadPart = oldAcpiDescriptor->BasePage << PAGE_SHIFT;
oldAcpiVa = MmMapIoSpace (physicalAddress, descriptorSize, MmCached);
physicalAddress.QuadPart = newAcpiDescriptor->BasePage << PAGE_SHIFT;
newAcpiVa = MmMapIoSpace (physicalAddress, descriptorSize, MmCached);
RtlCopyMemory( newAcpiVa, oldAcpiVa, descriptorSize );
MmUnmapIoSpace( oldAcpiVa, descriptorSize );
oldAcpiDescriptor->MemoryType = LoaderReserve;
//
// Now thunk the new ACPI tables.
//
physicalBias = (newAcpiDescriptor->BasePage - oldAcpiDescriptor->BasePage) << PAGE_SHIFT;
vaBias = (ULONG)newAcpiVa - (newAcpiDescriptor->BasePage << PAGE_SHIFT);
#define PHYS_TO_VA(p) (PVOID)(p + vaBias)
rsdtPhysical = BlRsdp->RsdtAddress + physicalBias;
BlRsdp->RsdtAddress = rsdtPhysical;
ASSERT(BlXsdt == NULL);
BlRsdt = (PRSDT)PHYS_TO_VA(rsdtPhysical);
//
// Thunk the phys mem pointer array at the end of the RSDT
//
for (index = 0; index < NumTableEntriesFromRSDTPointer(BlRsdt); index += 1) {
physAddr = BlRsdt->Tables[index];
physAddr += physicalBias;
BlRsdt->Tables[index] = physAddr;
//
// Look for tables that themselves have physical pointers that require thunking
//
descriptionHeader = (PDESCRIPTION_HEADER)(PHYS_TO_VA(physAddr));
if (descriptionHeader->Signature == FADT_SIGNATURE) {
fadt = (PFADT)descriptionHeader;
fadt->facs += physicalBias;
fadt->dsdt += physicalBias;
}
}
//
// Now unmap the ACPI tables and remap just the RSDT table
//
rsdtLength = BlRsdt->Header.Length;
MmUnmapIoSpace( newAcpiVa, descriptorSize );
physicalAddress.QuadPart = rsdtPhysical;
BlRsdt = MmMapIoSpace( physicalAddress, rsdtLength, MmCached );
//
// Find the ACPI BIOS configuration entry and update it with the new
// RSDT physical address
//
BlAmd64UpdateAcpiConfigurationEntry( rsdtPhysical );
//
// That's it.
//
return TRUE;
}
#else // BL_ENABLE_REMAP
BOOLEAN
BlAmd64RemapDram (
IN PCHAR LoaderOptions
)
{
return FALSE;
}
#endif // BL_ENABLE_REMAP