Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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