/*++ 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 #include #if defined(ROUND_UP) #undef ROUND_UP #endif #include "cmp.h" #define WANT_BLDRTHNK_FUNCTIONS #define COPYBUF_MALLOC BlAllocateHeap #include #define IMAGE_DEFINITIONS 0 #include // // 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 ); 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 KPCR, 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; // // 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. --*/ { BlAmd64LoaderParameterBlock = PTR_64(BlAmd64LoaderBlock64); BlAmd64KernelEntry = PTR_64(SystemEntry); DbgPrint("BlAmd64TransferToKernel():\n" " BlAmd64LoaderParameterBlock = 0x%08x%08x\n" " BlAmd64KernelEntry = 0x%08x%08x\n", (ULONG)(BlAmd64LoaderParameterBlock >> 32), (ULONG)BlAmd64LoaderParameterBlock, (ULONG)(BlAmd64KernelEntry >> 32), (ULONG)BlAmd64KernelEntry ); 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: KPCR etc. // status = BlAmd64PrepareSystemStructures(); 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; // // 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)); 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 ); 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 componentData32; 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; // // 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 resourceListSize64; 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) + sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * (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; } // // 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_LIST_ENTRY_64 driverListEntry64; 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. 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: KPCR GDT IDT KTSS64 EPROCESS ETHREAD Idle thread stack DPC 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(FIELD_OFFSET(KTSS64_64,IoMap)); 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(FIELD_OFFSET(KTSS64_64,IoMap)); // // 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); } } } }