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.

839 lines
20 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. entry.c
  5. Abstract:
  6. x86-specific startup for setupldr
  7. Author:
  8. John Vert (jvert) 14-Oct-1993
  9. Revision History:
  10. --*/
  11. #include "bootx86.h"
  12. #include "stdio.h"
  13. #include "flop.h"
  14. #include <ramdisk.h>
  15. #if 0
  16. #define DBGOUT(x) BlPrint x
  17. #define DBGPAUSE while(!GET_KEY());
  18. #else
  19. #define DBGOUT(x)
  20. #define DBGPAUSE
  21. #endif
  22. //
  23. // Prototypes for Internal Routines
  24. //
  25. VOID
  26. DoGlobalInitialization(
  27. PBOOT_CONTEXT
  28. );
  29. #if defined(ELTORITO)
  30. BOOLEAN ElToritoCDBoot = FALSE;
  31. #endif
  32. extern CHAR NetBootPath[];
  33. //
  34. // Global context pointers. These are passed to us by the SU module or
  35. // the bootstrap code.
  36. //
  37. PCONFIGURATION_COMPONENT_DATA FwConfigurationTree = NULL;
  38. PEXTERNAL_SERVICES_TABLE ExternalServicesTable;
  39. UCHAR BootPartitionName[80];
  40. ULONG MachineType = 0;
  41. ULONG OsLoaderBase;
  42. ULONG OsLoaderExports;
  43. extern PUCHAR BlpResourceDirectory;
  44. extern PUCHAR BlpResourceFileOffset;
  45. ULONG PcrBasePage;
  46. ULONG TssBasePage;
  47. ULONG BootFlags = 0;
  48. ULONG NtDetectStart = 0;
  49. ULONG NtDetectEnd = 0;
  50. #ifdef FORCE_CD_BOOT
  51. BOOLEAN
  52. BlGetCdRomDrive(
  53. PUCHAR DriveId
  54. )
  55. {
  56. BOOLEAN Result = FALSE;
  57. UCHAR Id = 0;
  58. do {
  59. if (BlIsElToritoCDBoot(Id)) {
  60. *DriveId = Id;
  61. Result = TRUE;
  62. break;
  63. }
  64. Id++;
  65. }
  66. while (Id != 0);
  67. return Result;
  68. }
  69. #endif
  70. VOID
  71. NtProcessStartup(
  72. IN PBOOT_CONTEXT BootContextRecord
  73. )
  74. /*++
  75. Routine Description:
  76. Main entry point for setup loader. Control is transferred here by the
  77. start-up (SU) module.
  78. Arguments:
  79. BootContextRecord - Supplies the boot context, particularly the
  80. ExternalServicesTable.
  81. Returns:
  82. Does not return. Control eventually passed to the kernel.
  83. --*/
  84. {
  85. ARC_STATUS Status;
  86. #ifdef FORCE_CD_BOOT
  87. BOOLEAN CdFound;
  88. UCHAR CdId;
  89. #endif
  90. ULONG_PTR pFirmwareHeapAddress;
  91. ULONG TssSize,TssPages;
  92. //
  93. // Initialize the boot loader's video
  94. //
  95. DoGlobalInitialization(BootContextRecord);
  96. BlFillInSystemParameters(BootContextRecord);
  97. //
  98. // Set the global bootflags
  99. //
  100. BootFlags = BootContextRecord->BootFlags;
  101. #ifdef FORCE_CD_BOOT
  102. CdFound = BlGetCdRomDrive(&CdId);
  103. if (CdFound) {
  104. BlPrint("CD/DVD-Rom drive found with id:%u\n", CdId);
  105. BootContextRecord->FSContextPointer->BootDrive = CdId;
  106. } else {
  107. BlPrint("CD/DVD-Rom drive not found");
  108. }
  109. #endif // for FORCE_CD_BOOT
  110. if (BootContextRecord->FSContextPointer->BootDrive == 0) {
  111. //
  112. // Boot was from A:
  113. //
  114. strcpy(BootPartitionName,"multi(0)disk(0)fdisk(0)");
  115. //
  116. // To get around an apparent bug on the BIOS of some MCA machines
  117. // (specifically the NCR 386sx/MC20 w/ BIOS version 1.04.00 (3421),
  118. // Phoenix BIOS 1.02.07), whereby the first int13 to floppy results
  119. // in a garbage buffer, reset drive 0 here.
  120. //
  121. GET_SECTOR(0,0,0,0,0,0,NULL);
  122. } else if (BootContextRecord->FSContextPointer->BootDrive == 0x40) {
  123. //
  124. // Boot was from the net
  125. //
  126. strcpy(BootPartitionName,"net(0)");
  127. BlBootingFromNet = TRUE;
  128. #if defined(REMOTE_BOOT)
  129. BlGetActivePartition(NetBootActivePartitionName);
  130. NetFindCSCPartitionName();
  131. #endif
  132. } else if (BootContextRecord->FSContextPointer->BootDrive == 0x41) {
  133. //
  134. // Boot was from an SDI image
  135. //
  136. strcpy(BootPartitionName,"ramdisk(0)");
  137. } else if (BlIsElToritoCDBoot(BootContextRecord->FSContextPointer->BootDrive)) {
  138. //
  139. // Boot was from El Torito CD
  140. //
  141. sprintf(BootPartitionName, "multi(0)disk(0)cdrom(%u)", BootContextRecord->FSContextPointer->BootDrive);
  142. ElToritoCDBoot = TRUE;
  143. } else {
  144. //
  145. // Find the partition we have been booted from. Note that this
  146. // is *NOT* necessarily the active partition. If the system has
  147. // Boot Mangler installed, it will be the active partition, and
  148. // we have to go figure out what partition we are actually on.
  149. //
  150. BlGetActivePartition(BootPartitionName);
  151. #if defined(REMOTE_BOOT)
  152. strcpy(NetBootActivePartitionName, BootPartitionName);
  153. NetFindCSCPartitionName();
  154. #endif
  155. }
  156. //
  157. // We need to make sure that we've got a signature on disk 80.
  158. // If not, then write one.
  159. //
  160. {
  161. ULONG DriveId;
  162. ULONG NewSignature;
  163. UCHAR SectorBuffer[4096+256];
  164. PUCHAR Sector;
  165. LARGE_INTEGER SeekValue;
  166. ULONG Count;
  167. Status = ArcOpen("multi(0)disk(0)rdisk(0)partition(0)", ArcOpenReadWrite, &DriveId);
  168. if (Status == ESUCCESS) {
  169. //
  170. // Get a reasonably unique seed to start with.
  171. //
  172. NewSignature = ArcGetRelativeTime();
  173. NewSignature = (NewSignature & 0xFFFF) << 16;
  174. NewSignature += ArcGetRelativeTime();
  175. //
  176. // Now we have a valid new signature to put on the disk.
  177. // Read the sector off disk, put the new signature in,
  178. // write the sector back, and recompute the checksum.
  179. //
  180. Sector = ALIGN_BUFFER(SectorBuffer);
  181. SeekValue.QuadPart = 0;
  182. Status = ArcSeek(DriveId, &SeekValue, SeekAbsolute);
  183. if (Status == ESUCCESS) {
  184. Status = ArcRead(DriveId,Sector,512,&Count);
  185. if( Status == ESUCCESS ) {
  186. if( ((PULONG)Sector)[PARTITION_TABLE_OFFSET/2-1] == 0 ) {
  187. //
  188. // He's 0. Write a real signature in there.
  189. //
  190. ((PULONG)Sector)[PARTITION_TABLE_OFFSET/2-1] = NewSignature;
  191. Status = ArcSeek(DriveId, &SeekValue, SeekAbsolute);
  192. if (Status == ESUCCESS) {
  193. Status = ArcWrite(DriveId,Sector,512,&Count);
  194. if( Status != ESUCCESS ) {
  195. #if DBG
  196. BlPrint( "Falied to write the new signature on the boot partition.\n" );
  197. #endif
  198. }
  199. } else {
  200. #if DBG
  201. BlPrint( "Failed second ArcSeek on the boot partition to check for a signature.\n" );
  202. #endif
  203. }
  204. }
  205. } else {
  206. #if DBG
  207. BlPrint( "Failed to ArcRead the boot partition to check for a signature.\n" );
  208. #endif
  209. }
  210. } else {
  211. #if DBG
  212. BlPrint( "Failed to ArcSeek the boot partition to check for a signature.\n" );
  213. #endif
  214. }
  215. ArcClose(DriveId);
  216. } else {
  217. #if DBG
  218. BlPrint( "Couldn't Open the boot partition to check for a signature.\n" );
  219. #endif
  220. }
  221. }
  222. //
  223. // squirrel away some memory for the PCR and TSS so that we get the
  224. // preferred memory location (<16MB) for this data.
  225. //
  226. pFirmwareHeapAddress = (ULONG_PTR)FwAllocateHeapPermanent( 2 );
  227. if (!pFirmwareHeapAddress) {
  228. BlPrint("Couldn't allocate memory for PCR\n");
  229. goto BootFailed;
  230. }
  231. PcrBasePage = (ULONG)(pFirmwareHeapAddress>>PAGE_SHIFT);
  232. TssSize = (sizeof(KTSS) + PAGE_SIZE) & ~(PAGE_SIZE - 1);
  233. TssPages = TssSize / PAGE_SIZE;
  234. pFirmwareHeapAddress = (ULONG_PTR)FwAllocateHeapPermanent( TssPages );
  235. if (!pFirmwareHeapAddress) {
  236. BlPrint("Couldn't allocate memory for TSS\n");
  237. goto BootFailed;
  238. }
  239. TssBasePage = (ULONG)(pFirmwareHeapAddress>>PAGE_SHIFT);
  240. //
  241. // Initialize the memory descriptor list, the OS loader heap, and the
  242. // OS loader parameter block.
  243. //
  244. Status = BlMemoryInitialize();
  245. if (Status != ESUCCESS) {
  246. BlPrint("Couldn't initialize memory\n");
  247. goto BootFailed;
  248. }
  249. //
  250. // Initialize the OS loader I/O system.
  251. //
  252. AEInitializeStall();
  253. Status = BlIoInitialize();
  254. if (Status != ESUCCESS) {
  255. BlPrint("Couldn't initialize I/O\n");
  256. goto BootFailed;
  257. }
  258. //
  259. // Call off to regular startup code
  260. //
  261. BlStartup(BootPartitionName);
  262. //
  263. // we should never get here!
  264. //
  265. BootFailed:
  266. if (BootFlags & BOOTFLAG_REBOOT_ON_FAILURE) {
  267. ULONG StartTime = ArcGetRelativeTime();
  268. BlPrint(TEXT("\nRebooting in 5 seconds...\n"));
  269. while ( ArcGetRelativeTime() - StartTime < 5) {}
  270. ArcRestart();
  271. }
  272. do {
  273. GET_KEY(); // BOOT FAILED!
  274. if (BlTerminalHandleLoaderFailure()) {
  275. ArcRestart();
  276. }
  277. } while ( 1 );
  278. }
  279. BOOLEAN
  280. BlDetectHardware(
  281. IN ULONG DriveId,
  282. IN PCHAR LoadOptions
  283. )
  284. /*++
  285. Routine Description:
  286. Loads and runs NTDETECT.COM to populate the ARC configuration tree.
  287. Arguments:
  288. DriveId - Supplies drive id where NTDETECT is located.
  289. LoadOptions - Supplies Load Options string to ntdetect.
  290. Return Value:
  291. TRUE - NTDETECT successfully run.
  292. FALSE - Error
  293. --*/
  294. {
  295. // Current Loader stack size is 8K, so make sure you do not
  296. // blow that space. Make sure this is not smaller than 140.
  297. #define LOAD_OPTIONS_BUFFER_SIZE 512
  298. ARC_STATUS Status;
  299. PCONFIGURATION_COMPONENT_DATA TempFwTree;
  300. ULONG TempFwHeapUsed;
  301. ULONG FileSize;
  302. ULONG DetectFileId;
  303. FILE_INFORMATION FileInformation;
  304. PUCHAR DetectionBuffer = (PUCHAR)DETECTION_LOADED_ADDRESS;
  305. PUCHAR Options = NULL;
  306. UCHAR Buffer[LOAD_OPTIONS_BUFFER_SIZE];
  307. LARGE_INTEGER SeekPosition;
  308. ULONG Read;
  309. BOOLEAN SkipLegacyDetection;
  310. BOOLEAN Success = FALSE;
  311. ULONG HeapStart;
  312. ULONG HeapSize;
  313. ULONG RequiredLength = 0;
  314. //
  315. // Check if the ntdetect.com was bundled as a data section
  316. // in the loader executable.
  317. //
  318. if (NtDetectStart == 0) {
  319. //
  320. // Now check if we have ntdetect.com in the root directory, if yes,
  321. // we will load it to predefined location and transfer control to
  322. // it.
  323. //
  324. #if defined(ELTORITO)
  325. if (ElToritoCDBoot) {
  326. // we assume ntdetect.com is in the i386 directory
  327. Status = BlOpen( DriveId,
  328. "\\i386\\ntdetect.com",
  329. ArcOpenReadOnly,
  330. &DetectFileId );
  331. } else {
  332. #endif
  333. if (BlBootingFromNet
  334. #if defined(REMOTE_BOOT)
  335. && NetworkBootRom
  336. #endif // defined(REMOTE_BOOT)
  337. ) {
  338. strcpy(Buffer, NetBootPath);
  339. #if defined(REMOTE_BOOT)
  340. //
  341. // This is the way it was done for remote BOOT, where we were
  342. // booting out of a client's machine directory.
  343. //
  344. strcat(Buffer, "BootDrive\\ntdetect.com");
  345. #else
  346. //
  347. // This is how it is done for remote INSTALL, where we are
  348. // booting out of the templates directory under a setup directory.
  349. //
  350. strcat(Buffer, "ntdetect.com");
  351. #endif // defined(REMOTE_BOOT)
  352. Status = BlOpen( DriveId,
  353. Buffer,
  354. ArcOpenReadOnly,
  355. &DetectFileId );
  356. } else {
  357. Status = BlOpen( DriveId,
  358. "\\ntdetect.com",
  359. ArcOpenReadOnly,
  360. &DetectFileId );
  361. }
  362. #if defined(ELTORITO)
  363. }
  364. #endif
  365. if (Status != ESUCCESS) {
  366. #if DBG
  367. BlPrint("Error opening NTDETECT.COM, status = %x\n", Status);
  368. BlPrint("Press any key to continue\n");
  369. #endif
  370. goto Exit;
  371. }
  372. //
  373. // Determine the length of the ntdetect.com file
  374. //
  375. Status = BlGetFileInformation(DetectFileId, &FileInformation);
  376. if (Status != ESUCCESS) {
  377. BlClose(DetectFileId);
  378. #if DBG
  379. BlPrint("Error getting NTDETECT.COM file information, status = %x\n", Status);
  380. BlPrint("Press any key to continue\n");
  381. #endif
  382. goto Exit;
  383. }
  384. FileSize = FileInformation.EndingAddress.LowPart;
  385. if (FileSize == 0) {
  386. BlClose(DetectFileId);
  387. #if DBG
  388. BlPrint("Error: size of NTDETECT.COM is zero.\n");
  389. BlPrint("Press any key to continue\n");
  390. #endif
  391. goto Exit;
  392. }
  393. SeekPosition.QuadPart = 0;
  394. Status = BlSeek(DetectFileId,
  395. &SeekPosition,
  396. SeekAbsolute);
  397. if (Status != ESUCCESS) {
  398. BlClose(DetectFileId);
  399. #if DBG
  400. BlPrint("Error seeking to start of NTDETECT.COM file\n");
  401. BlPrint("Press any key to continue\n");
  402. #endif
  403. goto Exit;
  404. }
  405. Status = BlRead( DetectFileId,
  406. DetectionBuffer,
  407. FileSize,
  408. &Read );
  409. BlClose(DetectFileId);
  410. if (Status != ESUCCESS) {
  411. #if DBG
  412. BlPrint("Error reading from NTDETECT.COM\n");
  413. BlPrint("Read %lx bytes\n",Read);
  414. BlPrint("Press any key to continue\n");
  415. #endif
  416. goto Exit;
  417. }
  418. } else {
  419. // ntdetect.com was bundled in the loader image
  420. // as a data section. We will use it contents
  421. // instead of opening the file.
  422. RtlCopyMemory( DetectionBuffer, (PVOID)NtDetectStart, NtDetectEnd - NtDetectStart );
  423. }
  424. //
  425. // Set the heap start and size used by ntdetect
  426. //
  427. HeapStart = (TEMPORARY_HEAP_START - 0x10) * PAGE_SIZE;
  428. HeapSize = 0x10000; // 64K
  429. //
  430. // We need to pass NTDETECT pointers < 1Mb, so
  431. // use local storage off the stack if possible. (which is
  432. // always < 1Mb.) If not possible (boot.ini is too big)
  433. // and we will add it to the heap used by ntdetect.com, thereby
  434. // reducing the heap space used by ntdetect.com
  435. //
  436. if ( LoadOptions ) {
  437. // count the characters in LoadOptions + null terminator +
  438. // room for " NOLEGACY" that might be appended later
  439. RequiredLength = strlen(LoadOptions) + strlen(" NOLEGACY") + 1;
  440. // check if the buffer on the stack is big enough
  441. if ( RequiredLength > LOAD_OPTIONS_BUFFER_SIZE ) {
  442. //
  443. // Buffer is too small. let move it to the
  444. // end of the ntdetect heap
  445. //
  446. Options = (PCHAR)( HeapStart + HeapSize - RequiredLength );
  447. HeapSize -= RequiredLength;
  448. strcpy( Options, LoadOptions );
  449. } else {
  450. //
  451. // Load options will fit on the stack. copy them there
  452. //
  453. strcpy( Buffer, LoadOptions );
  454. Options = Buffer;
  455. }
  456. } else {
  457. //
  458. // No load options
  459. //
  460. Options = NULL;
  461. }
  462. //
  463. // Check whether we need to add the NOLEGACY option
  464. //
  465. if (BlDetectLegacyFreeBios()) {
  466. if (Options != NULL) {
  467. strcat(Options, " NOLEGACY");
  468. } else {
  469. strcpy(Buffer, " NOLEGACY");
  470. Options = Buffer;
  471. }
  472. }
  473. DETECT_HARDWARE((ULONG)HeapStart,
  474. (ULONG)HeapSize,
  475. (PVOID)&TempFwTree,
  476. (PULONG)&TempFwHeapUsed,
  477. (PCHAR)Options,
  478. (Options != NULL) ? strlen(Options) : 0
  479. );
  480. FwConfigurationTree = TempFwTree;
  481. Status = BlpMarkExtendedVideoRegionOffLimits();
  482. Success = (BOOLEAN)(Status == ESUCCESS);
  483. Exit:
  484. //
  485. // Reinitialize the headless port - detect wipes it out.
  486. //
  487. BlInitializeHeadlessPort();
  488. return(Success);
  489. }
  490. VOID
  491. DoGlobalInitialization(
  492. IN PBOOT_CONTEXT BootContextRecord
  493. )
  494. /*++
  495. Routine Description
  496. This routine calls all of the subsytem initialization routines.
  497. Arguments:
  498. None
  499. Returns:
  500. Nothing
  501. --*/
  502. {
  503. ARC_STATUS Status;
  504. //
  505. // Set base address of OS Loader image for the debugger.
  506. //
  507. OsLoaderBase = BootContextRecord->OsLoaderBase;
  508. OsLoaderExports = BootContextRecord->OsLoaderExports;
  509. //
  510. // Initialize memory.
  511. //
  512. Status = InitializeMemorySubsystem(BootContextRecord);
  513. if (Status != ESUCCESS) {
  514. BlPrint("InitializeMemory failed %lx\n",Status);
  515. while (1) {
  516. }
  517. }
  518. ExternalServicesTable=BootContextRecord->ExternalServicesTable;
  519. MachineType = BootContextRecord->MachineType;
  520. //
  521. // Turn the cursor off
  522. //
  523. HW_CURSOR(0,127);
  524. BlpResourceDirectory = (PUCHAR)(BootContextRecord->ResourceDirectory);
  525. BlpResourceFileOffset = (PUCHAR)(BootContextRecord->ResourceOffset);
  526. NtDetectStart = BootContextRecord->NtDetectStart;
  527. NtDetectEnd = BootContextRecord->NtDetectEnd;
  528. //
  529. // If this is an SDI boot, copy the address of the SDI image out of the
  530. // boot context record. SdiAddress is declared in boot\inc\ramdisk.h and
  531. // initialized to 0 in boot\lib\ramdisk.c.
  532. //
  533. if (BootContextRecord->FSContextPointer->BootDrive == 0x41) {
  534. SdiAddress = BootContextRecord->SdiAddress;
  535. }
  536. InitializeMemoryDescriptors ();
  537. }
  538. VOID
  539. BlGetActivePartition(
  540. OUT PUCHAR BootPartitionName
  541. )
  542. /*++
  543. Routine Description:
  544. Determine the ARC name for the partition NTLDR was started from
  545. Arguments:
  546. BootPartitionName - Supplies a buffer where the ARC name of the
  547. partition will be returned.
  548. Return Value:
  549. Name of the partition is in BootPartitionName.
  550. Must always succeed.
  551. --*/
  552. {
  553. UCHAR SectorBuffer[512];
  554. ARC_STATUS Status;
  555. ULONG FileId;
  556. ULONG Count;
  557. int i;
  558. //
  559. // The boot sector used to boot us is still in memory at 0x7c00.
  560. // The hidden sectors field in the BPB is pretty much guaranteed
  561. // to be intact, since all boot codes use that field and thus
  562. // are unlikely to have overwritten it.
  563. // We open each partition and compare the in-memory hidden sector count
  564. // at 0x7c1c to the hidden sector value in the BPB.
  565. //
  566. i = 1;
  567. do {
  568. sprintf(BootPartitionName,"multi(0)disk(0)rdisk(0)partition(%u)",i);
  569. Status = ArcOpen(BootPartitionName,ArcOpenReadOnly,&FileId);
  570. if(Status == ESUCCESS) {
  571. //
  572. // Read the first part of the partition.
  573. //
  574. Status = ArcRead(FileId,SectorBuffer,512,&Count);
  575. ArcClose(FileId);
  576. if((Status == ESUCCESS) && !memcmp(SectorBuffer+0x1c,(PVOID)0x7c1c,4)) {
  577. //
  578. // Found it, BootPartitionName is already set for return.
  579. //
  580. return;
  581. }
  582. Status = ESUCCESS;
  583. }
  584. i++;
  585. } while (Status == ESUCCESS);
  586. //
  587. // Run out of partitions without finding match. Fall back on partition 1.
  588. //
  589. strcpy(BootPartitionName,"multi(0)disk(0)rdisk(0)partition(1)");
  590. }
  591. BOOLEAN
  592. BlIsElToritoCDBoot(
  593. UCHAR DriveNum
  594. )
  595. {
  596. //
  597. // Note, even though args are short, they are pushed on the stack with
  598. // 32bit alignment so the effect on the stack seen by the 16bit real
  599. // mode code is the same as if we were pushing longs here.
  600. //
  601. // GET_ELTORITO_STATUS is 0 if we are in emulation mode
  602. if (DriveNum > 0x81) {
  603. if (!GET_ELTORITO_STATUS(FwDiskCache,DriveNum)) {
  604. return(TRUE);
  605. } else {
  606. return(FALSE);
  607. }
  608. } else {
  609. return(FALSE);
  610. }
  611. }
  612. #if defined(REMOTE_BOOT)
  613. BOOLEAN
  614. NetFindCSCPartitionName(
  615. )
  616. {
  617. UCHAR FileName[80];
  618. UCHAR DiskName[80];
  619. UCHAR PartitionName[80];
  620. PUCHAR p;
  621. ULONG Part;
  622. ULONG FileId;
  623. ULONG DeviceId;
  624. if (NetBootSearchedForCSC) {
  625. return((BOOLEAN)strlen(NetBootCSCPartitionName));
  626. }
  627. if (!strlen(NetBootActivePartitionName)) {
  628. BlGetActivePartition(NetBootActivePartitionName);
  629. }
  630. strcpy(DiskName, NetBootActivePartitionName);
  631. p = strstr(DiskName, "partition");
  632. ASSERT( p != NULL );
  633. *p = '\0';
  634. Part = 1;
  635. while (TRUE) {
  636. sprintf(PartitionName, "%spartition(%u)", DiskName, Part);
  637. if (ArcOpen(PartitionName, ArcOpenReadOnly, &DeviceId) != ESUCCESS) {
  638. break;
  639. }
  640. ArcClose(DeviceId);
  641. sprintf(FileName,
  642. "%s%s",
  643. PartitionName,
  644. REMOTE_BOOT_IMIRROR_PATH_A REMOTE_BOOT_CSC_SUBDIR_A);
  645. if (ArcOpen(FileName, ArcOpenReadOnly, &FileId) == ESUCCESS) {
  646. ArcClose(FileId);
  647. NetBootSearchedForCSC = TRUE;
  648. strcpy(NetBootCSCPartitionName, PartitionName);
  649. return TRUE;
  650. }
  651. ArcClose(FileId);
  652. Part++;
  653. }
  654. strcpy(NetBootCSCPartitionName, NetBootActivePartitionName);
  655. return FALSE;
  656. }
  657. #endif // defined(REMOTE_BOOT)