Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1347 lines
36 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. initx86.c
  5. Abstract:
  6. Does any x86-specific initialization, then starts the common ARC osloader
  7. Author:
  8. John Vert (jvert) 4-Nov-1993
  9. Revision History:
  10. --*/
  11. #include "bldrx86.h"
  12. #include "acpitabl.h"
  13. #include "msg.h"
  14. #include "bootstatus.h"
  15. #include <string.h>
  16. #include <stdlib.h>
  17. #include <stdio.h>
  18. #include <netboot.h>
  19. #include <ramdisk.h>
  20. BOOLEAN
  21. BlpPaeSupported(
  22. VOID
  23. );
  24. BOOLEAN
  25. BlpChipsetPaeSupported(
  26. VOID
  27. );
  28. ARC_STATUS
  29. Blx86GetImageProperties(
  30. IN ULONG LoadDeviceId,
  31. IN PCHAR ImagePath,
  32. OUT PBOOLEAN IsPae,
  33. OUT PBOOLEAN SupportsHotPlugMemory
  34. );
  35. BOOLEAN
  36. Blx86IsKernelCompatible(
  37. IN ULONG LoadDeviceId,
  38. IN PCHAR ImagePath,
  39. IN BOOLEAN ProcessorSupportsPae,
  40. IN OUT PBOOLEAN UsePae
  41. );
  42. BOOLEAN
  43. Blx86NeedPaeForHotPlugMemory(
  44. VOID
  45. );
  46. UCHAR BootPartitionName[80];
  47. UCHAR KernelBootDevice[80];
  48. UCHAR OsLoadFilename[100];
  49. UCHAR OsLoaderFilename[100];
  50. UCHAR SystemPartition[100];
  51. UCHAR OsLoadPartition[100];
  52. UCHAR OsLoadOptions[100];
  53. UCHAR ConsoleInputName[50];
  54. UCHAR MyBuffer[SECTOR_SIZE+32];
  55. UCHAR ConsoleOutputName[50];
  56. UCHAR X86SystemPartition[sizeof("x86systempartition=") + sizeof(BootPartitionName)];
  57. extern BOOLEAN ForceLastKnownGood;
  58. extern ULONG BlHighestPage;
  59. extern PHARDWARE_PTE PDE;
  60. extern ULONG BootFlags;
  61. extern PDESCRIPTION_HEADER
  62. BlFindACPITable(
  63. IN PCHAR TableName,
  64. IN ULONG TableLength
  65. );
  66. VOID
  67. BlStartup(
  68. IN PCHAR PartitionName
  69. )
  70. /*++
  71. Routine Description:
  72. Does x86-specific initialization, particularly presenting the boot.ini
  73. menu and running NTDETECT, then calls to the common osloader.
  74. Arguments:
  75. PartitionName - Supplies the ARC name of the partition (or floppy) that
  76. setupldr was loaded from.
  77. Return Value:
  78. Does not return
  79. --*/
  80. {
  81. ULONG Argc = 0;
  82. PUCHAR Argv[10];
  83. ARC_STATUS Status;
  84. ULONG BootFileId;
  85. PCHAR BootFile = NULL;
  86. ULONG Read;
  87. PCHAR p,q;
  88. ULONG i;
  89. ULONG DriveId;
  90. ULONG FileSize;
  91. ULONG Count;
  92. LARGE_INTEGER SeekPosition;
  93. PCHAR LoadOptions = NULL;
  94. BOOLEAN UseTimeOut=TRUE;
  95. BOOLEAN AlreadyInitialized = FALSE;
  96. extern BOOLEAN FwDescriptorsValid;
  97. PCHAR BadLinkName = NULL;
  98. //
  99. // If this is an SDI boot, initialize the ramdisk "driver" now.
  100. //
  101. // NB. PartitionName is the name of the device from which the loader
  102. // was loaded. It is NOT the name of the device from which the OS will
  103. // be loaded. If we're doing a straight ramdisk boot, this will NOT be
  104. // ramdisk(0) -- it will be net(0) or a physical disk name. Only if
  105. // we're doing a real SDI boot will this be ramdisk(0). (See
  106. // NtProcessStartup() in boot\lib\i386\entry.c.)
  107. //
  108. if ( strcmp(PartitionName,"ramdisk(0)") == 0 ) {
  109. RamdiskInitialize( NULL, TRUE );
  110. }
  111. //
  112. // Open the boot partition so we can load boot drivers off it.
  113. //
  114. Status = ArcOpen(PartitionName, ArcOpenReadWrite, &DriveId);
  115. if (Status != ESUCCESS) {
  116. BlPrint(TEXT("Couldn't open drive %s\n"),PartitionName);
  117. BlPrint(BlFindMessage(BL_DRIVE_ERROR),PartitionName);
  118. goto BootFailed;
  119. }
  120. //
  121. // Initialize dbcs font and display support.
  122. //
  123. TextGrInitialize(DriveId, NULL);
  124. //
  125. // Initialize ARC StdIo functionality
  126. //
  127. strcpy(ConsoleInputName,"consolein=multi(0)key(0)keyboard(0)");
  128. strcpy(ConsoleOutputName,"consoleout=multi(0)video(0)monitor(0)");
  129. Argv[0]=ConsoleInputName;
  130. Argv[1]=ConsoleOutputName;
  131. BlInitStdio (2, Argv);
  132. //
  133. // Announce the loader
  134. //
  135. //BlPrint(OsLoaderVersion);
  136. //
  137. // Check ntldr partition for hiberation image
  138. //
  139. BlHiberRestore(DriveId, &BadLinkName);
  140. //
  141. // Re-open the boot partition as a temporary work around
  142. // for NTFS caching bug.
  143. //
  144. ArcClose(DriveId);
  145. Status = ArcOpen(PartitionName, ArcOpenReadWrite, &DriveId);
  146. if (Status != ESUCCESS) {
  147. BlPrint(TEXT("Couldn't open drive %s\n"),PartitionName);
  148. BlPrint(BlFindMessage(BL_DRIVE_ERROR),PartitionName);
  149. goto BootFailed;
  150. }
  151. //
  152. // It is possible that the link file points to the hiber file on a scsi
  153. // disk. In that case, we need to load NTBOOTDD.SYS in order to access the
  154. // hiber file and try again.
  155. //
  156. if ((BadLinkName != NULL) &&
  157. ((_strnicmp(BadLinkName,"scsi(",5)==0) || (_strnicmp(BadLinkName,"signature(",10)==0))) {
  158. ULONG HiberDrive;
  159. //
  160. // Before loading NTBOOTDD we must load NTDETECT as that figures
  161. // out the PCI buses
  162. //
  163. if (BlDetectHardware(DriveId, "/fastdetect")) {
  164. if (AEInitializeIo(DriveId) == ESUCCESS) {
  165. AlreadyInitialized = TRUE;
  166. //
  167. // Now that NTBOOTDD.SYS is loaded, we can try again.
  168. //
  169. Status = ArcOpen(BadLinkName, ArcOpenReadWrite, &HiberDrive);
  170. if (Status == ESUCCESS) {
  171. BlHiberRestore(HiberDrive, NULL);
  172. ArcClose(HiberDrive);
  173. }
  174. }
  175. }
  176. }
  177. do {
  178. if (BlBootingFromNet) {
  179. PCHAR BootIniPath;
  180. //
  181. // If we are booting from the network and
  182. // NetBootIniContents has been specified, we
  183. // will just its contents for boot.ini
  184. //
  185. if (NetBootIniContents[0] != '\0') {
  186. BootFile = NetBootIniContents;
  187. } else {
  188. //
  189. // NetBootIniContents is empty, therefore
  190. // we need to open the boot.ini file from the
  191. // network. The acutal file to open is either
  192. // specificied in NetBootIniPath or is the
  193. // default of NetBootPath\boot.ini
  194. //
  195. if (NetBootIniPath[0] != '\0') {
  196. BootIniPath = NetBootIniPath;
  197. } else {
  198. strcpy(MyBuffer, NetBootPath);
  199. strcat(MyBuffer, "boot.ini");
  200. BootIniPath = MyBuffer;
  201. }
  202. Status = BlOpen( DriveId,
  203. BootIniPath,
  204. ArcOpenReadOnly,
  205. &BootFileId );
  206. }
  207. } else {
  208. Status = BlOpen( DriveId,
  209. "\\boot.ini",
  210. ArcOpenReadOnly,
  211. &BootFileId );
  212. }
  213. if (BootFile == NULL) {
  214. BootFile = MyBuffer;
  215. RtlZeroMemory(MyBuffer, SECTOR_SIZE+32);
  216. if (Status != ESUCCESS) {
  217. BootFile[0]='\0';
  218. } else {
  219. //
  220. // Determine the length of the boot.ini file by reading to the end of
  221. // file.
  222. //
  223. FileSize = 0;
  224. do {
  225. Status = BlRead(BootFileId, BootFile, SECTOR_SIZE, &Count);
  226. if (Status != ESUCCESS) {
  227. BlClose(BootFileId);
  228. BlPrint(BlFindMessage(BL_READ_ERROR),Status);
  229. BootFile[0] = '\0';
  230. FileSize = 0;
  231. Count = 0;
  232. goto BootFailed;
  233. }
  234. FileSize += Count;
  235. } while (Count != 0);
  236. if (FileSize >= SECTOR_SIZE) {
  237. //
  238. // We need to allocate a bigger buffer, since the boot.ini file
  239. // is bigger than one sector.
  240. //
  241. BootFile=FwAllocateHeap(FileSize);
  242. }
  243. if (BootFile == NULL) {
  244. BlPrint(BlFindMessage(BL_READ_ERROR),ENOMEM);
  245. BootFile = MyBuffer;
  246. BootFile[0] = '\0';
  247. goto BootFailed;
  248. } else {
  249. SeekPosition.QuadPart = 0;
  250. Status = BlSeek(BootFileId,
  251. &SeekPosition,
  252. SeekAbsolute);
  253. if (Status != ESUCCESS) {
  254. BlPrint(BlFindMessage(BL_READ_ERROR),Status);
  255. BootFile = MyBuffer;
  256. BootFile[0] = '\0';
  257. goto BootFailed;
  258. } else {
  259. Status = BlRead( BootFileId,
  260. BootFile,
  261. FileSize,
  262. &Read );
  263. SeekPosition.QuadPart = 0;
  264. Status = BlSeek(BootFileId,
  265. &SeekPosition,
  266. SeekAbsolute);
  267. if (Status != ESUCCESS) {
  268. BlPrint(BlFindMessage(BL_READ_ERROR),Status);
  269. BootFile = MyBuffer;
  270. BootFile[0] = '\0';
  271. goto BootFailed;
  272. } else {
  273. BootFile[Read]='\0';
  274. }
  275. }
  276. }
  277. //
  278. // Find Ctrl-Z, if it exists
  279. //
  280. p=BootFile;
  281. while ((*p!='\0') && (*p!=26)) {
  282. ++p;
  283. }
  284. if (*p != '\0') {
  285. *p='\0';
  286. }
  287. BlClose(BootFileId);
  288. }
  289. }
  290. MdShutoffFloppy();
  291. ARC_DISPLAY_CLEAR();
  292. // We require a boot.ini file when booting from the network
  293. if (BlBootingFromNet && *BootFile == '\0') {
  294. BlPrint(BlFindMessage(BL_READ_ERROR),Status);
  295. goto BootFailed;
  296. }
  297. p=BlSelectKernel(DriveId,BootFile, &LoadOptions, UseTimeOut);
  298. if ( p == NULL ) {
  299. BlPrint(BlFindMessage(BL_INVALID_BOOT_INI_FATAL));
  300. goto BootFailed;
  301. }
  302. #if defined(REMOTE_BOOT)
  303. //
  304. // We may have loaded boot.ini from the hard drive but if the selection was
  305. // for a remote boot installation then we need to load the rest from the net.
  306. //
  307. if((DriveId != NET_DEVICE_ID) &&
  308. (!_strnicmp(p,"net(",4))) {
  309. BlPrint("\nWarning:Booting from CSC without access to server\n");
  310. strcpy(BootPartitionName,"net(0)");
  311. BlBootingFromNet = TRUE;
  312. NetworkBootRom = FALSE;
  313. //
  314. // p points to something like: "net(0)\COLINW3\IMirror\Clients\cwintel\BootDrive\WINNT"
  315. // NetBootPath needs to contain "\Clients\cwintel\"
  316. // ServerShare is used inside lib\netboot.c to find the correct file if CSC
  317. // is used.
  318. //
  319. q = strchr(p+sizeof("net(0)"), '\\');
  320. q = strchr(q+1, '\\');
  321. strcpy(NetBootPath,q);
  322. q = strrchr( NetBootPath, '\\' );
  323. q[1] = '\0';
  324. }
  325. #endif // defined(REMOTE_BOOT)
  326. if (!AlreadyInitialized) {
  327. // BlPrint(BlFindMessage(BL_NTDETECT_MSG));
  328. if (!BlDetectHardware(DriveId, LoadOptions)) {
  329. BlPrint(BlFindMessage(BL_NTDETECT_FAILURE));
  330. return;
  331. }
  332. ARC_DISPLAY_CLEAR();
  333. //
  334. // Initialize SCSI boot driver, if necessary.
  335. //
  336. if (_strnicmp(p,"scsi(",5)==0 || _strnicmp(p,"signature(",10)==0) {
  337. AEInitializeIo(DriveId);
  338. }
  339. ArcClose(DriveId);
  340. //
  341. // Indicate that fw memory descriptors cannot be changed from
  342. // now on.
  343. //
  344. FwDescriptorsValid = FALSE;
  345. } else {
  346. ARC_DISPLAY_CLEAR();
  347. }
  348. //
  349. // Set AlreadyInitialized Flag to TRUE to indicate that ntdetect
  350. // routines have been run.
  351. //
  352. AlreadyInitialized = TRUE;
  353. //
  354. // Only time out the boot menu the first time through the boot.
  355. // For all subsequent reboots, the menu will stay up.
  356. //
  357. UseTimeOut=FALSE;
  358. i=0;
  359. while (*p !='\\') {
  360. KernelBootDevice[i] = *p;
  361. i++;
  362. p++;
  363. }
  364. KernelBootDevice[i] = '\0';
  365. //
  366. // If the user hasn't chosen an advanced boot mode then we will present
  367. // them with the menu and select our default.
  368. //
  369. if(BlGetAdvancedBootOption() == -1) {
  370. PVOID dataHandle;
  371. ULONG abmDefault;
  372. BSD_LAST_BOOT_STATUS LastBootStatus = BsdLastBootGood;
  373. //
  374. // Open the boot status data.
  375. //
  376. Status = BlLockBootStatusData(0, KernelBootDevice, p, &dataHandle);
  377. if(Status == ESUCCESS) {
  378. //
  379. // Check the status of the last boot. This will return both the
  380. // status and the advanced boot mode we should enter (based on the
  381. // status and the "enable auto advanced boot" flag.
  382. //
  383. abmDefault = BlGetLastBootStatus(dataHandle, &LastBootStatus);
  384. if(LastBootStatus == BsdLastBootFailed) {
  385. //
  386. // If we should attempt an ABM mode then present the user with
  387. // the menu.
  388. //
  389. if(abmDefault != -1) {
  390. ULONG menuTitle;
  391. UCHAR timeout;
  392. if(LastBootStatus == BsdLastBootFailed) {
  393. menuTitle = BL_ADVANCEDBOOT_AUTOLKG_TITLE;
  394. } else if(LastBootStatus == BsdLastBootNotShutdown) {
  395. menuTitle = BL_ADVANCEDBOOT_AUTOSAFE_TITLE;
  396. }
  397. //
  398. // Read the timeout value.
  399. //
  400. Status = BlGetSetBootStatusData(dataHandle,
  401. TRUE,
  402. RtlBsdItemAabTimeout,
  403. &timeout,
  404. sizeof(UCHAR),
  405. NULL);
  406. if(Status != ESUCCESS) {
  407. timeout = 30;
  408. }
  409. abmDefault = BlDoAdvancedBoot(menuTitle,
  410. -1,
  411. TRUE,
  412. timeout);
  413. }
  414. BlAutoAdvancedBoot(&LoadOptions,
  415. LastBootStatus,
  416. abmDefault);
  417. }
  418. BlUnlockBootStatusData(dataHandle);
  419. }
  420. }
  421. //
  422. // We are fooling the OS Loader here. It only uses the osloader= variable
  423. // to determine where to load HAL.DLL from. Since x86 systems have no
  424. // "system partition" we want to load HAL.DLL from \nt\system\HAL.DLL.
  425. // So we pass that it as the osloader path.
  426. //
  427. strcpy(OsLoaderFilename,"osloader=");
  428. strcat(OsLoaderFilename,p);
  429. strcat(OsLoaderFilename,"\\System32\\NTLDR");
  430. strcpy(SystemPartition,"systempartition=");
  431. strcat(SystemPartition,KernelBootDevice);
  432. strcpy(OsLoadPartition,"osloadpartition=");
  433. strcat(OsLoadPartition,KernelBootDevice);
  434. strcpy(OsLoadFilename, "osloadfilename=");
  435. strcat(OsLoadFilename, p);
  436. strcpy(OsLoadOptions,"osloadoptions=");
  437. if (LoadOptions) {
  438. strcat(OsLoadOptions,LoadOptions);
  439. }
  440. strcpy(ConsoleInputName,"consolein=multi(0)key(0)keyboard(0)");
  441. strcpy(ConsoleOutputName,"consoleout=multi(0)video(0)monitor(0)");
  442. strcpy(X86SystemPartition,"x86systempartition=");
  443. strcat(X86SystemPartition,PartitionName);
  444. Argv[Argc++]="load";
  445. Argv[Argc++]=OsLoaderFilename;
  446. Argv[Argc++]=SystemPartition;
  447. Argv[Argc++]=OsLoadFilename;
  448. Argv[Argc++]=OsLoadPartition;
  449. Argv[Argc++]=OsLoadOptions;
  450. Argv[Argc++]=X86SystemPartition;
  451. Status = BlOsLoader( Argc, Argv, NULL );
  452. BootFailed:
  453. ForceLastKnownGood = FALSE;
  454. if (Status != ESUCCESS) {
  455. #if defined(ENABLE_LOADER_DEBUG)
  456. DbgBreakPoint();
  457. #endif
  458. if (BootFlags & BOOTFLAG_REBOOT_ON_FAILURE) {
  459. ULONG StartTime = ArcGetRelativeTime();
  460. BlPrint(TEXT("\nRebooting in 5 seconds...\n"));
  461. while ( ArcGetRelativeTime() - StartTime < 5) {}
  462. ArcRestart();
  463. }
  464. //
  465. // Boot failed, wait for reboot
  466. //
  467. while (TRUE) {
  468. while (!BlGetKey()); // BOOT FAILED!
  469. if (BlTerminalHandleLoaderFailure()) {
  470. ArcRestart();
  471. }
  472. }
  473. } else {
  474. //
  475. // Need to reopen the drive
  476. //
  477. Status = ArcOpen(BootPartitionName, ArcOpenReadOnly, &DriveId);
  478. if (Status != ESUCCESS) {
  479. BlPrint(BlFindMessage(BL_DRIVE_ERROR),BootPartitionName);
  480. goto BootFailed;
  481. }
  482. }
  483. } while (TRUE);
  484. }
  485. VOID
  486. DoApmAttemptReconnect(
  487. VOID
  488. )
  489. {
  490. APM_ATTEMPT_RECONNECT();
  491. }
  492. BOOLEAN
  493. Blx86NeedPaeForHotPlugMemory(
  494. VOID
  495. )
  496. /*++
  497. Routine Description:
  498. Determine whether any hot plug memory described in the SRAT table
  499. extends beyond the 4gb mark and thus implies a need for PAE.
  500. Arguments:
  501. None
  502. Return Value:
  503. TRUE: Machine supports hot plug memory beyond the 4gb mark, PAE should be
  504. turned on if possible.
  505. FALSE: Machine doesn't support hot plug memory beyond the 4gb mark.
  506. --*/
  507. {
  508. PACPI_SRAT SratTable;
  509. PACPI_SRAT_ENTRY SratEntry;
  510. PACPI_SRAT_ENTRY SratEnd;
  511. SratTable = (PACPI_SRAT) BlFindACPITable("SRAT", sizeof(ACPI_SRAT));
  512. if (SratTable == NULL) {
  513. return FALSE;
  514. }
  515. SratTable = (PACPI_SRAT) BlFindACPITable("SRAT", SratTable->Header.Length);
  516. if (SratTable == NULL) {
  517. return FALSE;
  518. }
  519. SratEnd = (PACPI_SRAT_ENTRY)(((PUCHAR)SratTable) +
  520. SratTable->Header.Length);
  521. for (SratEntry = (PACPI_SRAT_ENTRY)(SratTable + 1);
  522. SratEntry < SratEnd;
  523. SratEntry = (PACPI_SRAT_ENTRY)(((PUCHAR) SratEntry) + SratEntry->Length)) {
  524. if (SratEntry->Type != SratMemory) {
  525. continue;
  526. }
  527. if (SratEntry->MemoryAffinity.Flags.HotPlug &&
  528. SratEntry->MemoryAffinity.Flags.Enabled) {
  529. ULONGLONG Extent;
  530. //
  531. // Check if hot plug region ends beyond the 4gb mark.
  532. //
  533. Extent = SratEntry->MemoryAffinity.Base.QuadPart +
  534. SratEntry->MemoryAffinity.Length;
  535. if (Extent > 0x100000000) {
  536. return TRUE;
  537. }
  538. }
  539. }
  540. return FALSE;
  541. }
  542. ARC_STATUS
  543. Blx86CheckForPaeKernel(
  544. IN BOOLEAN UserSpecifiedPae,
  545. IN BOOLEAN UserSpecifiedNoPae,
  546. IN PCHAR UserSpecifiedKernelImage,
  547. IN PCHAR HalImagePath,
  548. IN ULONG LoadDeviceId,
  549. IN ULONG SystemDeviceId,
  550. OUT PULONG HighestSystemPage,
  551. OUT PBOOLEAN UsePaeMode,
  552. IN OUT PCHAR KernelPath
  553. )
  554. /*++
  555. Routine Description:
  556. There are two kernels: one, ntkrnlpa.exe, is compiled for PAE mode.
  557. The other, ntoskrnl.exe, is not.
  558. This routine is responsible for deciding which one to load.
  559. Arguments:
  560. UserSpecifiedPae - Indicates whether the user requested PAE mode
  561. via the /PAE loader switch.
  562. UserSpecifiedNoPae - Indicates whether the user requrested non-PAE mode
  563. via the /NOPAE loader switch.
  564. UserSpecifiedKernelImage - Points to the user-specified kernel image name,
  565. indicated via the /KERNEL= switch, or NULL if none was specified.
  566. HalImagePath - Points to the hal image that will be used.
  567. LoadDeviceId - The ARC device handle of the kernel load device.
  568. SystemDeviceId - The ARC device handle of the system device.
  569. HighestSystemPage - Out parameter returning the highest physical
  570. page found in the system.
  571. UsePaeMode - Indicates whether the kernel should be loaded in PAE mode.
  572. KernelPath - On input, the directory path of the kernel images. Upon
  573. successful return, this will contain the full kernel image path.
  574. Return Value:
  575. ESUCCESS : A compatible kernel has been located, and its path resides in
  576. the output buffer pointed to by KernelPath.
  577. EINVAL: A compatible kernel could not be located. This will be a fatal
  578. condition.
  579. EBADF: hal is corrupt or missing. This will be a fatal condition.
  580. --*/
  581. {
  582. BOOLEAN foundMemoryAbove4G;
  583. BOOLEAN usePae;
  584. BOOLEAN processorSupportsPae;
  585. BOOLEAN halSupportsPae;
  586. BOOLEAN osSupportsHotPlugMemory;
  587. BOOLEAN havePaeKernel;
  588. BOOLEAN compatibleKernel;
  589. ULONG lastPage;
  590. PLIST_ENTRY link;
  591. PLIST_ENTRY listHead;
  592. PMEMORY_ALLOCATION_DESCRIPTOR descriptor;
  593. PCHAR kernelImageNamePae;
  594. PCHAR kernelImageNameNoPae;
  595. PCHAR kernelImageName;
  596. PCHAR kernelImageNameTarget;
  597. ARC_STATUS status;
  598. ULONG highestSystemPage;
  599. ULONG pagesAbove4Gig;
  600. kernelImageNameNoPae = "ntoskrnl.exe";
  601. kernelImageNamePae = "ntkrnlpa.exe";
  602. kernelImageNameTarget = KernelPath + strlen( KernelPath );
  603. //
  604. // Determine the highest physical page. Also, count the number of pages
  605. // at or above the 4G mark.
  606. //
  607. highestSystemPage = 0;
  608. pagesAbove4Gig = 0;
  609. listHead = &BlLoaderBlock->MemoryDescriptorListHead;
  610. link = listHead->Flink;
  611. while (link != listHead) {
  612. descriptor = CONTAINING_RECORD(link,
  613. MEMORY_ALLOCATION_DESCRIPTOR,
  614. ListEntry);
  615. lastPage = descriptor->BasePage + descriptor->PageCount - 1;
  616. if (lastPage > highestSystemPage) {
  617. //
  618. // We have a new highest system page, record it.
  619. //
  620. highestSystemPage = lastPage;
  621. }
  622. if (lastPage >= (1024 * 1024)) {
  623. //
  624. // This descriptor includes one or more pages at or above
  625. // the 4G mark.
  626. //
  627. if (descriptor->BasePage >= (1024 * 1024)) {
  628. //
  629. // All of the pages in this descriptor lie at or above 4G.
  630. //
  631. pagesAbove4Gig += descriptor->PageCount;
  632. } else {
  633. //
  634. // Only some of the pages in this descriptor lie at or above
  635. // 4G.
  636. //
  637. pagesAbove4Gig += lastPage - (1024 * 1024) + 1;
  638. }
  639. }
  640. link = link->Flink;
  641. }
  642. *HighestSystemPage = highestSystemPage;
  643. //
  644. // Record whether there is a non-trivial amount of memory above 4G on this
  645. // machine. Note that most machines with "exactly" 4G of ram actually move
  646. // a small amount of ram to above the 4G mark.
  647. //
  648. // Because running a PAE kernel inflicts a performance hit, we would rather
  649. // ignore some amount of memory x rather than move into PAE mode to use it.
  650. //
  651. // Right now, x is set to 64MB, or 16384 pages.
  652. //
  653. if (pagesAbove4Gig > 16384) {
  654. foundMemoryAbove4G = TRUE;
  655. } else {
  656. foundMemoryAbove4G = FALSE;
  657. }
  658. //
  659. // Find out if this processor can handle PAE mode.
  660. //
  661. processorSupportsPae = BlpPaeSupported();
  662. //
  663. // Find out whether this chipset supports PAE
  664. //
  665. if (!BlpChipsetPaeSupported()) {
  666. processorSupportsPae = FALSE;
  667. }
  668. //
  669. // Start out with a pae flag based on whether memory above 4G was located
  670. // or whether a /PAE switch was passed on the command line.
  671. //
  672. //
  673. // It used to be the case that we would default to PAE mode if memory
  674. // above 4G physical was found in the machine. The decision was made
  675. // to NEVER default to PAE mode, rather to use PAE only when the
  676. // user specifically asks for it.
  677. //
  678. // If we revert back to the previous way of doing things, uncomment the
  679. // following line and remove the subsequent one.
  680. //
  681. // if (foundMemoryAbove4G || UserSpecifiedPae) {
  682. //
  683. if (UserSpecifiedPae) {
  684. usePae = TRUE;
  685. } else {
  686. usePae = FALSE;
  687. }
  688. //
  689. // Determine whether the HAL image can support PAE mode and
  690. // whether the underlying OS supports hot plug memory.
  691. //
  692. status = Blx86GetImageProperties( SystemDeviceId,
  693. HalImagePath,
  694. &halSupportsPae,
  695. &osSupportsHotPlugMemory );
  696. if (status != ESUCCESS) {
  697. //
  698. // Apparently the HAL image supplied is invalid.
  699. //
  700. return(EBADF);
  701. }
  702. //
  703. // If machine has the ability for memory to be hot plugged over
  704. // the 4gb mark, then this is interpreted as a user request for
  705. // PAE support. This request will be ignored if not supported by
  706. // the underlying hardware or operating system.
  707. //
  708. if (osSupportsHotPlugMemory && Blx86NeedPaeForHotPlugMemory()) {
  709. usePae = TRUE;
  710. }
  711. if (halSupportsPae == FALSE) {
  712. //
  713. // The HAL cannot support operation in PAE mode. Override
  714. // processorSupportsPae to FALSE in this case, meaning that we must
  715. // not under any circumstances try to use PAE mode.
  716. //
  717. processorSupportsPae = FALSE;
  718. }
  719. //
  720. // If the processor doesn't handle PAE mode or if the user specified
  721. // a /NOPAE switch on the loader command line, then disable PAE mode.
  722. //
  723. if (processorSupportsPae == FALSE || UserSpecifiedNoPae) {
  724. usePae = FALSE;
  725. }
  726. //
  727. // Choose the image name based on the data accumulated thus far.
  728. //
  729. if (UserSpecifiedKernelImage != NULL) {
  730. kernelImageName = UserSpecifiedKernelImage;
  731. } else if (usePae != FALSE) {
  732. kernelImageName = kernelImageNamePae;
  733. } else {
  734. kernelImageName = kernelImageNameNoPae;
  735. }
  736. //
  737. // Build the path for this kernel and determine its suitability.
  738. //
  739. strcpy( kernelImageNameTarget, kernelImageName );
  740. compatibleKernel = Blx86IsKernelCompatible( LoadDeviceId,
  741. KernelPath,
  742. processorSupportsPae,
  743. &usePae );
  744. if (compatibleKernel == FALSE) {
  745. //
  746. // This kernel is not compatible or does not exist. If the failed
  747. // kernel was user-specified, fall back to the default, non-PAE
  748. // kernel and see if that is compatible.
  749. //
  750. if (UserSpecifiedKernelImage != NULL) {
  751. kernelImageName = kernelImageNameNoPae;
  752. strcpy( kernelImageNameTarget, kernelImageName );
  753. compatibleKernel = Blx86IsKernelCompatible( LoadDeviceId,
  754. KernelPath,
  755. processorSupportsPae,
  756. &usePae );
  757. }
  758. }
  759. if (compatibleKernel == FALSE) {
  760. //
  761. // At this point we have tried one of the default kernel image names,
  762. // as well as any user-specified kernel image name. There remains
  763. // one final default image name that hasn't been tried. Determine
  764. // which one that is and try it.
  765. //
  766. if (kernelImageName == kernelImageNameNoPae) {
  767. kernelImageName = kernelImageNamePae;
  768. } else {
  769. kernelImageName = kernelImageNameNoPae;
  770. }
  771. strcpy( kernelImageNameTarget, kernelImageName );
  772. compatibleKernel = Blx86IsKernelCompatible( LoadDeviceId,
  773. KernelPath,
  774. processorSupportsPae,
  775. &usePae );
  776. }
  777. if (compatibleKernel != FALSE) {
  778. *UsePaeMode = usePae;
  779. status = ESUCCESS;
  780. } else {
  781. status = EINVAL;
  782. }
  783. return status;
  784. }
  785. BOOLEAN
  786. Blx86IsKernelCompatible(
  787. IN ULONG LoadDeviceId,
  788. IN PCHAR ImagePath,
  789. IN BOOLEAN ProcessorSupportsPae,
  790. OUT PBOOLEAN UsePae
  791. )
  792. /*++
  793. Routine Description:
  794. This routine examines the supplied kernel image and determines whether
  795. it is valid and compatible with the current processor and, if so, whether
  796. PAE mode should be enabled.
  797. Arguments:
  798. LoadDeviceId - The ARC device handle of the kernel load device.
  799. ImagePath - Pointer to a buffer containing the full path of the
  800. kernel to check.
  801. ProcessorSupportsPae - TRUE if the current processor supports PAE
  802. mode, FALSE otherwise.
  803. UsePae - Upon successful return, indicates whether the kernel is PAE
  804. enabled.
  805. Return Value:
  806. TRUE: The supplied kernel image is compatible with the current processor,
  807. and *UsePae has been updated as appropriate.
  808. FALSE: The supplied kernel image is invalid or is not compatible with the
  809. current processor.
  810. --*/
  811. {
  812. BOOLEAN isPaeKernel;
  813. BOOLEAN supportsHotPlugMemory;
  814. ARC_STATUS status;
  815. status = Blx86GetImageProperties( LoadDeviceId,
  816. ImagePath,
  817. &isPaeKernel,
  818. &supportsHotPlugMemory );
  819. if (status != ESUCCESS) {
  820. //
  821. // This kernel is invalid or does not exist. Therefore, it is
  822. // not compatible.
  823. //
  824. return FALSE;
  825. }
  826. if (isPaeKernel == FALSE) {
  827. //
  828. // This is a non-PAE kernel. All supported processors can run in
  829. // non-PAE mode. Indicate that PAE mode should not be used and that
  830. // this kernel is compatible.
  831. //
  832. *UsePae = FALSE;
  833. return TRUE;
  834. } else {
  835. //
  836. // This is a PAE kernel.
  837. //
  838. if (ProcessorSupportsPae == FALSE) {
  839. //
  840. // This is a PAE kernel but the processor will not run in that
  841. // mode. Indicate that this kernel is not compatible.
  842. //
  843. return FALSE;
  844. } else {
  845. //
  846. // This is a PAE kernel and a PAE processor. Indicate that PAE
  847. // mode should be used and that this kernel is compatible.
  848. //
  849. *UsePae = TRUE;
  850. return TRUE;
  851. }
  852. }
  853. }
  854. ARC_STATUS
  855. Blx86GetImageProperties(
  856. IN ULONG LoadDeviceId,
  857. IN PCHAR ImagePath,
  858. OUT PBOOLEAN IsPae,
  859. OUT PBOOLEAN SupportsHotPlugMemory
  860. )
  861. /*++
  862. Routine Description:
  863. This routine examines the supplied image and determines whether
  864. it is valid and, if so, whether it is PAE compatible by examining
  865. the IMAGE_FILE_LARGE_ADDRESS_AWARE bit.
  866. Arguments:
  867. LoadDeviceId - The ARC device handle of the image device.
  868. ImagePath - Pointer to a buffer containing the full path of the
  869. kernel to check.
  870. IsPae - Upon successful return, indicates whether the image is PAE
  871. compatible.
  872. SupportsHotPlugMemory - Upon successful return, indicates whether the
  873. image indicates an OS that supports hot plug memory.
  874. Return Value:
  875. ESUCCESS - The supplied kernel image is valid, and *IsPae has been updated
  876. according to the image header.
  877. Otherwise, the Arc status of the failed operation is returned.
  878. --*/
  879. {
  880. CHAR localBufferSpace[ SECTOR_SIZE * 2 + SECTOR_SIZE - 1 ];
  881. PCHAR localBuffer;
  882. ARC_STATUS status;
  883. ULONG fileId;
  884. PIMAGE_NT_HEADERS ntHeaders;
  885. USHORT imageCharacteristics;
  886. ULONG bytesRead;
  887. //
  888. // File I/O here must be sector-aligned.
  889. //
  890. localBuffer = (PCHAR)
  891. (((ULONG)localBufferSpace + SECTOR_SIZE - 1) & ~(SECTOR_SIZE - 1));
  892. //
  893. // Read in the PE image header.
  894. //
  895. status = BlOpen( LoadDeviceId, ImagePath, ArcOpenReadOnly, &fileId );
  896. if (status != ESUCCESS) {
  897. return status;
  898. }
  899. status = BlRead( fileId, localBuffer, SECTOR_SIZE * 2, &bytesRead );
  900. BlClose( fileId );
  901. if (bytesRead != SECTOR_SIZE * 2) {
  902. status = EBADF;
  903. }
  904. if (status != ESUCCESS) {
  905. return status;
  906. }
  907. //
  908. // If the file header has the IMAGE_FILE_LARGE_ADDRESS_AWARE
  909. // characteristic set then this is a PAE image.
  910. //
  911. ntHeaders = RtlImageNtHeader( localBuffer );
  912. if (ntHeaders == NULL) {
  913. return EBADF;
  914. }
  915. imageCharacteristics = ntHeaders->FileHeader.Characteristics;
  916. if ((imageCharacteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE) != 0) {
  917. //
  918. // This is a PAE image.
  919. //
  920. *IsPae = TRUE;
  921. } else {
  922. //
  923. // This is not a PAE image.
  924. //
  925. *IsPae = FALSE;
  926. }
  927. //
  928. // Hot Plug Memory is only supported post 5.0
  929. //
  930. if (ntHeaders->OptionalHeader.MajorOperatingSystemVersion > 5 ||
  931. ((ntHeaders->OptionalHeader.MajorOperatingSystemVersion == 5) &&
  932. (ntHeaders->OptionalHeader.MinorOperatingSystemVersion > 0 ))) {
  933. *SupportsHotPlugMemory = TRUE;
  934. } else {
  935. *SupportsHotPlugMemory = FALSE;
  936. }
  937. return ESUCCESS;
  938. }
  939. BOOLEAN
  940. BlpChipsetPaeSupported(
  941. VOID
  942. )
  943. /*++
  944. Routine Description:
  945. Scans PCI space to see if the current chipset is supported for PAE mode.
  946. Arguments:
  947. None
  948. Return Value:
  949. TRUE - PAE is supported
  950. FALSE - PAE is not supported
  951. --*/
  952. {
  953. ULONG DevVenId=0;
  954. ULONG i;
  955. typedef struct _PCIDEVICE {
  956. ULONG Bus;
  957. ULONG Device;
  958. ULONG DevVen;
  959. } PCIDEVICE, *PPCIDEVICE;
  960. PCIDEVICE BadChipsets[] = {
  961. {0, 0, 0x1a208086}, // MCH
  962. {0, 0, 0x1a218086}, // MCH
  963. {0, 0, 0x1a228086}, // MCH
  964. {0, 30, 0x24188086}, // ICH
  965. {0, 30, 0x24288086} // ICH
  966. };
  967. for (i=0; i<sizeof(BadChipsets)/sizeof(PCIDEVICE); i++) {
  968. HalGetBusData(PCIConfiguration,
  969. BadChipsets[i].Bus,
  970. BadChipsets[i].Device,
  971. &DevVenId,
  972. sizeof(DevVenId));
  973. if (DevVenId == BadChipsets[i].DevVen) {
  974. return(FALSE);
  975. }
  976. }
  977. return(TRUE);
  978. }
  979. ARC_STATUS
  980. BlpCheckVersion(
  981. IN ULONG LoadDeviceId,
  982. IN PCHAR ImagePath
  983. )
  984. {
  985. CHAR localBufferSpace[ SECTOR_SIZE * 2 + SECTOR_SIZE - 1 ];
  986. PCHAR localBuffer;
  987. ARC_STATUS status;
  988. ULONG fileId;
  989. PIMAGE_NT_HEADERS ntHeaders;
  990. USHORT imageCharacteristics;
  991. ULONG bytesRead;
  992. ULONG i,j,Count;
  993. HARDWARE_PTE_X86 nullpte;
  994. //
  995. // File I/O here must be sector-aligned.
  996. //
  997. localBuffer = (PCHAR)
  998. (((ULONG)localBufferSpace + SECTOR_SIZE - 1) & ~(SECTOR_SIZE - 1));
  999. //
  1000. // Read in the PE image header.
  1001. //
  1002. status = BlOpen( LoadDeviceId, ImagePath, ArcOpenReadOnly, &fileId );
  1003. if (status != ESUCCESS) {
  1004. return status;
  1005. }
  1006. status = BlRead( fileId, localBuffer, SECTOR_SIZE * 2, &bytesRead );
  1007. BlClose( fileId );
  1008. if (bytesRead != SECTOR_SIZE * 2) {
  1009. status = EBADF;
  1010. }
  1011. if (status != ESUCCESS) {
  1012. return status;
  1013. }
  1014. ntHeaders = RtlImageNtHeader( localBuffer );
  1015. if (ntHeaders == NULL) {
  1016. return EBADF;
  1017. }
  1018. //
  1019. // Setup the mm remapping checks for post 5.0 or pre 5.0
  1020. //
  1021. if (ntHeaders->OptionalHeader.MajorOperatingSystemVersion < 5 ||
  1022. ((ntHeaders->OptionalHeader.MajorOperatingSystemVersion == 5) &&
  1023. (ntHeaders->OptionalHeader.MinorOperatingSystemVersion == 0 ))) {
  1024. BlOldKernel=TRUE;
  1025. BlKernelChecked=TRUE;
  1026. BlHighestPage = ((16*1024*1024) >> PAGE_SHIFT) - 40;
  1027. //
  1028. // Virtual was moved up for the dynamic load case. It was 64MB off
  1029. // in 5.0 and prior
  1030. //
  1031. RtlZeroMemory (&nullpte,sizeof (HARDWARE_PTE_X86));
  1032. if (BlVirtualBias != 0 ) {
  1033. BlVirtualBias = OLD_ALTERNATE-KSEG0_BASE;
  1034. //
  1035. // PDE entries represent 4MB. Zap the new ones.
  1036. //
  1037. i=(OLD_ALTERNATE) >> 22L;
  1038. j=(ALTERNATE_BASE)>> 22L;
  1039. for (Count = 0; Count < 4;Count++){
  1040. PDE[i++]= PDE[j++];
  1041. }
  1042. for (Count = 0; Count < 12; Count++) {
  1043. PDE[i++]= nullpte;
  1044. }
  1045. }
  1046. }
  1047. return (ESUCCESS);
  1048. }