windows-nt/Source/XPSP1/NT/base/boot/bldr/i386/initx86.c
2020-09-26 16:20:57 +08:00

1348 lines
36 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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);
}