1348 lines
36 KiB
C
1348 lines
36 KiB
C
/*++
|
||
|
||
Copyright (c) 1992 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
initx86.c
|
||
|
||
Abstract:
|
||
|
||
Does any x86-specific initialization, then starts the common ARC osloader
|
||
|
||
Author:
|
||
|
||
John Vert (jvert) 4-Nov-1993
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
#include "bldrx86.h"
|
||
#include "acpitabl.h"
|
||
#include "msg.h"
|
||
#include "bootstatus.h"
|
||
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
#include <stdio.h>
|
||
#include <netboot.h>
|
||
#include <ramdisk.h>
|
||
|
||
BOOLEAN
|
||
BlpPaeSupported(
|
||
VOID
|
||
);
|
||
|
||
BOOLEAN
|
||
BlpChipsetPaeSupported(
|
||
VOID
|
||
);
|
||
|
||
ARC_STATUS
|
||
Blx86GetImageProperties(
|
||
IN ULONG LoadDeviceId,
|
||
IN PCHAR ImagePath,
|
||
OUT PBOOLEAN IsPae,
|
||
OUT PBOOLEAN SupportsHotPlugMemory
|
||
);
|
||
|
||
BOOLEAN
|
||
Blx86IsKernelCompatible(
|
||
IN ULONG LoadDeviceId,
|
||
IN PCHAR ImagePath,
|
||
IN BOOLEAN ProcessorSupportsPae,
|
||
IN OUT PBOOLEAN UsePae
|
||
);
|
||
|
||
BOOLEAN
|
||
Blx86NeedPaeForHotPlugMemory(
|
||
VOID
|
||
);
|
||
|
||
UCHAR BootPartitionName[80];
|
||
UCHAR KernelBootDevice[80];
|
||
UCHAR OsLoadFilename[100];
|
||
UCHAR OsLoaderFilename[100];
|
||
UCHAR SystemPartition[100];
|
||
UCHAR OsLoadPartition[100];
|
||
UCHAR OsLoadOptions[100];
|
||
UCHAR ConsoleInputName[50];
|
||
UCHAR MyBuffer[SECTOR_SIZE+32];
|
||
UCHAR ConsoleOutputName[50];
|
||
UCHAR X86SystemPartition[sizeof("x86systempartition=") + sizeof(BootPartitionName)];
|
||
|
||
extern BOOLEAN ForceLastKnownGood;
|
||
extern ULONG BlHighestPage;
|
||
extern PHARDWARE_PTE PDE;
|
||
|
||
extern ULONG BootFlags;
|
||
|
||
extern PDESCRIPTION_HEADER
|
||
BlFindACPITable(
|
||
IN PCHAR TableName,
|
||
IN ULONG TableLength
|
||
);
|
||
|
||
|
||
|
||
VOID
|
||
BlStartup(
|
||
IN PCHAR PartitionName
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Does x86-specific initialization, particularly presenting the boot.ini
|
||
menu and running NTDETECT, then calls to the common osloader.
|
||
|
||
Arguments:
|
||
|
||
PartitionName - Supplies the ARC name of the partition (or floppy) that
|
||
setupldr was loaded from.
|
||
|
||
Return Value:
|
||
|
||
Does not return
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG Argc = 0;
|
||
PUCHAR Argv[10];
|
||
ARC_STATUS Status;
|
||
ULONG BootFileId;
|
||
PCHAR BootFile = NULL;
|
||
ULONG Read;
|
||
PCHAR p,q;
|
||
ULONG i;
|
||
ULONG DriveId;
|
||
ULONG FileSize;
|
||
ULONG Count;
|
||
LARGE_INTEGER SeekPosition;
|
||
PCHAR LoadOptions = NULL;
|
||
BOOLEAN UseTimeOut=TRUE;
|
||
BOOLEAN AlreadyInitialized = FALSE;
|
||
extern BOOLEAN FwDescriptorsValid;
|
||
PCHAR BadLinkName = NULL;
|
||
|
||
//
|
||
// If this is an SDI boot, initialize the ramdisk "driver" now.
|
||
//
|
||
// NB. PartitionName is the name of the device from which the loader
|
||
// was loaded. It is NOT the name of the device from which the OS will
|
||
// be loaded. If we're doing a straight ramdisk boot, this will NOT be
|
||
// ramdisk(0) -- it will be net(0) or a physical disk name. Only if
|
||
// we're doing a real SDI boot will this be ramdisk(0). (See
|
||
// NtProcessStartup() in boot\lib\i386\entry.c.)
|
||
//
|
||
|
||
if ( strcmp(PartitionName,"ramdisk(0)") == 0 ) {
|
||
RamdiskInitialize( NULL, TRUE );
|
||
}
|
||
|
||
//
|
||
// Open the boot partition so we can load boot drivers off it.
|
||
//
|
||
Status = ArcOpen(PartitionName, ArcOpenReadWrite, &DriveId);
|
||
if (Status != ESUCCESS) {
|
||
BlPrint(TEXT("Couldn't open drive %s\n"),PartitionName);
|
||
BlPrint(BlFindMessage(BL_DRIVE_ERROR),PartitionName);
|
||
goto BootFailed;
|
||
}
|
||
|
||
//
|
||
// Initialize dbcs font and display support.
|
||
//
|
||
TextGrInitialize(DriveId, NULL);
|
||
|
||
//
|
||
// Initialize ARC StdIo functionality
|
||
//
|
||
|
||
strcpy(ConsoleInputName,"consolein=multi(0)key(0)keyboard(0)");
|
||
strcpy(ConsoleOutputName,"consoleout=multi(0)video(0)monitor(0)");
|
||
Argv[0]=ConsoleInputName;
|
||
Argv[1]=ConsoleOutputName;
|
||
BlInitStdio (2, Argv);
|
||
|
||
//
|
||
// Announce the loader
|
||
//
|
||
//BlPrint(OsLoaderVersion);
|
||
|
||
//
|
||
// Check ntldr partition for hiberation image
|
||
//
|
||
|
||
BlHiberRestore(DriveId, &BadLinkName);
|
||
|
||
//
|
||
// Re-open the boot partition as a temporary work around
|
||
// for NTFS caching bug.
|
||
//
|
||
ArcClose(DriveId);
|
||
|
||
Status = ArcOpen(PartitionName, ArcOpenReadWrite, &DriveId);
|
||
|
||
if (Status != ESUCCESS) {
|
||
BlPrint(TEXT("Couldn't open drive %s\n"),PartitionName);
|
||
BlPrint(BlFindMessage(BL_DRIVE_ERROR),PartitionName);
|
||
goto BootFailed;
|
||
}
|
||
|
||
//
|
||
// It is possible that the link file points to the hiber file on a scsi
|
||
// disk. In that case, we need to load NTBOOTDD.SYS in order to access the
|
||
// hiber file and try again.
|
||
//
|
||
if ((BadLinkName != NULL) &&
|
||
((_strnicmp(BadLinkName,"scsi(",5)==0) || (_strnicmp(BadLinkName,"signature(",10)==0))) {
|
||
ULONG HiberDrive;
|
||
|
||
//
|
||
// Before loading NTBOOTDD we must load NTDETECT as that figures
|
||
// out the PCI buses
|
||
//
|
||
if (BlDetectHardware(DriveId, "/fastdetect")) {
|
||
if (AEInitializeIo(DriveId) == ESUCCESS) {
|
||
AlreadyInitialized = TRUE;
|
||
|
||
//
|
||
// Now that NTBOOTDD.SYS is loaded, we can try again.
|
||
//
|
||
Status = ArcOpen(BadLinkName, ArcOpenReadWrite, &HiberDrive);
|
||
if (Status == ESUCCESS) {
|
||
BlHiberRestore(HiberDrive, NULL);
|
||
ArcClose(HiberDrive);
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
do {
|
||
|
||
if (BlBootingFromNet) {
|
||
PCHAR BootIniPath;
|
||
|
||
//
|
||
// If we are booting from the network and
|
||
// NetBootIniContents has been specified, we
|
||
// will just its contents for boot.ini
|
||
//
|
||
if (NetBootIniContents[0] != '\0') {
|
||
BootFile = NetBootIniContents;
|
||
} else {
|
||
//
|
||
// NetBootIniContents is empty, therefore
|
||
// we need to open the boot.ini file from the
|
||
// network. The acutal file to open is either
|
||
// specificied in NetBootIniPath or is the
|
||
// default of NetBootPath\boot.ini
|
||
//
|
||
if (NetBootIniPath[0] != '\0') {
|
||
BootIniPath = NetBootIniPath;
|
||
} else {
|
||
strcpy(MyBuffer, NetBootPath);
|
||
strcat(MyBuffer, "boot.ini");
|
||
BootIniPath = MyBuffer;
|
||
}
|
||
Status = BlOpen( DriveId,
|
||
BootIniPath,
|
||
ArcOpenReadOnly,
|
||
&BootFileId );
|
||
}
|
||
|
||
} else {
|
||
Status = BlOpen( DriveId,
|
||
"\\boot.ini",
|
||
ArcOpenReadOnly,
|
||
&BootFileId );
|
||
}
|
||
|
||
if (BootFile == NULL) {
|
||
|
||
BootFile = MyBuffer;
|
||
RtlZeroMemory(MyBuffer, SECTOR_SIZE+32);
|
||
|
||
if (Status != ESUCCESS) {
|
||
BootFile[0]='\0';
|
||
} else {
|
||
//
|
||
// Determine the length of the boot.ini file by reading to the end of
|
||
// file.
|
||
//
|
||
|
||
FileSize = 0;
|
||
do {
|
||
Status = BlRead(BootFileId, BootFile, SECTOR_SIZE, &Count);
|
||
if (Status != ESUCCESS) {
|
||
BlClose(BootFileId);
|
||
BlPrint(BlFindMessage(BL_READ_ERROR),Status);
|
||
BootFile[0] = '\0';
|
||
FileSize = 0;
|
||
Count = 0;
|
||
goto BootFailed;
|
||
}
|
||
|
||
FileSize += Count;
|
||
} while (Count != 0);
|
||
|
||
if (FileSize >= SECTOR_SIZE) {
|
||
|
||
//
|
||
// We need to allocate a bigger buffer, since the boot.ini file
|
||
// is bigger than one sector.
|
||
//
|
||
|
||
BootFile=FwAllocateHeap(FileSize);
|
||
}
|
||
|
||
if (BootFile == NULL) {
|
||
BlPrint(BlFindMessage(BL_READ_ERROR),ENOMEM);
|
||
BootFile = MyBuffer;
|
||
BootFile[0] = '\0';
|
||
goto BootFailed;
|
||
} else {
|
||
|
||
SeekPosition.QuadPart = 0;
|
||
Status = BlSeek(BootFileId,
|
||
&SeekPosition,
|
||
SeekAbsolute);
|
||
if (Status != ESUCCESS) {
|
||
BlPrint(BlFindMessage(BL_READ_ERROR),Status);
|
||
BootFile = MyBuffer;
|
||
BootFile[0] = '\0';
|
||
goto BootFailed;
|
||
} else {
|
||
Status = BlRead( BootFileId,
|
||
BootFile,
|
||
FileSize,
|
||
&Read );
|
||
|
||
SeekPosition.QuadPart = 0;
|
||
Status = BlSeek(BootFileId,
|
||
&SeekPosition,
|
||
SeekAbsolute);
|
||
if (Status != ESUCCESS) {
|
||
BlPrint(BlFindMessage(BL_READ_ERROR),Status);
|
||
BootFile = MyBuffer;
|
||
BootFile[0] = '\0';
|
||
goto BootFailed;
|
||
} else {
|
||
BootFile[Read]='\0';
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Find Ctrl-Z, if it exists
|
||
//
|
||
|
||
p=BootFile;
|
||
while ((*p!='\0') && (*p!=26)) {
|
||
++p;
|
||
}
|
||
if (*p != '\0') {
|
||
*p='\0';
|
||
}
|
||
BlClose(BootFileId);
|
||
}
|
||
}
|
||
|
||
MdShutoffFloppy();
|
||
|
||
ARC_DISPLAY_CLEAR();
|
||
|
||
// We require a boot.ini file when booting from the network
|
||
if (BlBootingFromNet && *BootFile == '\0') {
|
||
BlPrint(BlFindMessage(BL_READ_ERROR),Status);
|
||
goto BootFailed;
|
||
}
|
||
|
||
p=BlSelectKernel(DriveId,BootFile, &LoadOptions, UseTimeOut);
|
||
if ( p == NULL ) {
|
||
BlPrint(BlFindMessage(BL_INVALID_BOOT_INI_FATAL));
|
||
goto BootFailed;
|
||
}
|
||
|
||
#if defined(REMOTE_BOOT)
|
||
//
|
||
// We may have loaded boot.ini from the hard drive but if the selection was
|
||
// for a remote boot installation then we need to load the rest from the net.
|
||
//
|
||
|
||
if((DriveId != NET_DEVICE_ID) &&
|
||
(!_strnicmp(p,"net(",4))) {
|
||
BlPrint("\nWarning:Booting from CSC without access to server\n");
|
||
strcpy(BootPartitionName,"net(0)");
|
||
BlBootingFromNet = TRUE;
|
||
NetworkBootRom = FALSE;
|
||
|
||
//
|
||
// p points to something like: "net(0)\COLINW3\IMirror\Clients\cwintel\BootDrive\WINNT"
|
||
// NetBootPath needs to contain "\Clients\cwintel\"
|
||
// ServerShare is used inside lib\netboot.c to find the correct file if CSC
|
||
// is used.
|
||
//
|
||
|
||
q = strchr(p+sizeof("net(0)"), '\\');
|
||
q = strchr(q+1, '\\');
|
||
strcpy(NetBootPath,q);
|
||
q = strrchr( NetBootPath, '\\' );
|
||
q[1] = '\0';
|
||
}
|
||
#endif // defined(REMOTE_BOOT)
|
||
|
||
if (!AlreadyInitialized) {
|
||
|
||
// BlPrint(BlFindMessage(BL_NTDETECT_MSG));
|
||
if (!BlDetectHardware(DriveId, LoadOptions)) {
|
||
BlPrint(BlFindMessage(BL_NTDETECT_FAILURE));
|
||
return;
|
||
}
|
||
|
||
ARC_DISPLAY_CLEAR();
|
||
|
||
//
|
||
// Initialize SCSI boot driver, if necessary.
|
||
//
|
||
if (_strnicmp(p,"scsi(",5)==0 || _strnicmp(p,"signature(",10)==0) {
|
||
AEInitializeIo(DriveId);
|
||
}
|
||
|
||
ArcClose(DriveId);
|
||
//
|
||
// Indicate that fw memory descriptors cannot be changed from
|
||
// now on.
|
||
//
|
||
FwDescriptorsValid = FALSE;
|
||
} else {
|
||
ARC_DISPLAY_CLEAR();
|
||
}
|
||
|
||
//
|
||
// Set AlreadyInitialized Flag to TRUE to indicate that ntdetect
|
||
// routines have been run.
|
||
//
|
||
|
||
AlreadyInitialized = TRUE;
|
||
|
||
//
|
||
// Only time out the boot menu the first time through the boot.
|
||
// For all subsequent reboots, the menu will stay up.
|
||
//
|
||
UseTimeOut=FALSE;
|
||
|
||
i=0;
|
||
while (*p !='\\') {
|
||
KernelBootDevice[i] = *p;
|
||
i++;
|
||
p++;
|
||
}
|
||
KernelBootDevice[i] = '\0';
|
||
|
||
//
|
||
// If the user hasn't chosen an advanced boot mode then we will present
|
||
// them with the menu and select our default.
|
||
//
|
||
|
||
if(BlGetAdvancedBootOption() == -1) {
|
||
PVOID dataHandle;
|
||
ULONG abmDefault;
|
||
BSD_LAST_BOOT_STATUS LastBootStatus = BsdLastBootGood;
|
||
|
||
//
|
||
// Open the boot status data.
|
||
//
|
||
|
||
Status = BlLockBootStatusData(0, KernelBootDevice, p, &dataHandle);
|
||
|
||
if(Status == ESUCCESS) {
|
||
|
||
//
|
||
// Check the status of the last boot. This will return both the
|
||
// status and the advanced boot mode we should enter (based on the
|
||
// status and the "enable auto advanced boot" flag.
|
||
//
|
||
|
||
abmDefault = BlGetLastBootStatus(dataHandle, &LastBootStatus);
|
||
|
||
if(LastBootStatus == BsdLastBootFailed) {
|
||
|
||
//
|
||
// If we should attempt an ABM mode then present the user with
|
||
// the menu.
|
||
//
|
||
|
||
if(abmDefault != -1) {
|
||
ULONG menuTitle;
|
||
UCHAR timeout;
|
||
|
||
if(LastBootStatus == BsdLastBootFailed) {
|
||
menuTitle = BL_ADVANCEDBOOT_AUTOLKG_TITLE;
|
||
} else if(LastBootStatus == BsdLastBootNotShutdown) {
|
||
menuTitle = BL_ADVANCEDBOOT_AUTOSAFE_TITLE;
|
||
}
|
||
|
||
//
|
||
// Read the timeout value.
|
||
//
|
||
|
||
Status = BlGetSetBootStatusData(dataHandle,
|
||
TRUE,
|
||
RtlBsdItemAabTimeout,
|
||
&timeout,
|
||
sizeof(UCHAR),
|
||
NULL);
|
||
|
||
if(Status != ESUCCESS) {
|
||
timeout = 30;
|
||
}
|
||
|
||
abmDefault = BlDoAdvancedBoot(menuTitle,
|
||
-1,
|
||
TRUE,
|
||
timeout);
|
||
}
|
||
|
||
BlAutoAdvancedBoot(&LoadOptions,
|
||
LastBootStatus,
|
||
abmDefault);
|
||
}
|
||
|
||
BlUnlockBootStatusData(dataHandle);
|
||
}
|
||
}
|
||
|
||
//
|
||
// We are fooling the OS Loader here. It only uses the osloader= variable
|
||
// to determine where to load HAL.DLL from. Since x86 systems have no
|
||
// "system partition" we want to load HAL.DLL from \nt\system\HAL.DLL.
|
||
// So we pass that it as the osloader path.
|
||
//
|
||
|
||
strcpy(OsLoaderFilename,"osloader=");
|
||
strcat(OsLoaderFilename,p);
|
||
strcat(OsLoaderFilename,"\\System32\\NTLDR");
|
||
|
||
strcpy(SystemPartition,"systempartition=");
|
||
strcat(SystemPartition,KernelBootDevice);
|
||
|
||
strcpy(OsLoadPartition,"osloadpartition=");
|
||
strcat(OsLoadPartition,KernelBootDevice);
|
||
|
||
strcpy(OsLoadFilename, "osloadfilename=");
|
||
strcat(OsLoadFilename, p);
|
||
|
||
strcpy(OsLoadOptions,"osloadoptions=");
|
||
if (LoadOptions) {
|
||
strcat(OsLoadOptions,LoadOptions);
|
||
}
|
||
|
||
strcpy(ConsoleInputName,"consolein=multi(0)key(0)keyboard(0)");
|
||
|
||
strcpy(ConsoleOutputName,"consoleout=multi(0)video(0)monitor(0)");
|
||
|
||
strcpy(X86SystemPartition,"x86systempartition=");
|
||
strcat(X86SystemPartition,PartitionName);
|
||
|
||
Argv[Argc++]="load";
|
||
|
||
Argv[Argc++]=OsLoaderFilename;
|
||
Argv[Argc++]=SystemPartition;
|
||
Argv[Argc++]=OsLoadFilename;
|
||
Argv[Argc++]=OsLoadPartition;
|
||
Argv[Argc++]=OsLoadOptions;
|
||
Argv[Argc++]=X86SystemPartition;
|
||
|
||
Status = BlOsLoader( Argc, Argv, NULL );
|
||
|
||
BootFailed:
|
||
ForceLastKnownGood = FALSE;
|
||
if (Status != ESUCCESS) {
|
||
|
||
#if defined(ENABLE_LOADER_DEBUG)
|
||
DbgBreakPoint();
|
||
#endif
|
||
|
||
if (BootFlags & BOOTFLAG_REBOOT_ON_FAILURE) {
|
||
ULONG StartTime = ArcGetRelativeTime();
|
||
BlPrint(TEXT("\nRebooting in 5 seconds...\n"));
|
||
while ( ArcGetRelativeTime() - StartTime < 5) {}
|
||
ArcRestart();
|
||
}
|
||
|
||
//
|
||
// Boot failed, wait for reboot
|
||
//
|
||
while (TRUE) {
|
||
while (!BlGetKey()); // BOOT FAILED!
|
||
if (BlTerminalHandleLoaderFailure()) {
|
||
ArcRestart();
|
||
}
|
||
}
|
||
|
||
} else {
|
||
//
|
||
// Need to reopen the drive
|
||
//
|
||
Status = ArcOpen(BootPartitionName, ArcOpenReadOnly, &DriveId);
|
||
if (Status != ESUCCESS) {
|
||
BlPrint(BlFindMessage(BL_DRIVE_ERROR),BootPartitionName);
|
||
goto BootFailed;
|
||
}
|
||
}
|
||
} while (TRUE);
|
||
|
||
}
|
||
|
||
VOID
|
||
DoApmAttemptReconnect(
|
||
VOID
|
||
)
|
||
{
|
||
APM_ATTEMPT_RECONNECT();
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
Blx86NeedPaeForHotPlugMemory(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Determine whether any hot plug memory described in the SRAT table
|
||
extends beyond the 4gb mark and thus implies a need for PAE.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
TRUE: Machine supports hot plug memory beyond the 4gb mark, PAE should be
|
||
turned on if possible.
|
||
|
||
FALSE: Machine doesn't support hot plug memory beyond the 4gb mark.
|
||
|
||
--*/
|
||
{
|
||
PACPI_SRAT SratTable;
|
||
PACPI_SRAT_ENTRY SratEntry;
|
||
PACPI_SRAT_ENTRY SratEnd;
|
||
|
||
SratTable = (PACPI_SRAT) BlFindACPITable("SRAT", sizeof(ACPI_SRAT));
|
||
if (SratTable == NULL) {
|
||
return FALSE;
|
||
}
|
||
|
||
SratTable = (PACPI_SRAT) BlFindACPITable("SRAT", SratTable->Header.Length);
|
||
if (SratTable == NULL) {
|
||
return FALSE;
|
||
}
|
||
|
||
SratEnd = (PACPI_SRAT_ENTRY)(((PUCHAR)SratTable) +
|
||
SratTable->Header.Length);
|
||
for (SratEntry = (PACPI_SRAT_ENTRY)(SratTable + 1);
|
||
SratEntry < SratEnd;
|
||
SratEntry = (PACPI_SRAT_ENTRY)(((PUCHAR) SratEntry) + SratEntry->Length)) {
|
||
if (SratEntry->Type != SratMemory) {
|
||
continue;
|
||
}
|
||
|
||
if (SratEntry->MemoryAffinity.Flags.HotPlug &&
|
||
SratEntry->MemoryAffinity.Flags.Enabled) {
|
||
ULONGLONG Extent;
|
||
|
||
//
|
||
// Check if hot plug region ends beyond the 4gb mark.
|
||
//
|
||
|
||
Extent = SratEntry->MemoryAffinity.Base.QuadPart +
|
||
SratEntry->MemoryAffinity.Length;
|
||
if (Extent > 0x100000000) {
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
ARC_STATUS
|
||
Blx86CheckForPaeKernel(
|
||
IN BOOLEAN UserSpecifiedPae,
|
||
IN BOOLEAN UserSpecifiedNoPae,
|
||
IN PCHAR UserSpecifiedKernelImage,
|
||
IN PCHAR HalImagePath,
|
||
IN ULONG LoadDeviceId,
|
||
IN ULONG SystemDeviceId,
|
||
OUT PULONG HighestSystemPage,
|
||
OUT PBOOLEAN UsePaeMode,
|
||
IN OUT PCHAR KernelPath
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
There are two kernels: one, ntkrnlpa.exe, is compiled for PAE mode.
|
||
The other, ntoskrnl.exe, is not.
|
||
|
||
This routine is responsible for deciding which one to load.
|
||
|
||
Arguments:
|
||
|
||
UserSpecifiedPae - Indicates whether the user requested PAE mode
|
||
via the /PAE loader switch.
|
||
|
||
UserSpecifiedNoPae - Indicates whether the user requrested non-PAE mode
|
||
via the /NOPAE loader switch.
|
||
|
||
UserSpecifiedKernelImage - Points to the user-specified kernel image name,
|
||
indicated via the /KERNEL= switch, or NULL if none was specified.
|
||
|
||
HalImagePath - Points to the hal image that will be used.
|
||
|
||
LoadDeviceId - The ARC device handle of the kernel load device.
|
||
|
||
SystemDeviceId - The ARC device handle of the system device.
|
||
|
||
HighestSystemPage - Out parameter returning the highest physical
|
||
page found in the system.
|
||
|
||
UsePaeMode - Indicates whether the kernel should be loaded in PAE mode.
|
||
|
||
KernelPath - On input, the directory path of the kernel images. Upon
|
||
successful return, this will contain the full kernel image path.
|
||
|
||
Return Value:
|
||
|
||
ESUCCESS : A compatible kernel has been located, and its path resides in
|
||
the output buffer pointed to by KernelPath.
|
||
|
||
EINVAL: A compatible kernel could not be located. This will be a fatal
|
||
condition.
|
||
|
||
EBADF: hal is corrupt or missing. This will be a fatal condition.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN foundMemoryAbove4G;
|
||
BOOLEAN usePae;
|
||
BOOLEAN processorSupportsPae;
|
||
BOOLEAN halSupportsPae;
|
||
BOOLEAN osSupportsHotPlugMemory;
|
||
BOOLEAN havePaeKernel;
|
||
BOOLEAN compatibleKernel;
|
||
ULONG lastPage;
|
||
PLIST_ENTRY link;
|
||
PLIST_ENTRY listHead;
|
||
PMEMORY_ALLOCATION_DESCRIPTOR descriptor;
|
||
PCHAR kernelImageNamePae;
|
||
PCHAR kernelImageNameNoPae;
|
||
PCHAR kernelImageName;
|
||
PCHAR kernelImageNameTarget;
|
||
ARC_STATUS status;
|
||
ULONG highestSystemPage;
|
||
ULONG pagesAbove4Gig;
|
||
|
||
kernelImageNameNoPae = "ntoskrnl.exe";
|
||
kernelImageNamePae = "ntkrnlpa.exe";
|
||
kernelImageNameTarget = KernelPath + strlen( KernelPath );
|
||
|
||
//
|
||
// Determine the highest physical page. Also, count the number of pages
|
||
// at or above the 4G mark.
|
||
//
|
||
|
||
highestSystemPage = 0;
|
||
pagesAbove4Gig = 0;
|
||
listHead = &BlLoaderBlock->MemoryDescriptorListHead;
|
||
link = listHead->Flink;
|
||
while (link != listHead) {
|
||
|
||
descriptor = CONTAINING_RECORD(link,
|
||
MEMORY_ALLOCATION_DESCRIPTOR,
|
||
ListEntry);
|
||
|
||
lastPage = descriptor->BasePage + descriptor->PageCount - 1;
|
||
if (lastPage > highestSystemPage) {
|
||
|
||
//
|
||
// We have a new highest system page, record it.
|
||
//
|
||
|
||
highestSystemPage = lastPage;
|
||
}
|
||
|
||
if (lastPage >= (1024 * 1024)) {
|
||
|
||
//
|
||
// This descriptor includes one or more pages at or above
|
||
// the 4G mark.
|
||
//
|
||
|
||
if (descriptor->BasePage >= (1024 * 1024)) {
|
||
|
||
//
|
||
// All of the pages in this descriptor lie at or above 4G.
|
||
//
|
||
|
||
pagesAbove4Gig += descriptor->PageCount;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Only some of the pages in this descriptor lie at or above
|
||
// 4G.
|
||
//
|
||
|
||
pagesAbove4Gig += lastPage - (1024 * 1024) + 1;
|
||
}
|
||
}
|
||
|
||
link = link->Flink;
|
||
}
|
||
*HighestSystemPage = highestSystemPage;
|
||
|
||
//
|
||
// Record whether there is a non-trivial amount of memory above 4G on this
|
||
// machine. Note that most machines with "exactly" 4G of ram actually move
|
||
// a small amount of ram to above the 4G mark.
|
||
//
|
||
// Because running a PAE kernel inflicts a performance hit, we would rather
|
||
// ignore some amount of memory x rather than move into PAE mode to use it.
|
||
//
|
||
// Right now, x is set to 64MB, or 16384 pages.
|
||
//
|
||
|
||
if (pagesAbove4Gig > 16384) {
|
||
foundMemoryAbove4G = TRUE;
|
||
} else {
|
||
foundMemoryAbove4G = FALSE;
|
||
}
|
||
|
||
//
|
||
// Find out if this processor can handle PAE mode.
|
||
//
|
||
|
||
processorSupportsPae = BlpPaeSupported();
|
||
|
||
//
|
||
// Find out whether this chipset supports PAE
|
||
//
|
||
if (!BlpChipsetPaeSupported()) {
|
||
processorSupportsPae = FALSE;
|
||
}
|
||
|
||
//
|
||
// Start out with a pae flag based on whether memory above 4G was located
|
||
// or whether a /PAE switch was passed on the command line.
|
||
//
|
||
|
||
//
|
||
// It used to be the case that we would default to PAE mode if memory
|
||
// above 4G physical was found in the machine. The decision was made
|
||
// to NEVER default to PAE mode, rather to use PAE only when the
|
||
// user specifically asks for it.
|
||
//
|
||
// If we revert back to the previous way of doing things, uncomment the
|
||
// following line and remove the subsequent one.
|
||
//
|
||
// if (foundMemoryAbove4G || UserSpecifiedPae) {
|
||
//
|
||
|
||
if (UserSpecifiedPae) {
|
||
usePae = TRUE;
|
||
} else {
|
||
usePae = FALSE;
|
||
}
|
||
|
||
//
|
||
// Determine whether the HAL image can support PAE mode and
|
||
// whether the underlying OS supports hot plug memory.
|
||
//
|
||
|
||
status = Blx86GetImageProperties( SystemDeviceId,
|
||
HalImagePath,
|
||
&halSupportsPae,
|
||
&osSupportsHotPlugMemory );
|
||
if (status != ESUCCESS) {
|
||
|
||
//
|
||
// Apparently the HAL image supplied is invalid.
|
||
//
|
||
return(EBADF);
|
||
}
|
||
|
||
//
|
||
// If machine has the ability for memory to be hot plugged over
|
||
// the 4gb mark, then this is interpreted as a user request for
|
||
// PAE support. This request will be ignored if not supported by
|
||
// the underlying hardware or operating system.
|
||
//
|
||
|
||
if (osSupportsHotPlugMemory && Blx86NeedPaeForHotPlugMemory()) {
|
||
usePae = TRUE;
|
||
}
|
||
|
||
|
||
if (halSupportsPae == FALSE) {
|
||
|
||
//
|
||
// The HAL cannot support operation in PAE mode. Override
|
||
// processorSupportsPae to FALSE in this case, meaning that we must
|
||
// not under any circumstances try to use PAE mode.
|
||
//
|
||
|
||
processorSupportsPae = FALSE;
|
||
}
|
||
|
||
//
|
||
// If the processor doesn't handle PAE mode or if the user specified
|
||
// a /NOPAE switch on the loader command line, then disable PAE mode.
|
||
//
|
||
|
||
if (processorSupportsPae == FALSE || UserSpecifiedNoPae) {
|
||
|
||
usePae = FALSE;
|
||
}
|
||
|
||
//
|
||
// Choose the image name based on the data accumulated thus far.
|
||
//
|
||
|
||
if (UserSpecifiedKernelImage != NULL) {
|
||
kernelImageName = UserSpecifiedKernelImage;
|
||
} else if (usePae != FALSE) {
|
||
kernelImageName = kernelImageNamePae;
|
||
} else {
|
||
kernelImageName = kernelImageNameNoPae;
|
||
}
|
||
|
||
//
|
||
// Build the path for this kernel and determine its suitability.
|
||
//
|
||
|
||
strcpy( kernelImageNameTarget, kernelImageName );
|
||
compatibleKernel = Blx86IsKernelCompatible( LoadDeviceId,
|
||
KernelPath,
|
||
processorSupportsPae,
|
||
&usePae );
|
||
if (compatibleKernel == FALSE) {
|
||
|
||
//
|
||
// This kernel is not compatible or does not exist. If the failed
|
||
// kernel was user-specified, fall back to the default, non-PAE
|
||
// kernel and see if that is compatible.
|
||
//
|
||
|
||
if (UserSpecifiedKernelImage != NULL) {
|
||
|
||
kernelImageName = kernelImageNameNoPae;
|
||
strcpy( kernelImageNameTarget, kernelImageName );
|
||
compatibleKernel = Blx86IsKernelCompatible( LoadDeviceId,
|
||
KernelPath,
|
||
processorSupportsPae,
|
||
&usePae );
|
||
|
||
}
|
||
}
|
||
|
||
if (compatibleKernel == FALSE) {
|
||
|
||
//
|
||
// At this point we have tried one of the default kernel image names,
|
||
// as well as any user-specified kernel image name. There remains
|
||
// one final default image name that hasn't been tried. Determine
|
||
// which one that is and try it.
|
||
//
|
||
|
||
if (kernelImageName == kernelImageNameNoPae) {
|
||
kernelImageName = kernelImageNamePae;
|
||
} else {
|
||
kernelImageName = kernelImageNameNoPae;
|
||
}
|
||
|
||
strcpy( kernelImageNameTarget, kernelImageName );
|
||
compatibleKernel = Blx86IsKernelCompatible( LoadDeviceId,
|
||
KernelPath,
|
||
processorSupportsPae,
|
||
&usePae );
|
||
}
|
||
|
||
if (compatibleKernel != FALSE) {
|
||
|
||
*UsePaeMode = usePae;
|
||
status = ESUCCESS;
|
||
} else {
|
||
status = EINVAL;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
BOOLEAN
|
||
Blx86IsKernelCompatible(
|
||
IN ULONG LoadDeviceId,
|
||
IN PCHAR ImagePath,
|
||
IN BOOLEAN ProcessorSupportsPae,
|
||
OUT PBOOLEAN UsePae
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine examines the supplied kernel image and determines whether
|
||
it is valid and compatible with the current processor and, if so, whether
|
||
PAE mode should be enabled.
|
||
|
||
Arguments:
|
||
|
||
LoadDeviceId - The ARC device handle of the kernel load device.
|
||
|
||
ImagePath - Pointer to a buffer containing the full path of the
|
||
kernel to check.
|
||
|
||
ProcessorSupportsPae - TRUE if the current processor supports PAE
|
||
mode, FALSE otherwise.
|
||
|
||
UsePae - Upon successful return, indicates whether the kernel is PAE
|
||
enabled.
|
||
|
||
Return Value:
|
||
|
||
TRUE: The supplied kernel image is compatible with the current processor,
|
||
and *UsePae has been updated as appropriate.
|
||
|
||
FALSE: The supplied kernel image is invalid or is not compatible with the
|
||
current processor.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN isPaeKernel;
|
||
BOOLEAN supportsHotPlugMemory;
|
||
ARC_STATUS status;
|
||
|
||
status = Blx86GetImageProperties( LoadDeviceId,
|
||
ImagePath,
|
||
&isPaeKernel,
|
||
&supportsHotPlugMemory );
|
||
if (status != ESUCCESS) {
|
||
|
||
//
|
||
// This kernel is invalid or does not exist. Therefore, it is
|
||
// not compatible.
|
||
//
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
if (isPaeKernel == FALSE) {
|
||
|
||
//
|
||
// This is a non-PAE kernel. All supported processors can run in
|
||
// non-PAE mode. Indicate that PAE mode should not be used and that
|
||
// this kernel is compatible.
|
||
//
|
||
|
||
*UsePae = FALSE;
|
||
return TRUE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// This is a PAE kernel.
|
||
//
|
||
|
||
if (ProcessorSupportsPae == FALSE) {
|
||
|
||
//
|
||
// This is a PAE kernel but the processor will not run in that
|
||
// mode. Indicate that this kernel is not compatible.
|
||
//
|
||
|
||
return FALSE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// This is a PAE kernel and a PAE processor. Indicate that PAE
|
||
// mode should be used and that this kernel is compatible.
|
||
//
|
||
|
||
*UsePae = TRUE;
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
ARC_STATUS
|
||
Blx86GetImageProperties(
|
||
IN ULONG LoadDeviceId,
|
||
IN PCHAR ImagePath,
|
||
OUT PBOOLEAN IsPae,
|
||
OUT PBOOLEAN SupportsHotPlugMemory
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine examines the supplied image and determines whether
|
||
it is valid and, if so, whether it is PAE compatible by examining
|
||
the IMAGE_FILE_LARGE_ADDRESS_AWARE bit.
|
||
|
||
Arguments:
|
||
|
||
LoadDeviceId - The ARC device handle of the image device.
|
||
|
||
ImagePath - Pointer to a buffer containing the full path of the
|
||
kernel to check.
|
||
|
||
IsPae - Upon successful return, indicates whether the image is PAE
|
||
compatible.
|
||
|
||
SupportsHotPlugMemory - Upon successful return, indicates whether the
|
||
image indicates an OS that supports hot plug memory.
|
||
|
||
|
||
Return Value:
|
||
|
||
ESUCCESS - The supplied kernel image is valid, and *IsPae has been updated
|
||
according to the image header.
|
||
|
||
Otherwise, the Arc status of the failed operation is returned.
|
||
|
||
--*/
|
||
|
||
{
|
||
CHAR localBufferSpace[ SECTOR_SIZE * 2 + SECTOR_SIZE - 1 ];
|
||
PCHAR localBuffer;
|
||
ARC_STATUS status;
|
||
ULONG fileId;
|
||
PIMAGE_NT_HEADERS ntHeaders;
|
||
USHORT imageCharacteristics;
|
||
ULONG bytesRead;
|
||
|
||
//
|
||
// File I/O here must be sector-aligned.
|
||
//
|
||
|
||
localBuffer = (PCHAR)
|
||
(((ULONG)localBufferSpace + SECTOR_SIZE - 1) & ~(SECTOR_SIZE - 1));
|
||
|
||
//
|
||
// Read in the PE image header.
|
||
//
|
||
|
||
status = BlOpen( LoadDeviceId, ImagePath, ArcOpenReadOnly, &fileId );
|
||
if (status != ESUCCESS) {
|
||
return status;
|
||
}
|
||
|
||
status = BlRead( fileId, localBuffer, SECTOR_SIZE * 2, &bytesRead );
|
||
BlClose( fileId );
|
||
|
||
if (bytesRead != SECTOR_SIZE * 2) {
|
||
status = EBADF;
|
||
}
|
||
|
||
if (status != ESUCCESS) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// If the file header has the IMAGE_FILE_LARGE_ADDRESS_AWARE
|
||
// characteristic set then this is a PAE image.
|
||
//
|
||
|
||
ntHeaders = RtlImageNtHeader( localBuffer );
|
||
if (ntHeaders == NULL) {
|
||
return EBADF;
|
||
}
|
||
|
||
imageCharacteristics = ntHeaders->FileHeader.Characteristics;
|
||
if ((imageCharacteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE) != 0) {
|
||
|
||
//
|
||
// This is a PAE image.
|
||
//
|
||
|
||
*IsPae = TRUE;
|
||
|
||
} else {
|
||
|
||
//
|
||
// This is not a PAE image.
|
||
//
|
||
|
||
*IsPae = FALSE;
|
||
}
|
||
|
||
//
|
||
// Hot Plug Memory is only supported post 5.0
|
||
//
|
||
if (ntHeaders->OptionalHeader.MajorOperatingSystemVersion > 5 ||
|
||
((ntHeaders->OptionalHeader.MajorOperatingSystemVersion == 5) &&
|
||
(ntHeaders->OptionalHeader.MinorOperatingSystemVersion > 0 ))) {
|
||
*SupportsHotPlugMemory = TRUE;
|
||
} else {
|
||
*SupportsHotPlugMemory = FALSE;
|
||
}
|
||
|
||
return ESUCCESS;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
BlpChipsetPaeSupported(
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Scans PCI space to see if the current chipset is supported for PAE mode.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
Return Value:
|
||
|
||
TRUE - PAE is supported
|
||
|
||
FALSE - PAE is not supported
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG DevVenId=0;
|
||
ULONG i;
|
||
|
||
typedef struct _PCIDEVICE {
|
||
ULONG Bus;
|
||
ULONG Device;
|
||
ULONG DevVen;
|
||
} PCIDEVICE, *PPCIDEVICE;
|
||
|
||
PCIDEVICE BadChipsets[] = {
|
||
{0, 0, 0x1a208086}, // MCH
|
||
{0, 0, 0x1a218086}, // MCH
|
||
{0, 0, 0x1a228086}, // MCH
|
||
{0, 30, 0x24188086}, // ICH
|
||
{0, 30, 0x24288086} // ICH
|
||
};
|
||
|
||
|
||
for (i=0; i<sizeof(BadChipsets)/sizeof(PCIDEVICE); i++) {
|
||
HalGetBusData(PCIConfiguration,
|
||
BadChipsets[i].Bus,
|
||
BadChipsets[i].Device,
|
||
&DevVenId,
|
||
sizeof(DevVenId));
|
||
if (DevVenId == BadChipsets[i].DevVen) {
|
||
return(FALSE);
|
||
}
|
||
}
|
||
|
||
return(TRUE);
|
||
}
|
||
|
||
ARC_STATUS
|
||
BlpCheckVersion(
|
||
IN ULONG LoadDeviceId,
|
||
IN PCHAR ImagePath
|
||
)
|
||
{
|
||
CHAR localBufferSpace[ SECTOR_SIZE * 2 + SECTOR_SIZE - 1 ];
|
||
PCHAR localBuffer;
|
||
ARC_STATUS status;
|
||
ULONG fileId;
|
||
PIMAGE_NT_HEADERS ntHeaders;
|
||
USHORT imageCharacteristics;
|
||
ULONG bytesRead;
|
||
ULONG i,j,Count;
|
||
HARDWARE_PTE_X86 nullpte;
|
||
|
||
|
||
//
|
||
// File I/O here must be sector-aligned.
|
||
//
|
||
|
||
localBuffer = (PCHAR)
|
||
(((ULONG)localBufferSpace + SECTOR_SIZE - 1) & ~(SECTOR_SIZE - 1));
|
||
|
||
//
|
||
// Read in the PE image header.
|
||
//
|
||
|
||
status = BlOpen( LoadDeviceId, ImagePath, ArcOpenReadOnly, &fileId );
|
||
if (status != ESUCCESS) {
|
||
return status;
|
||
}
|
||
|
||
status = BlRead( fileId, localBuffer, SECTOR_SIZE * 2, &bytesRead );
|
||
BlClose( fileId );
|
||
|
||
if (bytesRead != SECTOR_SIZE * 2) {
|
||
status = EBADF;
|
||
}
|
||
|
||
if (status != ESUCCESS) {
|
||
return status;
|
||
}
|
||
ntHeaders = RtlImageNtHeader( localBuffer );
|
||
if (ntHeaders == NULL) {
|
||
return EBADF;
|
||
}
|
||
//
|
||
// Setup the mm remapping checks for post 5.0 or pre 5.0
|
||
//
|
||
if (ntHeaders->OptionalHeader.MajorOperatingSystemVersion < 5 ||
|
||
((ntHeaders->OptionalHeader.MajorOperatingSystemVersion == 5) &&
|
||
(ntHeaders->OptionalHeader.MinorOperatingSystemVersion == 0 ))) {
|
||
|
||
|
||
BlOldKernel=TRUE;
|
||
BlKernelChecked=TRUE;
|
||
BlHighestPage = ((16*1024*1024) >> PAGE_SHIFT) - 40;
|
||
|
||
//
|
||
// Virtual was moved up for the dynamic load case. It was 64MB off
|
||
// in 5.0 and prior
|
||
//
|
||
RtlZeroMemory (&nullpte,sizeof (HARDWARE_PTE_X86));
|
||
|
||
if (BlVirtualBias != 0 ) {
|
||
|
||
BlVirtualBias = OLD_ALTERNATE-KSEG0_BASE;
|
||
|
||
//
|
||
// PDE entries represent 4MB. Zap the new ones.
|
||
//
|
||
i=(OLD_ALTERNATE) >> 22L;
|
||
j=(ALTERNATE_BASE)>> 22L;
|
||
|
||
for (Count = 0; Count < 4;Count++){
|
||
PDE[i++]= PDE[j++];
|
||
}
|
||
for (Count = 0; Count < 12; Count++) {
|
||
PDE[i++]= nullpte;
|
||
}
|
||
}
|
||
}
|
||
|
||
return (ESUCCESS);
|
||
}
|