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.

4896 lines
139 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. spboot.c
  5. Abstract:
  6. accessing and configuring boot variables.
  7. Author:
  8. Sunil Pai (sunilp) 26-October-1993
  9. Revision History:
  10. --*/
  11. #include "spprecmp.h"
  12. #pragma hdrstop
  13. #include <hdlsblk.h>
  14. #include <hdlsterm.h>
  15. #if defined(EFI_NVRAM_ENABLED)
  16. #include <efi.h>
  17. #include <efiapi.h>
  18. #endif
  19. #include "bootvar.h"
  20. //
  21. // Globals to this module
  22. //
  23. static ULONG Timeout;
  24. static PWSTR Default;
  25. ULONG DefaultSignature;
  26. static PWSTR *BootVars[MAXBOOTVARS];
  27. static BOOLEAN CleanSysPartOrphan = FALSE;
  28. PWSTR *CurrentNtDirectoryList = NULL;
  29. // do NOT change the order of the elements in this array.
  30. PCHAR NvramVarNames[MAXBOOTVARS] = {
  31. LOADIDENTIFIERVAR,
  32. OSLOADERVAR,
  33. OSLOADPARTITIONVAR,
  34. OSLOADFILENAMEVAR,
  35. OSLOADOPTIONSVAR,
  36. SYSTEMPARTITIONVAR
  37. };
  38. PCHAR OldBootVars[MAXBOOTVARS];
  39. PWSTR NewBootVars[MAXBOOTVARS];
  40. #if defined(_X86_)
  41. BOOLEAN IsArcChecked = FALSE;
  42. BOOLEAN IsArcMachine;
  43. #endif
  44. PSP_BOOT_ENTRY SpBootEntries = NULL;
  45. PBOOT_OPTIONS SpBootOptions = NULL;
  46. RedirectSwitchesModeEnum RedirectSwitchesMode = UseDefaultSwitches;
  47. REDIRECT_SWITCHES RedirectSwitches;
  48. #ifdef _X86_
  49. extern BOOLEAN g_Win9xBackup;
  50. #endif
  51. //
  52. // Local functions.
  53. //
  54. PWSTR
  55. SpArcPathFromBootSet(
  56. IN BOOTVAR BootVariable,
  57. IN ULONG Component
  58. );
  59. BOOLEAN
  60. SpConvertArcBootEntries (
  61. IN ULONG MaxComponents
  62. );
  63. VOID
  64. SpCreateBootEntry(
  65. IN ULONG_PTR Status,
  66. IN PDISK_REGION BootFileRegion,
  67. IN PWSTR BootFilePath,
  68. IN PDISK_REGION OsLoadRegion,
  69. IN PWSTR OsLoadPath,
  70. IN PWSTR OsLoadOptions,
  71. IN PWSTR FriendlyName
  72. );
  73. PCHAR
  74. SppGetArcEnvVar(
  75. IN BOOTVAR Variable
  76. );
  77. VOID
  78. SpFreeBootEntries (
  79. VOID
  80. );
  81. BOOLEAN
  82. SppSetArcEnvVar(
  83. IN BOOTVAR Variable,
  84. IN PWSTR *VarComponents,
  85. IN BOOLEAN bWriteVar
  86. );
  87. #if defined(EFI_NVRAM_ENABLED)
  88. typedef struct _HARDDISK_NAME_TRANSLATION {
  89. struct _HARDDISK_NAME_TRANSLATION *Next;
  90. PWSTR VolumeName;
  91. PWSTR PartitionName;
  92. } HARDDISK_NAME_TRANSLATION, *PHARDDISK_NAME_TRANSLATION;
  93. PHARDDISK_NAME_TRANSLATION SpHarddiskNameTranslations = NULL;
  94. BOOLEAN
  95. SpBuildHarddiskNameTranslations (
  96. VOID
  97. );
  98. BOOLEAN
  99. SpFlushEfiBootEntries (
  100. VOID
  101. );
  102. BOOLEAN
  103. SpReadAndConvertEfiBootEntries (
  104. VOID
  105. );
  106. ULONG
  107. SpSafeWcslen (
  108. IN PWSTR String,
  109. IN PWSTR Max
  110. );
  111. VOID
  112. SpTranslateFilePathToRegion (
  113. IN PFILE_PATH FilePath,
  114. OUT PDISK_REGION *DiskRegion,
  115. OUT PWSTR *PartitionNtName,
  116. OUT PWSTR *PartitionRelativePath
  117. );
  118. #define ADD_OFFSET(_p,_o) (PVOID)((PUCHAR)(_p) + (_p)->_o)
  119. #endif
  120. //
  121. // Function implementation
  122. //
  123. BOOLEAN
  124. SpInitBootVars(
  125. )
  126. /*++
  127. Routine Description:
  128. Captures the state of the NVRAM Boot Variables.
  129. Arguments:
  130. None.
  131. Return Value:
  132. --*/
  133. {
  134. BOOLEAN Status = TRUE;
  135. BOOTVAR i;
  136. ULONG Component, MaxComponents, SysPartComponents;
  137. PCHAR puArcString; // SGI
  138. CLEAR_CLIENT_SCREEN();
  139. SpDisplayStatusText(SP_STAT_EXAMINING_FLEXBOOT,DEFAULT_STATUS_ATTRIBUTE);
  140. //
  141. // Initialize the boot variables from the corresponding NVRAM variables
  142. //
  143. #if defined(EFI_NVRAM_ENABLED)
  144. if (SpIsEfi()) {
  145. //
  146. // Build a list of all of the \Device\HarddiskN\PartitionM symbolic
  147. // links, along with their translations to \Device\HarddiskVolumeN
  148. // device names. This list is used to translate the
  149. // \Device\HarddiskVolumeN names returned by NtTranslateFilePath into
  150. // names that setupdd can translate to ARC names.
  151. //
  152. SpBuildHarddiskNameTranslations();
  153. //
  154. // Read the boot entries from NVRAM and convert them into our
  155. // internal format.
  156. //
  157. Status = SpReadAndConvertEfiBootEntries();
  158. } else
  159. #endif
  160. {
  161. if (SpIsArc()) {
  162. ULONG NumComponents;
  163. for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) {
  164. OldBootVars[i] = SppGetArcEnvVar( i );
  165. SpGetEnvVarWComponents( OldBootVars[i], BootVars + i, &NumComponents );
  166. }
  167. Timeout = DEFAULT_TIMEOUT;
  168. Default = NULL;
  169. #if defined(_AMD64_) || defined(_X86_)
  170. } else {
  171. Spx86InitBootVars( BootVars, &Default, &Timeout );
  172. #endif // defined(_AMD64_) || defined(_X86_)
  173. }
  174. //
  175. // We now go back and replace all NULL OsLoadOptions with "", because we
  176. // validate a boot set by making sure that all components are non-NULL.
  177. //
  178. // First, find the maximum number of components in any of the other
  179. // boot variables, so that we can make OsLoadOptions have this many.
  180. // (We also disregard SYSTEMPARTITION since some machines have this component
  181. // sitting all by itself on a new machine.)
  182. //
  183. MaxComponents = 0;
  184. for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) {
  185. if(i != OSLOADOPTIONS) {
  186. for(Component = 0; BootVars[i][Component]; Component++);
  187. if (i == SYSTEMPARTITION) {
  188. SysPartComponents = Component;
  189. } else if(Component > MaxComponents) {
  190. MaxComponents = Component;
  191. }
  192. }
  193. }
  194. if(SysPartComponents > MaxComponents) {
  195. CleanSysPartOrphan = TRUE;
  196. }
  197. for(Component = 0; BootVars[OSLOADOPTIONS][Component]; Component++);
  198. if(Component < MaxComponents) {
  199. //
  200. // Then we need to add empty strings to fill it out.
  201. //
  202. BootVars[OSLOADOPTIONS] = SpMemRealloc(BootVars[OSLOADOPTIONS],
  203. (MaxComponents + 1) * sizeof(PWSTR *));
  204. ASSERT(BootVars[OSLOADOPTIONS]);
  205. BootVars[OSLOADOPTIONS][MaxComponents] = NULL;
  206. for(; Component < MaxComponents; Component++) {
  207. BootVars[OSLOADOPTIONS][Component] = SpDupStringW(L"");
  208. }
  209. }
  210. //
  211. // Now convert the ARC boot sets into our internal format.
  212. //
  213. Status = SpConvertArcBootEntries(MaxComponents);
  214. }
  215. CLEAR_CLIENT_SCREEN();
  216. return ( Status );
  217. }
  218. BOOLEAN
  219. SpFlushBootVars(
  220. )
  221. /*++
  222. Routine Description:
  223. Updates the NVRAM variables / boot.ini
  224. from the current state of the boot variables.
  225. Arguments:
  226. Return Value:
  227. --*/
  228. {
  229. BOOLEAN Status, OldStatus;
  230. BOOTVAR i, iFailPoint;
  231. CHAR TimeoutValue[24];
  232. #if defined(EFI_NVRAM_ENABLED)
  233. if (SpIsEfi()) {
  234. //
  235. // This is an EFI machine. Write changed boot entries back to NVRAM.
  236. //
  237. Status = SpFlushEfiBootEntries();
  238. } else
  239. #endif
  240. {
  241. Status = FALSE;
  242. if (SpIsArc()) {
  243. //
  244. // Run through all the boot variables and set the corresponding
  245. // NVRAM variables
  246. for(OldStatus = TRUE, i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) {
  247. Status = SppSetArcEnvVar( i, BootVars[i], OldStatus );
  248. if(Status != OldStatus) {
  249. iFailPoint = i;
  250. OldStatus = Status;
  251. }
  252. }
  253. // if we failed in writing any of the variables, then restore everything we
  254. // modified back to its original state.
  255. if(!Status) {
  256. for(i = FIRSTBOOTVAR; i < iFailPoint; i++) {
  257. HalSetEnvironmentVariable(NvramVarNames[i], OldBootVars[i]);
  258. }
  259. }
  260. // Free all of the old boot variable strings
  261. for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) {
  262. SpMemFree(OldBootVars[i]);
  263. OldBootVars[i] = NULL;
  264. }
  265. //
  266. // Now set the timeout.
  267. //
  268. if(Status) {
  269. Status = FALSE;
  270. sprintf(TimeoutValue,"%u",Timeout);
  271. if((HalSetEnvironmentVariable("COUNTDOWN",TimeoutValue) == ESUCCESS)
  272. && (HalSetEnvironmentVariable("AUTOLOAD" ,"YES" ) == ESUCCESS))
  273. {
  274. Status = TRUE;
  275. }
  276. }
  277. #if defined(_AMD64_) || defined(_X86_)
  278. } else {
  279. Status = Spx86FlushBootVars( BootVars, Timeout, Default );
  280. #endif // defined(_AMD64_) || defined(_X86_)
  281. }
  282. }
  283. return( Status );
  284. }
  285. VOID
  286. SpFreeBootVars(
  287. )
  288. /*++
  289. Routine Description:
  290. To free any memory allocated and do other cleanup
  291. Arguments:
  292. None
  293. Return Value:
  294. None
  295. --*/
  296. {
  297. BOOTVAR i;
  298. //
  299. // Free internal-format boot entries.
  300. //
  301. SpFreeBootEntries();
  302. #if defined(EFI_NVRAM_ENABLED)
  303. if (!SpIsEfi())
  304. #endif
  305. {
  306. //
  307. // Go through the globals and free them
  308. //
  309. for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) {
  310. if( BootVars[i] ) {
  311. SpFreeEnvVarComponents( BootVars[i] );
  312. BootVars[i] = NULL;
  313. }
  314. }
  315. if ( Default ) {
  316. SpMemFree( Default );
  317. Default = NULL;
  318. }
  319. }
  320. return;
  321. }
  322. VOID
  323. SpAddBootSet(
  324. IN PWSTR *BootSet,
  325. IN BOOLEAN DefaultOS,
  326. IN ULONG Signature
  327. )
  328. /*++
  329. Routine Description:
  330. To add a new system to the installed system list. The system is added
  331. as the first bootset. If is found in the currently installed boot sets
  332. the boot set is extracted and shifted to position 0.
  333. Arguments:
  334. BootSet - A list of the boot variables to use.
  335. Default - Whether this system is to be the default system to boot.
  336. Return Value:
  337. Component list of the value of the boot variable.
  338. --*/
  339. {
  340. BOOTVAR i;
  341. ULONG MatchComponent, j;
  342. LONG k;
  343. BOOLEAN ValidBootSet, ComponentMatched;
  344. PWSTR Temp;
  345. ASSERT( !SpIsEfi() );
  346. //
  347. // Validate the BootSet passed in
  348. //
  349. for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) {
  350. ASSERT( BootSet[i] );
  351. }
  352. //
  353. // Examine all the boot sets and make sure we don't have a boot set
  354. // already matching. Note that we will compare all variables in
  355. // tandem. We are not interested in matches which are generated by
  356. // the variables not being in tandem because they are difficult to
  357. // shift around.
  358. //
  359. ValidBootSet = TRUE;
  360. ComponentMatched = FALSE;
  361. for( MatchComponent = 0;
  362. BootVars[OSLOADPARTITION][MatchComponent];
  363. MatchComponent++
  364. ) {
  365. //
  366. // Validate the boot set at the current component
  367. //
  368. for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) {
  369. ValidBootSet = ValidBootSet && BootVars[i][MatchComponent];
  370. }
  371. if( !ValidBootSet ) {
  372. break;
  373. }
  374. //
  375. // Valid Boot Set, compare the components against what we have in the
  376. // current BootSet
  377. //
  378. ComponentMatched = TRUE;
  379. for(i = FIRSTBOOTVAR; ComponentMatched && i <= LASTBOOTVAR; i++) {
  380. ComponentMatched = !_wcsicmp( BootSet[i], BootVars[i][MatchComponent] );
  381. }
  382. if( ComponentMatched ) {
  383. break;
  384. }
  385. }
  386. //
  387. // If component didn't match then prepend the BootSet to the boot sets
  388. // that currently exist. It is important to prepend the BootSet, because
  389. // appending the BootSet doesn't guarantee a matched BootSet in the
  390. // environment variables. If a match was found then we
  391. // have a cleanly matched set which can be exchanged with the first
  392. // one in the set.
  393. //
  394. if( ComponentMatched ) {
  395. // If the currently selected OS is to be the default:
  396. // Shift down all variables from position 0 to MatchComponent - 1
  397. // and store whatever was there at MatchComponent at position 0
  398. //
  399. if ( DefaultOS && MatchComponent != 0 ) {
  400. for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) {
  401. Temp = BootVars[i][MatchComponent];
  402. for( k = MatchComponent - 1; k >= 0; k-- ) {
  403. BootVars[i][k + 1] = BootVars[i][k];
  404. }
  405. BootVars[i][0] = Temp;
  406. }
  407. }
  408. }
  409. else {
  410. for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) {
  411. //
  412. // Find out the size of the current value
  413. //
  414. for(j = 0; BootVars[i][j]; j++) {
  415. }
  416. //
  417. // Realloc the current buffer to hold one more
  418. //
  419. BootVars[i] = SpMemRealloc( BootVars[i], (j + 1 + 1)*sizeof(PWSTR) );
  420. //
  421. // Shift all the variables down one and store the current value
  422. // at index 0;
  423. //
  424. for( k = j; k >= 0 ; k-- ) {
  425. BootVars[i][k+1] = BootVars[i][k];
  426. }
  427. BootVars[i][0] = SpDupStringW( BootSet[i] );
  428. ASSERT( BootVars[i][0] );
  429. }
  430. }
  431. //
  432. // If this has been indicated as the default then set this to be the
  433. // default OS after freeing the current default variable
  434. //
  435. if( DefaultOS ) {
  436. if( Default ) {
  437. SpMemFree( Default );
  438. }
  439. Default = SpMemAlloc( MAX_PATH * sizeof(WCHAR) );
  440. ASSERT( Default );
  441. wcscpy( Default, BootSet[OSLOADPARTITION] );
  442. wcscat( Default, BootSet[OSLOADFILENAME] );
  443. DefaultSignature = Signature;
  444. }
  445. return;
  446. }
  447. VOID
  448. SpDeleteBootSet(
  449. IN PWSTR *BootSet,
  450. OUT PWSTR *OldOsLoadOptions OPTIONAL
  451. )
  452. /*++
  453. Routine Description:
  454. To delete all boot sets in the list matching the boot set provided.
  455. Note that the information to use in comparing the bootset is provided
  456. by selectively providing fields in the boot set. So in the boot set
  457. if the system partition is not provided it is not used in the comparison
  458. to see if the boot sets match. By providing all NULL members we can
  459. delete all the boot sets currently present.
  460. Arguments:
  461. BootSet - A list of the boot variables to use.
  462. Return Value:
  463. None.
  464. --*/
  465. {
  466. ULONG Component, j;
  467. BOOLEAN ValidBootSet, ComponentMatched;
  468. BOOTVAR i;
  469. PWSTR OsPartPath;
  470. ASSERT( !SpIsEfi() );
  471. Component = 0;
  472. while(TRUE) {
  473. //
  474. // See if we have any boot sets left, if none left we are done
  475. //
  476. ValidBootSet = TRUE;
  477. for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) {
  478. ValidBootSet = ValidBootSet && BootVars[i][Component];
  479. }
  480. if( !ValidBootSet ) {
  481. break;
  482. }
  483. //
  484. // Valid Boot Set, compare the components against what we have in the
  485. // current BootSet. Use only members of the BootSet which are not NULL
  486. //
  487. ComponentMatched = TRUE;
  488. for(i = FIRSTBOOTVAR; ComponentMatched && i <= LASTBOOTVAR; i++) {
  489. if( BootSet[i] ) {
  490. if((i == OSLOADPARTITION) ||
  491. (i == SYSTEMPARTITION)) {
  492. //
  493. // Then we may have a boot set existing in tertiary ARC path form, so
  494. // we first translate this path to a primary or secondary ARC path.
  495. //
  496. OsPartPath = SpArcPathFromBootSet(i, Component);
  497. ComponentMatched = !_wcsicmp( BootSet[i], OsPartPath );
  498. SpMemFree(OsPartPath);
  499. } else {
  500. ComponentMatched = !_wcsicmp( BootSet[i], BootVars[i][Component] );
  501. }
  502. }
  503. }
  504. if( (ComponentMatched)
  505. #ifdef PRERELEASE
  506. //
  507. // If we're being asked to delete a boot entry, and this
  508. // isn't the *exact* entry (i.e. it's a duplicate) that
  509. // also has some private OSLOADOPTIONS, then keep it around.
  510. //
  511. && !( wcsstr(BootVars[OSLOADOPTIONS][Component], L"/kernel") ||
  512. wcsstr(BootVars[OSLOADOPTIONS][Component], L"/hal") ||
  513. wcsstr(BootVars[OSLOADOPTIONS][Component], L"/pae") ||
  514. wcsstr(BootVars[OSLOADOPTIONS][Component], L"/sos") )
  515. #endif
  516. ) {
  517. //
  518. // Delete all the values in the current component and advance
  519. // all the other components one index up
  520. //
  521. for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) {
  522. if((i == OSLOADOPTIONS) && OldOsLoadOptions && !(*OldOsLoadOptions)) {
  523. //
  524. // If we've been passed a pointer to OldOsLoadOptions,
  525. // and haven't previously found a pertinent entry, then
  526. // save this one
  527. //
  528. *OldOsLoadOptions = BootVars[i][Component];
  529. } else {
  530. SpMemFree(BootVars[i][Component]);
  531. }
  532. j = Component;
  533. do {
  534. BootVars[i][j] = BootVars[i][j+1];
  535. j++;
  536. } while(BootVars[i][j] != NULL);
  537. }
  538. }
  539. else {
  540. Component++;
  541. }
  542. }
  543. return;
  544. }
  545. VOID
  546. SpCleanSysPartOrphan(
  547. VOID
  548. )
  549. {
  550. INT Component, Orphan;
  551. BOOLEAN DupFound;
  552. PWSTR NormalizedArcPath;
  553. if(!CleanSysPartOrphan) {
  554. return;
  555. }
  556. ASSERT( !SpIsEfi() );
  557. //
  558. // find the last SystemPartition entry
  559. //
  560. for(Orphan = 0; BootVars[SYSTEMPARTITION][Orphan]; Orphan++);
  561. //
  562. // it's position better be > 0, otherwise, just exit
  563. //
  564. if(Orphan < 2) {
  565. return;
  566. } else {
  567. NormalizedArcPath = SpNormalizeArcPath(BootVars[SYSTEMPARTITION][--Orphan]);
  568. }
  569. //
  570. // Make sure that this component is duplicated somewhere else in the
  571. // SystemPartition list.
  572. //
  573. for(Component = Orphan - 1, DupFound = FALSE;
  574. ((Component >= 0) && !DupFound);
  575. Component--)
  576. {
  577. DupFound = !_wcsicmp(NormalizedArcPath, BootVars[SYSTEMPARTITION][Component]);
  578. }
  579. if(DupFound) {
  580. SpMemFree(BootVars[SYSTEMPARTITION][Orphan]);
  581. BootVars[SYSTEMPARTITION][Orphan] = NULL;
  582. }
  583. SpMemFree(NormalizedArcPath);
  584. }
  585. PWSTR
  586. SpArcPathFromBootSet(
  587. IN BOOTVAR BootVariable,
  588. IN ULONG Component
  589. )
  590. /*++
  591. Routine Description:
  592. Given the index of a boot set, return the primary (multi) or
  593. secondary ("absolute" scsi) ARC path for the specified variable.
  594. This takes into account the NT 3.1 case where we had 'tertiary'
  595. ARC paths where a relative scsi ordinal was passed in via the
  596. /scsiordinal switch.
  597. Arguments:
  598. BootVariable - supplies the index of the variable we want to return.
  599. Component - supplies the index of the boot set to use.
  600. Return Value:
  601. String representing the primary or secondary ARC path. This string
  602. must be freed by the caller with SpMemFree.
  603. --*/
  604. {
  605. ASSERT( !SpIsEfi() );
  606. if(!SpIsArc()){
  607. PWSTR p = NULL, q = NULL, ReturnedPath = NULL, RestOfString;
  608. WCHAR ForceOrdinalSwitch[] = L"/scsiordinal:";
  609. WCHAR ScsiPrefix[] = L"scsi(";
  610. WCHAR OrdinalString[11];
  611. ULONG ScsiOrdinal, PrefixLength;
  612. //
  613. // Check to see if this boot set had the /scsiordinal option switch
  614. //
  615. if(BootVars[OSLOADOPTIONS][Component]) {
  616. wcscpy(TemporaryBuffer, BootVars[OSLOADOPTIONS][Component]);
  617. SpStringToLower(TemporaryBuffer);
  618. if(p = wcsstr(TemporaryBuffer, ForceOrdinalSwitch)) {
  619. p += sizeof(ForceOrdinalSwitch)/sizeof(WCHAR) - 1;
  620. if(!(*p)) {
  621. p = NULL;
  622. }
  623. }
  624. }
  625. if(p) {
  626. //
  627. // We have found a scsiordinal, so use it
  628. //
  629. ScsiOrdinal = SpStringToLong(p, &RestOfString, 10);
  630. wcscpy(TemporaryBuffer, BootVars[BootVariable][Component]);
  631. SpStringToLower(TemporaryBuffer);
  632. if(p = wcsstr(TemporaryBuffer, ScsiPrefix)) {
  633. p += sizeof(ScsiPrefix)/sizeof(WCHAR) - 1;
  634. if(*p) {
  635. q = wcschr(p, L')');
  636. } else {
  637. p = NULL;
  638. }
  639. }
  640. if(q) {
  641. //
  642. // build the new secondary ARC path
  643. //
  644. swprintf(OrdinalString, L"%u", ScsiOrdinal);
  645. PrefixLength = (ULONG)(p - TemporaryBuffer);
  646. ReturnedPath = SpMemAlloc((PrefixLength + wcslen(OrdinalString) + wcslen(q) + 1)
  647. * sizeof(WCHAR)
  648. );
  649. wcsncpy(ReturnedPath, TemporaryBuffer, PrefixLength);
  650. ReturnedPath[PrefixLength] = L'\0';
  651. wcscat(ReturnedPath, OrdinalString);
  652. wcscat(ReturnedPath, q);
  653. }
  654. }
  655. if(!ReturnedPath) {
  656. //
  657. // We didn't find a scsiordinal, this is a multi-style path, or
  658. // there was some problem, so just use the boot variable as-is.
  659. //
  660. ReturnedPath = SpDupStringW(BootVars[BootVariable][Component]);
  661. }
  662. return ReturnedPath;
  663. } else {
  664. //
  665. // Nothing to do on ARC machines.
  666. //
  667. return SpDupStringW(BootVars[BootVariable][Component]);
  668. }
  669. }
  670. #if defined(REMOTE_BOOT)
  671. BOOLEAN
  672. SpFlushRemoteBootVars(
  673. IN PDISK_REGION TargetRegion
  674. )
  675. {
  676. #if defined(EFI_NVRAM_ENABLED)
  677. if (SpIsEfi()) {
  678. //
  679. // Insert EFI code here.
  680. //
  681. return FALSE;
  682. } else
  683. #endif
  684. {
  685. if (SpIsArc()) {
  686. //
  687. // Insert ARC code here.
  688. //
  689. return FALSE;
  690. #if defined(_AMD64_) || defined(_X86_)
  691. } else {
  692. return Spx86FlushRemoteBootVars( TargetRegion, BootVars, Default );
  693. #endif // defined(_AMD64_) || defined(_X86_)
  694. }
  695. }
  696. }
  697. #endif // defined(REMOTE_BOOT)
  698. BOOLEAN
  699. SppSetArcEnvVar(
  700. IN BOOTVAR Variable,
  701. IN PWSTR *VarComponents,
  702. IN BOOLEAN bWriteVar
  703. )
  704. /*++
  705. Routine Description:
  706. Set the value of the arc environment variable
  707. Arguments:
  708. VarName - supplies the name of the arc environment variable
  709. whose value is to be set.
  710. VarComponents - Set of components of the variable value to be set
  711. bWriteVar - if TRUE, then write the variable to nvram, otherwise
  712. just return FALSE (having put the first component in NewBootVars).
  713. Return Value:
  714. TRUE if values were written to nvram / FALSE otherwise
  715. --*/
  716. {
  717. ULONG Length, NBVLen, i;
  718. PWSTR Temp;
  719. PUCHAR Value;
  720. ARC_STATUS ArcStatus;
  721. ASSERT( !SpIsEfi() );
  722. if( VarComponents == NULL ) {
  723. Temp = SpDupStringW( L"" );
  724. NewBootVars[Variable] = SpDupStringW( L"" );
  725. }
  726. else {
  727. for( i = 0, Length = 0; VarComponents[i]; i++ ) {
  728. Length = Length + (wcslen(VarComponents[i]) + 1) * sizeof(WCHAR);
  729. if(i == 0) {
  730. NBVLen = Length; // we just want to store the first component
  731. }
  732. }
  733. Temp = SpMemAlloc( Length );
  734. ASSERT( Temp );
  735. wcscpy( Temp, L"" );
  736. NewBootVars[Variable] = SpMemAlloc( NBVLen );
  737. ASSERT( NewBootVars[Variable] );
  738. wcscpy( NewBootVars[Variable], L"" );
  739. for( i = 0; VarComponents[i]; i++ ) {
  740. wcscat( Temp, VarComponents[i] );
  741. if( VarComponents[i + 1] ) {
  742. wcscat( Temp, L";" );
  743. }
  744. if(i == 0) {
  745. wcscat( NewBootVars[Variable], VarComponents[i]);
  746. }
  747. }
  748. }
  749. if(bWriteVar) {
  750. Value = SpToOem( Temp );
  751. ArcStatus = HalSetEnvironmentVariable( NvramVarNames[ Variable ], Value );
  752. SpMemFree( Value );
  753. } else {
  754. ArcStatus = ENOMEM;
  755. }
  756. SpMemFree( Temp );
  757. return ( ArcStatus == ESUCCESS );
  758. }
  759. #ifdef _X86_
  760. BOOLEAN
  761. SpIsArc(
  762. VOID
  763. )
  764. /*++
  765. Routine Description:
  766. Run time check to determine if this is an Arc system. We attempt to read an
  767. Arc variable using the Hal. This will fail for Bios based systems.
  768. Arguments:
  769. None
  770. Return Value:
  771. True = This is an Arc system.
  772. --*/
  773. {
  774. #define BUFFERLENGTH 512
  775. ARC_STATUS ArcStatus = EBADF;
  776. UCHAR *buf;
  777. if (IsArcChecked) {
  778. return IsArcMachine;
  779. }
  780. IsArcChecked = TRUE;
  781. IsArcMachine = FALSE;
  782. //
  783. // Get the env var into the temp buffer.
  784. //
  785. buf = SpMemAlloc( BUFFERLENGTH );
  786. if( buf ) {
  787. ArcStatus = HalGetEnvironmentVariable(
  788. NvramVarNames[ OSLOADER ],
  789. BUFFERLENGTH, //sizeof(TemporaryBuffer),
  790. buf //(PUCHAR)TemporaryBuffer
  791. );
  792. SpMemFree( buf );
  793. }
  794. if (ArcStatus == ESUCCESS) {
  795. IsArcMachine = TRUE;
  796. }
  797. return IsArcMachine;
  798. }
  799. #endif
  800. VOID
  801. SpFreeBootEntries (
  802. VOID
  803. )
  804. /*++
  805. Routine Description:
  806. Frees memory used to hold internal-format boot entries and options.
  807. Arguments:
  808. None.
  809. Return Value:
  810. None.
  811. --*/
  812. {
  813. PSP_BOOT_ENTRY bootEntry;
  814. //
  815. // Free boot options. These will only be allocated on EFI machines.
  816. //
  817. if (SpBootOptions != NULL) {
  818. ASSERT(SpIsEfi());
  819. SpMemFree(SpBootOptions);
  820. SpBootOptions = NULL;
  821. }
  822. //
  823. // Free internal-format boot entries. These will be allocated on all
  824. // machines.
  825. //
  826. while (SpBootEntries != NULL) {
  827. bootEntry = SpBootEntries;
  828. SpBootEntries = bootEntry->Next;
  829. //
  830. // Space for some fields is allocated with the base structure.
  831. // If a fields address indicates that it was allocated with the
  832. // base structure, don't try to free it.
  833. //
  834. #define IS_SEPARATE_ALLOCATION(_p) \
  835. ((bootEntry->_p != NULL) && \
  836. (((PUCHAR)bootEntry->_p < (PUCHAR)bootEntry) || \
  837. ((PUCHAR)bootEntry->_p > (PUCHAR)bootEntry->AllocationEnd)))
  838. #define FREE_IF_SEPARATE_ALLOCATION(_p) \
  839. if (IS_SEPARATE_ALLOCATION(_p)) { \
  840. SpMemFree(bootEntry->_p); \
  841. }
  842. FREE_IF_SEPARATE_ALLOCATION(FriendlyName);
  843. FREE_IF_SEPARATE_ALLOCATION(OsLoadOptions);
  844. FREE_IF_SEPARATE_ALLOCATION(LoaderPath);
  845. FREE_IF_SEPARATE_ALLOCATION(LoaderPartitionNtName);
  846. FREE_IF_SEPARATE_ALLOCATION(LoaderFile);
  847. FREE_IF_SEPARATE_ALLOCATION(OsPath);
  848. FREE_IF_SEPARATE_ALLOCATION(OsPartitionNtName);
  849. FREE_IF_SEPARATE_ALLOCATION(OsDirectory);
  850. FREE_IF_SEPARATE_ALLOCATION(Pid20Array);
  851. SpMemFree(bootEntry);
  852. }
  853. ASSERT(SpBootEntries == NULL);
  854. return;
  855. } // SpFreeBootEntries
  856. PCHAR
  857. SppGetArcEnvVar(
  858. IN BOOTVAR Variable
  859. )
  860. /*++
  861. Routine Description:
  862. Query the value of an ARC environment variable.
  863. A buffer will be returned in all cases -- if the variable does not exist,
  864. the buffer will be empty.
  865. Arguments:
  866. VarName - supplies the name of the arc environment variable
  867. whose value is desired.
  868. Return Value:
  869. Buffer containing value of the environemnt variable.
  870. The caller must free this buffer with SpMemFree.
  871. --*/
  872. {
  873. ARC_STATUS ArcStatus;
  874. ASSERT( !SpIsEfi() );
  875. //
  876. // Get the env var into the temp buffer.
  877. //
  878. ArcStatus = HalGetEnvironmentVariable(
  879. NvramVarNames[ Variable ],
  880. sizeof(TemporaryBuffer),
  881. (PCHAR) TemporaryBuffer
  882. );
  883. if(ArcStatus != ESUCCESS) {
  884. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: arc status %u getting env var %s\n",ArcStatus,NvramVarNames[Variable]));
  885. //
  886. // return empty buffer.
  887. //
  888. TemporaryBuffer[0] = 0;
  889. }
  890. return(SpDupString((PCHAR)TemporaryBuffer));
  891. }
  892. #ifdef _X86_
  893. //
  894. // NEC98
  895. //
  896. BOOLEAN
  897. SpReInitializeBootVars_Nec98(
  898. VOID
  899. )
  900. {
  901. return SppReInitializeBootVars_Nec98( BootVars, &Default, &Timeout );
  902. }
  903. #endif
  904. PWSTR
  905. SpGetDefaultBootEntry (
  906. OUT UINT *DefaultSignatureOut
  907. )
  908. {
  909. *DefaultSignatureOut = DefaultSignature;
  910. return Default;
  911. }
  912. VOID
  913. SpDetermineUniqueAndPresentBootEntries(
  914. VOID
  915. )
  916. /*++
  917. Routine Description:
  918. This routine goes through the list of NT boot entries and marks all
  919. such entries that are both unique and present.
  920. Arguments:
  921. None. This routine modifies entries in the SpBootEntries list as
  922. appropriate.
  923. Return Value:
  924. None.
  925. --*/
  926. {
  927. PSP_BOOT_ENTRY BootEntry;
  928. PSP_BOOT_ENTRY BootEntry2;
  929. //
  930. // Initialize
  931. //
  932. CLEAR_CLIENT_SCREEN();
  933. SpDisplayStatusText(SP_STAT_LOOKING_FOR_WINNT,DEFAULT_STATUS_ATTRIBUTE);
  934. //
  935. // Go through all the matched boot sets and find out which NTs are
  936. // upgradeable/repairable. The criteria here are:
  937. //
  938. // 1. The system partition should exist and be valid.
  939. // 2. The OS load partition should exist.
  940. // 3. An NT should exist in <OSLoadPartition><OsDirectory>.
  941. // 4. OsLoadPartition should be a non-FT partition, or it should be a
  942. // member 0 of a mirror.
  943. //
  944. for (BootEntry = SpBootEntries; BootEntry != NULL; BootEntry = BootEntry->Next) {
  945. //
  946. // Initialize to false.
  947. //
  948. BootEntry->Processable = FALSE;
  949. //
  950. // If this entry has been deleted or is not an NT boot entry, skip it.
  951. //
  952. if (!IS_BOOT_ENTRY_WINDOWS(BootEntry) || IS_BOOT_ENTRY_DELETED(BootEntry)) {
  953. continue;
  954. }
  955. //
  956. // Check if the system and OS partitions are present and valid.
  957. //
  958. if ((BootEntry->LoaderPartitionDiskRegion == NULL) ||
  959. (BootEntry->OsPartitionDiskRegion == NULL)) {
  960. continue;
  961. }
  962. if (!BootEntry->LoaderPartitionDiskRegion->PartitionedSpace) {
  963. continue;
  964. }
  965. //
  966. // Check whether this directory has been covered before in the
  967. // boot entry list. This happens when multiple boot entries point
  968. // at the same tree. The comparison is done based on the system
  969. // partition region, the OS partition region, and the OS directory.
  970. //
  971. for ( BootEntry2 = SpBootEntries; BootEntry2 != BootEntry; BootEntry2 = BootEntry2->Next ) {
  972. if ((BootEntry->LoaderPartitionDiskRegion == BootEntry2->LoaderPartitionDiskRegion) &&
  973. (BootEntry->OsPartitionDiskRegion == BootEntry2->OsPartitionDiskRegion) &&
  974. (_wcsicmp(BootEntry->OsDirectory, BootEntry2->OsDirectory) == 0)) {
  975. break;
  976. }
  977. }
  978. if (BootEntry != BootEntry2) {
  979. //
  980. // This entry duplicates a previous entry. Skip it.
  981. //
  982. continue;
  983. }
  984. //
  985. // This boot entry is the first one to point to this OS directory.
  986. // Check whether an NT installation is actually present there.
  987. //
  988. if (SpIsNtInDirectory(BootEntry->OsPartitionDiskRegion, BootEntry->OsDirectory)
  989. // && !BootEntry->OsPartitionDiskRegion->FtPartition
  990. ) {
  991. }
  992. BootEntry->Processable = TRUE;
  993. }
  994. CLEAR_CLIENT_SCREEN();
  995. return;
  996. }
  997. VOID
  998. SpRemoveInstallationFromBootList(
  999. IN PDISK_REGION SysPartitionRegion, OPTIONAL
  1000. IN PDISK_REGION NtPartitionRegion, OPTIONAL
  1001. IN PWSTR SysRoot, OPTIONAL
  1002. IN PWSTR SystemLoadIdentifier, OPTIONAL
  1003. IN PWSTR SystemLoadOptions, OPTIONAL
  1004. IN ENUMARCPATHTYPE ArcPathType,
  1005. #if defined(REMOTE_BOOT)
  1006. IN BOOLEAN RemoteBootPath,
  1007. #endif // defined(REMOTE_BOOT)
  1008. OUT PWSTR *OldOsLoadOptions OPTIONAL
  1009. )
  1010. {
  1011. PWSTR BootSet[MAXBOOTVARS];
  1012. PWSTR TempSysRoot = NULL;
  1013. PWSTR FirstBackslash;
  1014. BOOTVAR i;
  1015. WCHAR Drive[] = L"?:";
  1016. PWSTR tmp2;
  1017. PSP_BOOT_ENTRY bootEntry;
  1018. //
  1019. // Tell the user what we are doing.
  1020. //
  1021. CLEAR_CLIENT_SCREEN();
  1022. SpDisplayStatusText(SP_STAT_CLEANING_FLEXBOOT,DEFAULT_STATUS_ATTRIBUTE);
  1023. //
  1024. // Find all boot entries that match the input specifications, and mark
  1025. // them for deletion.
  1026. //
  1027. for (bootEntry = SpBootEntries; bootEntry != NULL; bootEntry = bootEntry->Next) {
  1028. ASSERT(bootEntry->FriendlyName != NULL);
  1029. if (IS_BOOT_ENTRY_WINDOWS(bootEntry)) {
  1030. ASSERT(bootEntry->OsLoadOptions != NULL);
  1031. }
  1032. if (IS_BOOT_ENTRY_WINDOWS(bootEntry) &&
  1033. !IS_BOOT_ENTRY_DELETED(bootEntry) &&
  1034. ((SysPartitionRegion == NULL) ||
  1035. (bootEntry->LoaderPartitionDiskRegion == SysPartitionRegion)) &&
  1036. ((NtPartitionRegion == NULL) ||
  1037. (bootEntry->OsPartitionDiskRegion == NtPartitionRegion)) &&
  1038. ((SysRoot == NULL) ||
  1039. ((bootEntry->OsDirectory != NULL) &&
  1040. (_wcsicmp(bootEntry->OsDirectory, SysRoot) == 0))) &&
  1041. ((SystemLoadIdentifier == NULL) ||
  1042. (_wcsicmp(bootEntry->FriendlyName, SystemLoadIdentifier) == 0)) &&
  1043. ((SystemLoadOptions == NULL) ||
  1044. (_wcsicmp(bootEntry->OsLoadOptions, SystemLoadOptions) == 0))) {
  1045. bootEntry->Status |= BE_STATUS_DELETED;
  1046. if ((OldOsLoadOptions != NULL) && (*OldOsLoadOptions == NULL)) {
  1047. *OldOsLoadOptions = SpDupStringW(bootEntry->OsLoadOptions);
  1048. }
  1049. }
  1050. }
  1051. //
  1052. // If not on an EFI machine, then also delete matching ARC boot sets.
  1053. //
  1054. if (!SpIsEfi()) {
  1055. //
  1056. // Set up the boot set
  1057. //
  1058. for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) {
  1059. BootSet[i] = NULL;
  1060. }
  1061. tmp2 = TemporaryBuffer + (sizeof(TemporaryBuffer) / sizeof(WCHAR) / 2);
  1062. if( NtPartitionRegion ) {
  1063. SpArcNameFromRegion(NtPartitionRegion,tmp2,sizeof(TemporaryBuffer)/2,PartitionOrdinalOnDisk,ArcPathType);
  1064. BootSet[OSLOADPARTITION] = SpDupStringW(tmp2);
  1065. }
  1066. if( SysPartitionRegion ) {
  1067. SpArcNameFromRegion(SysPartitionRegion,tmp2,sizeof(TemporaryBuffer)/2,PartitionOrdinalOnDisk,ArcPathType);
  1068. BootSet[SYSTEMPARTITION] = SpDupStringW(tmp2);
  1069. }
  1070. BootSet[OSLOADFILENAME] = SysRoot;
  1071. BootSet[LOADIDENTIFIER] = SystemLoadIdentifier;
  1072. BootSet[OSLOADOPTIONS] = SystemLoadOptions;
  1073. #if defined(REMOTE_BOOT)
  1074. //
  1075. // If this is a remote boot path, then move anything in OSLOADPARTITION
  1076. // after (and including) the first backslash over to the OSLOADFILENAME --
  1077. // this is the way that boot.ini is parsed when it is read, so it will
  1078. // allow SpDeleteBootSet to match it properly.
  1079. //
  1080. if (RemoteBootPath && NtPartitionRegion &&
  1081. (FirstBackslash = wcschr(BootSet[OSLOADPARTITION], L'\\'))) {
  1082. wcscpy(tmp2, FirstBackslash);
  1083. wcscat(tmp2, SysRoot);
  1084. TempSysRoot = SpDupStringW(tmp2);
  1085. BootSet[OSLOADFILENAME] = TempSysRoot;
  1086. *FirstBackslash = L'\0'; // truncate BootSet[OSLOADPARTITION]
  1087. }
  1088. #endif // defined(REMOTE_BOOT)
  1089. //
  1090. // Delete the boot set
  1091. //
  1092. SpDeleteBootSet(BootSet, OldOsLoadOptions);
  1093. //
  1094. // To take care of the case where the OSLOADPARTITION is a DOS drive letter
  1095. // in the boot set, change the OSLOADPARTITION to a drive and retry
  1096. // deletion
  1097. //
  1098. if( BootSet[OSLOADPARTITION] != NULL ) {
  1099. SpMemFree(BootSet[OSLOADPARTITION]);
  1100. }
  1101. if( NtPartitionRegion && (ULONG)(Drive[0] = NtPartitionRegion->DriveLetter) != 0) {
  1102. BootSet[OSLOADPARTITION] = Drive;
  1103. SpDeleteBootSet(BootSet, OldOsLoadOptions);
  1104. }
  1105. #if defined(_AMD64_) || defined(_X86_)
  1106. //
  1107. // If OldOsLoadOptions contains "/scsiordinal:", then remove it
  1108. //
  1109. if( ( OldOsLoadOptions != NULL ) &&
  1110. ( *OldOsLoadOptions != NULL ) ) {
  1111. PWSTR p, q;
  1112. WCHAR SaveChar;
  1113. SpStringToLower(*OldOsLoadOptions);
  1114. p = wcsstr( *OldOsLoadOptions, L"/scsiordinal:" );
  1115. if( p != NULL ) {
  1116. SaveChar = *p;
  1117. *p = (WCHAR)'\0';
  1118. wcscpy(TemporaryBuffer, *OldOsLoadOptions);
  1119. *p = SaveChar;
  1120. q = wcschr( p, (WCHAR)' ' );
  1121. if( q != NULL ) {
  1122. wcscat( TemporaryBuffer, q );
  1123. }
  1124. SpMemFree( *OldOsLoadOptions );
  1125. *OldOsLoadOptions = SpDupStringW( ( PWSTR )TemporaryBuffer );
  1126. }
  1127. }
  1128. #endif // defined(_AMD64_) || defined(_X86_)
  1129. //
  1130. // Cleanup
  1131. //
  1132. if( BootSet[SYSTEMPARTITION] != NULL ) {
  1133. SpMemFree(BootSet[SYSTEMPARTITION]);
  1134. }
  1135. if (TempSysRoot != NULL) {
  1136. SpMemFree(TempSysRoot);
  1137. }
  1138. }
  1139. return;
  1140. }
  1141. VOID
  1142. SpAddInstallationToBootList(
  1143. IN PVOID SifHandle,
  1144. IN PDISK_REGION SystemPartitionRegion,
  1145. IN PWSTR SystemPartitionDirectory,
  1146. IN PDISK_REGION NtPartitionRegion,
  1147. IN PWSTR Sysroot,
  1148. IN BOOLEAN BaseVideoOption,
  1149. IN PWSTR OldOsLoadOptions OPTIONAL
  1150. )
  1151. /*++
  1152. Routine Description:
  1153. Construct a boot set for the given installation
  1154. parameters and add it to the current boot list.
  1155. Perform modifications to the os load options if
  1156. necessary.
  1157. Notes: if this code changes, please ensure that
  1158. SpAddUserDefinedInstallationToBootList()
  1159. stays in sync if appropriate.
  1160. --*/
  1161. {
  1162. PWSTR BootVars[MAXBOOTVARS];
  1163. PWSTR SystemPartitionArcName;
  1164. PWSTR TargetPartitionArcName;
  1165. PWSTR tmp;
  1166. PWSTR tmp2;
  1167. PWSTR SifKeyName;
  1168. ULONG Signature;
  1169. BOOLEAN AddBaseVideo = FALSE;
  1170. WCHAR BaseVideoString[] = L"/basevideo";
  1171. WCHAR BaseVideoSosString[] = L"/sos";
  1172. BOOLEAN AddSosToBaseVideoString;
  1173. HEADLESS_RSP_QUERY_INFO Response;
  1174. WCHAR HeadlessRedirectString[] = L"/redirect";
  1175. #if defined(_AMD64_) || defined(_X86_)
  1176. WCHAR BootFastString[] = L"/fastdetect";
  1177. BOOLEAN AddBootFastString = TRUE;
  1178. #endif // defined(_AMD64_) || defined(_X86_)
  1179. ENUMARCPATHTYPE ArcPathType = PrimaryArcPath;
  1180. WCHAR HalString[] = L"/hal=";
  1181. BOOLEAN OldOsLoadOptionsReplaced;
  1182. NTSTATUS Status;
  1183. SIZE_T Length;
  1184. PWSTR LoadOptions;
  1185. PWSTR LoadIdentifier;
  1186. //
  1187. // Tell the user what we are doing.
  1188. //
  1189. CLEAR_CLIENT_SCREEN();
  1190. SpDisplayStatusText(SP_STAT_INITING_FLEXBOOT,DEFAULT_STATUS_ATTRIBUTE);
  1191. OldOsLoadOptionsReplaced = FALSE;
  1192. if( OldOsLoadOptions ) {
  1193. PWSTR p;
  1194. tmp = SpDupStringW( OldOsLoadOptions );
  1195. if (tmp) {
  1196. SpStringToLower(tmp);
  1197. if( p = wcsstr(tmp, HalString) ) { // found /hal=
  1198. WCHAR SaveChar;
  1199. PWSTR q;
  1200. SaveChar = *p;
  1201. *p = L'\0';
  1202. wcscpy( TemporaryBuffer, OldOsLoadOptions );
  1203. q = TemporaryBuffer + wcslen( tmp );
  1204. *q = L'\0';
  1205. Length = wcslen( tmp );
  1206. *p = SaveChar;
  1207. for( ; *p && (*p != L' '); p++ ) {
  1208. Length++;
  1209. }
  1210. for( ; *p && (*p == L' '); p++ ) {
  1211. Length++;
  1212. }
  1213. if( *p ) {
  1214. wcscat( TemporaryBuffer, OldOsLoadOptions+Length );
  1215. }
  1216. OldOsLoadOptions = SpDupStringW( TemporaryBuffer );
  1217. OldOsLoadOptionsReplaced = TRUE;
  1218. }
  1219. SpMemFree( tmp );
  1220. }
  1221. }
  1222. tmp2 = TemporaryBuffer + (sizeof(TemporaryBuffer) / sizeof(WCHAR) / 2);
  1223. if (!SpIsEfi()) {
  1224. //
  1225. // Get an ARC name for the system partition.
  1226. //
  1227. if (SystemPartitionRegion != NULL) {
  1228. SpArcNameFromRegion(
  1229. SystemPartitionRegion,
  1230. tmp2,
  1231. sizeof(TemporaryBuffer)/2,
  1232. PartitionOrdinalOnDisk,
  1233. PrimaryArcPath
  1234. );
  1235. SystemPartitionArcName = SpDupStringW(tmp2);
  1236. } else {
  1237. SystemPartitionArcName = NULL;
  1238. }
  1239. //
  1240. // Get an ARC name for the target partition.
  1241. //
  1242. //
  1243. // If the partition is on a SCSI disk that has more than 1024 cylinders
  1244. // and the partition has sectors located on cylinders beyond cylinder
  1245. // 1024, the get the arc name in the secondary format. See also
  1246. // spcopy.c!SpCreateNtbootddSys().
  1247. //
  1248. if(
  1249. !SpIsArc() &&
  1250. #if defined(REMOTE_BOOT)
  1251. !RemoteBootSetup &&
  1252. #endif // defined(REMOTE_BOOT)
  1253. #if defined(_AMD64_) || defined(_X86_)
  1254. !SpUseBIOSToBoot(NtPartitionRegion, NULL, SifHandle) &&
  1255. #endif // defined(_AMD64_) || defined(_X86_)
  1256. (HardDisks[NtPartitionRegion->DiskNumber].ScsiMiniportShortname[0]) ) {
  1257. ArcPathType = SecondaryArcPath;
  1258. } else {
  1259. ArcPathType = PrimaryArcPath;
  1260. }
  1261. SpArcNameFromRegion(
  1262. NtPartitionRegion,
  1263. tmp2,
  1264. sizeof(TemporaryBuffer)/2,
  1265. PartitionOrdinalOnDisk,
  1266. ArcPathType
  1267. );
  1268. TargetPartitionArcName = SpDupStringW(tmp2);
  1269. }
  1270. //
  1271. // OSLOADOPTIONS is specified in the setup information file.
  1272. //
  1273. tmp = SpGetSectionKeyIndex(
  1274. WinntSifHandle,
  1275. SIF_SETUPDATA,
  1276. SIF_OSLOADOPTIONSVAR,
  1277. 0
  1278. );
  1279. if (tmp == NULL) {
  1280. tmp = SpGetSectionKeyIndex(
  1281. SifHandle,
  1282. SIF_SETUPDATA,
  1283. SIF_OSLOADOPTIONSVAR,
  1284. 0
  1285. );
  1286. }
  1287. //
  1288. // If OsLoadOptionsVar wasn't specified, then we'll preserve any flags
  1289. // the user had specified.
  1290. //
  1291. if(!tmp && OldOsLoadOptions) {
  1292. tmp = OldOsLoadOptions;
  1293. }
  1294. AddSosToBaseVideoString = BaseVideoOption;
  1295. AddBaseVideo = BaseVideoOption;
  1296. if(tmp) {
  1297. //
  1298. // make sure we don't already have a /basevideo option, so we
  1299. // won't add another
  1300. //
  1301. wcscpy(TemporaryBuffer, tmp);
  1302. SpStringToLower(TemporaryBuffer);
  1303. if(wcsstr(TemporaryBuffer, BaseVideoString)) { // already have /basevideo
  1304. BaseVideoOption = TRUE;
  1305. AddBaseVideo = FALSE;
  1306. }
  1307. if(wcsstr(TemporaryBuffer, BaseVideoSosString)) { // already have /sos
  1308. AddSosToBaseVideoString = FALSE;
  1309. }
  1310. #if defined(_AMD64_) || defined(_X86_)
  1311. if(wcsstr(TemporaryBuffer, BootFastString)) { // already have /bootfast
  1312. AddBootFastString = FALSE;
  1313. }
  1314. #endif // defined(_AMD64_) || defined(_X86_)
  1315. }
  1316. if(AddBaseVideo || AddSosToBaseVideoString
  1317. #if defined(_AMD64_) || defined(_X86_)
  1318. || AddBootFastString
  1319. #endif // defined(_AMD64_) || defined(_X86_)
  1320. ) {
  1321. Length = ((tmp ? wcslen(tmp) + 1 : 0) * sizeof(WCHAR));
  1322. if( AddBaseVideo ) {
  1323. Length += sizeof(BaseVideoString);
  1324. }
  1325. if( AddSosToBaseVideoString ) {
  1326. Length += sizeof( BaseVideoSosString );
  1327. }
  1328. #if defined(_AMD64_) || defined(_X86_)
  1329. if( AddBootFastString ) {
  1330. Length += sizeof( BootFastString );
  1331. }
  1332. #endif // defined(_AMD64_) || defined(_X86_)
  1333. tmp2 = SpMemAlloc(Length);
  1334. *tmp2 = ( WCHAR )'\0';
  1335. if( AddBaseVideo ) {
  1336. wcscat(tmp2, BaseVideoString);
  1337. }
  1338. if( AddSosToBaseVideoString ) {
  1339. if( *tmp2 != (WCHAR)'\0' ) {
  1340. wcscat(tmp2, L" ");
  1341. }
  1342. wcscat(tmp2, BaseVideoSosString);
  1343. }
  1344. #if defined(_AMD64_) || defined(_X86_)
  1345. if( AddBootFastString ) {
  1346. if( *tmp2 != (WCHAR)'\0' ) {
  1347. wcscat(tmp2, L" ");
  1348. }
  1349. wcscat(tmp2, BootFastString);
  1350. }
  1351. #endif // defined(_AMD64_) || defined(_X86_)
  1352. if(tmp) {
  1353. if( *tmp2 != (WCHAR)'\0' ) {
  1354. wcscat(tmp2, L" ");
  1355. }
  1356. wcscat(tmp2, tmp);
  1357. }
  1358. LoadOptions = SpDupStringW(tmp2);
  1359. SpMemFree(tmp2);
  1360. } else {
  1361. LoadOptions = SpDupStringW(tmp ? tmp : L"");
  1362. }
  1363. //
  1364. // Add on headless redirect parameter if we are redirecting right now.
  1365. //
  1366. Length = sizeof(HEADLESS_RSP_QUERY_INFO);
  1367. Status = HeadlessDispatch(HeadlessCmdQueryInformation,
  1368. NULL,
  1369. 0,
  1370. &Response,
  1371. &Length
  1372. );
  1373. if (NT_SUCCESS(Status) &&
  1374. (Response.PortType == HeadlessSerialPort) &&
  1375. Response.Serial.TerminalAttached) {
  1376. //
  1377. // Before we go adding a /redirect string, we need to make
  1378. // sure there's not already one.
  1379. //
  1380. if( !wcsstr(LoadOptions, HeadlessRedirectString) ) {
  1381. Length = (wcslen(LoadOptions) + 1) * sizeof(WCHAR);
  1382. Length += sizeof(HeadlessRedirectString);
  1383. tmp2 = SpMemAlloc(Length);
  1384. ASSERT(tmp2 != NULL);
  1385. *tmp2 = UNICODE_NULL;
  1386. wcscat(tmp2, LoadOptions);
  1387. if (*tmp2 != UNICODE_NULL) {
  1388. wcscat(tmp2, L" ");
  1389. }
  1390. wcscat(tmp2, HeadlessRedirectString);
  1391. SpMemFree(LoadOptions);
  1392. LoadOptions = tmp2;
  1393. }
  1394. }
  1395. //
  1396. // LOADIDENTIFIER is specified in the setup information file.
  1397. // We need to surround it in double quotes.
  1398. // Which value to use depends on the BaseVideo flag.
  1399. //
  1400. SifKeyName = BaseVideoOption ? SIF_BASEVIDEOLOADID : SIF_LOADIDENTIFIER;
  1401. tmp = SpGetSectionKeyIndex(SifHandle,SIF_SETUPDATA,SifKeyName,0);
  1402. if(!tmp) {
  1403. SpFatalSifError(SifHandle,SIF_SETUPDATA,SifKeyName,0,0);
  1404. }
  1405. if(!SpIsArc()) {
  1406. //
  1407. // Need quotation marks around the description on amd64/x86.
  1408. //
  1409. LoadIdentifier = SpMemAlloc((wcslen(tmp)+3)*sizeof(WCHAR));
  1410. LoadIdentifier[0] = L'\"';
  1411. wcscpy(LoadIdentifier+1,tmp);
  1412. wcscat(LoadIdentifier,L"\"");
  1413. } else {
  1414. LoadIdentifier = SpDupStringW(tmp);
  1415. }
  1416. //
  1417. // Create a new internal-format boot entry.
  1418. //
  1419. tmp = TemporaryBuffer;
  1420. wcscpy(tmp,SystemPartitionDirectory);
  1421. SpConcatenatePaths(
  1422. tmp,
  1423. #if defined(_AMD64_) || defined(_X86_)
  1424. SpIsArc() ? L"arcldr.exe" : L"ntldr"
  1425. #elif defined(_IA64_)
  1426. L"ia64ldr.efi"
  1427. #else
  1428. L"osloader.exe"
  1429. #endif // defined(_AMD64_) || defined(_X86_)
  1430. );
  1431. tmp = SpDupStringW(tmp);
  1432. SpCreateBootEntry(
  1433. BE_STATUS_NEW,
  1434. SystemPartitionRegion,
  1435. tmp,
  1436. NtPartitionRegion,
  1437. Sysroot,
  1438. LoadOptions,
  1439. LoadIdentifier
  1440. );
  1441. SpMemFree(tmp);
  1442. //
  1443. // If not on an EFI machine, add a new ARC-style boot set.
  1444. //
  1445. if (!SpIsEfi()) {
  1446. BootVars[OSLOADOPTIONS] = LoadOptions;
  1447. BootVars[LOADIDENTIFIER] = LoadIdentifier;
  1448. //
  1449. // OSLOADER is the system partition path + the system partition directory +
  1450. // osloader.exe. (ntldr on amd64/x86 machines).
  1451. //
  1452. if (SystemPartitionRegion != NULL) {
  1453. tmp = TemporaryBuffer;
  1454. wcscpy(tmp,SystemPartitionArcName);
  1455. SpConcatenatePaths(tmp,SystemPartitionDirectory);
  1456. SpConcatenatePaths(
  1457. tmp,
  1458. #if defined(_AMD64_) || defined(_X86_)
  1459. (SpIsArc() ? L"arcldr.exe" : L"ntldr")
  1460. #elif defined(_IA64_)
  1461. L"ia64ldr.efi"
  1462. #else
  1463. L"osloader.exe"
  1464. #endif // defined(_AMD64_) || defined(_X86_)
  1465. );
  1466. BootVars[OSLOADER] = SpDupStringW(tmp);
  1467. } else {
  1468. BootVars[OSLOADER] = SpDupStringW(L"");
  1469. }
  1470. //
  1471. // OSLOADPARTITION is the ARC name of the windows nt partition.
  1472. //
  1473. BootVars[OSLOADPARTITION] = TargetPartitionArcName;
  1474. //
  1475. // OSLOADFILENAME is sysroot.
  1476. //
  1477. BootVars[OSLOADFILENAME] = Sysroot;
  1478. //
  1479. // SYSTEMPARTITION is the ARC name of the system partition.
  1480. //
  1481. if (SystemPartitionRegion != NULL) {
  1482. BootVars[SYSTEMPARTITION] = SystemPartitionArcName;
  1483. } else {
  1484. BootVars[SYSTEMPARTITION] = L"";
  1485. }
  1486. //
  1487. // get the disk signature
  1488. //
  1489. if ((NtPartitionRegion->DiskNumber != 0xffffffff) && HardDisks[NtPartitionRegion->DiskNumber].Signature) {
  1490. Signature = HardDisks[NtPartitionRegion->DiskNumber].Signature;
  1491. } else {
  1492. Signature = 0;
  1493. }
  1494. //
  1495. // Add the boot set and make it the default.
  1496. //
  1497. SpAddBootSet(BootVars, TRUE, Signature);
  1498. SpMemFree(BootVars[OSLOADER]);
  1499. }
  1500. //
  1501. // Free memory allocated.
  1502. //
  1503. SpMemFree(LoadOptions);
  1504. SpMemFree(LoadIdentifier);
  1505. if (!SpIsEfi()) {
  1506. if (SystemPartitionArcName != NULL) {
  1507. SpMemFree(SystemPartitionArcName);
  1508. }
  1509. SpMemFree(TargetPartitionArcName);
  1510. }
  1511. if( OldOsLoadOptionsReplaced ) {
  1512. SpMemFree( OldOsLoadOptions );
  1513. }
  1514. }
  1515. VOID
  1516. SpCompleteBootListConfig(
  1517. WCHAR DriveLetter
  1518. )
  1519. {
  1520. if(!RepairWinnt) {
  1521. if (!SpIsArc()) {
  1522. Timeout = 1;
  1523. } else {
  1524. Timeout = 5;
  1525. //
  1526. // If this is a winnt setup, there will be a boot set to start
  1527. // text setup ("Install/Upgrade Windows NT"). Remove it here.
  1528. //
  1529. if(WinntSetup) {
  1530. PSP_BOOT_ENTRY bootEntry;
  1531. for (bootEntry = SpBootEntries; bootEntry != NULL; bootEntry = bootEntry->Next) {
  1532. if (IS_BOOT_ENTRY_WINDOWS(bootEntry) &&
  1533. !IS_BOOT_ENTRY_DELETED(bootEntry) &&
  1534. (_wcsicmp(bootEntry->OsLoadOptions, L"WINNT32") == 0)) {
  1535. bootEntry->Status |= BE_STATUS_DELETED;
  1536. }
  1537. }
  1538. if (!SpIsEfi()) {
  1539. PWSTR BootVars[MAXBOOTVARS];
  1540. RtlZeroMemory(BootVars,sizeof(BootVars));
  1541. BootVars[OSLOADOPTIONS] = L"WINNT32";
  1542. SpDeleteBootSet(BootVars, NULL);
  1543. }
  1544. }
  1545. }
  1546. }
  1547. #ifdef _X86_
  1548. if (g_Win9xBackup) {
  1549. SpRemoveExtraBootIniEntry();
  1550. }
  1551. #endif
  1552. //
  1553. // Flush boot vars.
  1554. // On some machines, NVRAM update takes a few seconds,
  1555. // so change the message to tell the user we are doing something different.
  1556. //
  1557. SpDisplayStatusText(SP_STAT_UPDATING_NVRAM,DEFAULT_STATUS_ATTRIBUTE);
  1558. if(!SpFlushBootVars()) {
  1559. if(SpIsEfi() || !SpIsArc()) {
  1560. //
  1561. // Fatal on x86 and EFI machines, nonfatal on arc machines.
  1562. //
  1563. if (SpIsEfi()) {
  1564. SpStartScreen(SP_SCRN_CANT_INIT_FLEXBOOT_EFI,
  1565. 3,
  1566. HEADER_HEIGHT+1,
  1567. FALSE,
  1568. FALSE,
  1569. DEFAULT_ATTRIBUTE
  1570. );
  1571. } else {
  1572. WCHAR DriveLetterString[2];
  1573. DriveLetterString[0] = DriveLetter;
  1574. DriveLetterString[1] = L'\0';
  1575. SpStringToUpper(DriveLetterString);
  1576. SpStartScreen(SP_SCRN_CANT_INIT_FLEXBOOT,
  1577. 3,
  1578. HEADER_HEIGHT+1,
  1579. FALSE,
  1580. FALSE,
  1581. DEFAULT_ATTRIBUTE,
  1582. DriveLetterString,
  1583. DriveLetterString
  1584. );
  1585. }
  1586. SpDisplayStatusText(SP_STAT_F3_EQUALS_EXIT,DEFAULT_STATUS_ATTRIBUTE);
  1587. SpInputDrain();
  1588. while(SpInputGetKeypress() != KEY_F3) ;
  1589. SpDone(0,FALSE,TRUE);
  1590. } else {
  1591. BOOL b;
  1592. b = TRUE;
  1593. while(b) {
  1594. ULONG ValidKeys[2] = { ASCI_CR, 0 };
  1595. SpStartScreen(
  1596. SP_SCRN_CANT_UPDATE_BOOTVARS,
  1597. 3,
  1598. HEADER_HEIGHT+1,
  1599. FALSE,
  1600. FALSE,
  1601. DEFAULT_ATTRIBUTE,
  1602. NewBootVars[LOADIDENTIFIER],
  1603. NewBootVars[OSLOADER],
  1604. NewBootVars[OSLOADPARTITION],
  1605. NewBootVars[OSLOADFILENAME],
  1606. NewBootVars[OSLOADOPTIONS],
  1607. NewBootVars[SYSTEMPARTITION]
  1608. );
  1609. SpDisplayStatusOptions(
  1610. DEFAULT_STATUS_ATTRIBUTE,
  1611. SP_STAT_ENTER_EQUALS_CONTINUE,
  1612. 0
  1613. );
  1614. switch(SpWaitValidKey(ValidKeys,NULL,NULL)) {
  1615. case ASCI_CR:
  1616. b = FALSE;
  1617. }
  1618. }
  1619. }
  1620. }
  1621. if(SpIsArc() && !SpIsEfi()) {
  1622. // Free all of the boot variable strings
  1623. BOOTVAR i;
  1624. for(i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) {
  1625. SpMemFree(NewBootVars[i]);
  1626. NewBootVars[i] = NULL;
  1627. }
  1628. }
  1629. }
  1630. VOID
  1631. SpPtDeleteBootSetsForRegion(
  1632. PDISK_REGION Region
  1633. )
  1634. /*++
  1635. Routine Description:
  1636. This routine goes through all the valid boot entries and
  1637. deletes the ones which point to the specified region.
  1638. Arguments:
  1639. Region : The region whose references from boot entries need
  1640. to be removed
  1641. Return Value:
  1642. None.
  1643. --*/
  1644. {
  1645. PWSTR bootSet[MAXBOOTVARS];
  1646. ENUMARCPATHTYPE arcPathType;
  1647. ULONG i;
  1648. PSP_BOOT_ENTRY bootEntry;
  1649. if (Region->PartitionedSpace) {
  1650. BOOLEAN IsSystemPartition = SPPT_IS_REGION_SYSTEMPARTITION(Region);
  1651. //
  1652. // Find all boot entries that have the specified region as the
  1653. // OS load partition, and mark them for deletion.
  1654. //
  1655. for (bootEntry = SpBootEntries; bootEntry != NULL; bootEntry = bootEntry->Next) {
  1656. if (IS_BOOT_ENTRY_WINDOWS(bootEntry) &&
  1657. !IS_BOOT_ENTRY_DELETED(bootEntry) &&
  1658. (IsSystemPartition ? (bootEntry->LoaderPartitionDiskRegion == Region) :
  1659. (bootEntry->OsPartitionDiskRegion == Region))) {
  1660. bootEntry->Status |= BE_STATUS_DELETED;
  1661. //
  1662. // Make the regions also NULL since they might have actually
  1663. // been deleted
  1664. //
  1665. bootEntry->LoaderPartitionDiskRegion = NULL;
  1666. bootEntry->OsPartitionDiskRegion = NULL;
  1667. }
  1668. }
  1669. //
  1670. // If we're not on an EFI machine, we also have to munge the ARC
  1671. // boot variables.
  1672. //
  1673. if (!SpIsEfi()) {
  1674. //
  1675. // Set up the boot set
  1676. //
  1677. for (i = FIRSTBOOTVAR; i <= LASTBOOTVAR; i++) {
  1678. bootSet[i] = NULL;
  1679. }
  1680. //
  1681. // We go through this loop twice, once for primary ARC path
  1682. // and once for secondary. We delete any image which has
  1683. // the OS load partition on the region we are deleting.
  1684. //
  1685. for (i = 0; i < 2; i++) {
  1686. if (i == 0) {
  1687. arcPathType = PrimaryArcPath;
  1688. } else {
  1689. arcPathType = SecondaryArcPath;
  1690. }
  1691. SpArcNameFromRegion(
  1692. Region,
  1693. TemporaryBuffer,
  1694. sizeof(TemporaryBuffer),
  1695. PartitionOrdinalOnDisk,
  1696. arcPathType);
  1697. if ((TemporaryBuffer)[0] != L'\0') {
  1698. ULONG Index = IsSystemPartition ?
  1699. SYSTEMPARTITION : OSLOADPARTITION;
  1700. bootSet[Index] = SpDupStringW(TemporaryBuffer);
  1701. SpDeleteBootSet(bootSet, NULL);
  1702. SpMemFree(bootSet[Index]);
  1703. }
  1704. }
  1705. }
  1706. }
  1707. }
  1708. VOID
  1709. SpGetNtDirectoryList(
  1710. OUT PWSTR **DirectoryList,
  1711. OUT PULONG DirectoryCount
  1712. )
  1713. /*++
  1714. Routine Description:
  1715. Determine the list of directories into which NT may be installed.
  1716. This is independent of the partitions onto which it may be installed.
  1717. The determination of which directories nt might be in is based on
  1718. boot.ini in the amd64/x86 cases, or on arc firmware (OSLOADFILENAME
  1719. var) in the arc case.
  1720. Arguments:
  1721. DirectoryList - receives a pointer to an array of strings,
  1722. each of which contains a possible windows nt tree.
  1723. DirectoryCount - receives the number of elements in DirectoryList.
  1724. This may be 0.
  1725. Return Value:
  1726. None. The caller must free the array in DirectoryList if
  1727. DirectoryCount is returned as non-0.
  1728. --*/
  1729. {
  1730. ULONG count;
  1731. PSP_BOOT_ENTRY BootEntry;
  1732. PSP_BOOT_ENTRY BootEntry2;
  1733. PWSTR *DirList;
  1734. //
  1735. // Free any previously allocated list.
  1736. //
  1737. if (CurrentNtDirectoryList != NULL) {
  1738. SpMemFree(CurrentNtDirectoryList);
  1739. }
  1740. //
  1741. // Walk the boot entry list to determine how many unique NT directory names
  1742. // exist.
  1743. //
  1744. count = 0;
  1745. for (BootEntry = SpBootEntries; BootEntry != NULL; BootEntry = BootEntry->Next) {
  1746. if (!IS_BOOT_ENTRY_WINDOWS(BootEntry) || (BootEntry->OsDirectory == NULL)) {
  1747. continue;
  1748. }
  1749. for (BootEntry2 = SpBootEntries; BootEntry2 != BootEntry; BootEntry2 = BootEntry2->Next) {
  1750. if (!IS_BOOT_ENTRY_WINDOWS(BootEntry2) || (BootEntry2->OsDirectory == NULL)) {
  1751. continue;
  1752. }
  1753. if (_wcsicmp(BootEntry2->OsDirectory, BootEntry->OsDirectory) == 0) {
  1754. break;
  1755. }
  1756. }
  1757. if (BootEntry2 == BootEntry) {
  1758. count++;
  1759. }
  1760. }
  1761. //
  1762. // Allocate space for the list.
  1763. //
  1764. DirList = SpMemAlloc(count * sizeof(PWSTR));
  1765. ASSERT(DirList != NULL);
  1766. //
  1767. // Populate the list.
  1768. //
  1769. count = 0;
  1770. for (BootEntry = SpBootEntries; BootEntry != NULL; BootEntry = BootEntry->Next) {
  1771. if (!IS_BOOT_ENTRY_WINDOWS(BootEntry) || (BootEntry->OsDirectory == NULL)) {
  1772. continue;
  1773. }
  1774. for (BootEntry2 = SpBootEntries; BootEntry2 != BootEntry; BootEntry2 = BootEntry2->Next) {
  1775. if (!IS_BOOT_ENTRY_WINDOWS(BootEntry2) || (BootEntry2->OsDirectory == NULL)) {
  1776. continue;
  1777. }
  1778. if (_wcsicmp(BootEntry2->OsDirectory, BootEntry->OsDirectory) == 0) {
  1779. break;
  1780. }
  1781. }
  1782. if (BootEntry2 == BootEntry) {
  1783. DirList[count++] = BootEntry->OsDirectory;
  1784. }
  1785. }
  1786. //
  1787. // Return a pointer to the list that we allocated.
  1788. //
  1789. CurrentNtDirectoryList = DirList;
  1790. *DirectoryList = DirList;
  1791. *DirectoryCount = count;
  1792. return;
  1793. }
  1794. BOOLEAN
  1795. SpConvertArcBootEntries (
  1796. IN ULONG MaxComponents
  1797. )
  1798. /*++
  1799. Routine Description:
  1800. Convert ARC boot entries (read from boot.ini or from ARC NVRAM) into
  1801. our internal format.
  1802. Arguments:
  1803. MaxComponents - maximum number of elements in any NVRAM variable.
  1804. Return Value:
  1805. BOOLEAN - FALSE if any unexpected errors occurred.
  1806. --*/
  1807. {
  1808. LONG i;
  1809. PDISK_REGION systemPartitionRegion;
  1810. PDISK_REGION ntPartitionRegion;
  1811. PWSTR loaderName;
  1812. for (i = (LONG)MaxComponents - 1; i >= 0; i--) {
  1813. //
  1814. // Skip this boot set if it is not complete.
  1815. //
  1816. if ((BootVars[SYSTEMPARTITION][i] != NULL) &&
  1817. (BootVars[OSLOADPARTITION][i] != NULL) &&
  1818. (BootVars[OSLOADER][i] != NULL) &&
  1819. (BootVars[OSLOADFILENAME][i] != NULL) &&
  1820. (BootVars[OSLOADOPTIONS][i] != NULL) &&
  1821. (BootVars[LOADIDENTIFIER][i] != NULL)) {
  1822. //
  1823. // Translate the SYSTEMPARTITION and OSLOADPARTITION ARC names
  1824. // into disk region pointers. Get the loader file name from
  1825. // OSLOADER, which contains an ARC name (same as OSLOADPARTITION)
  1826. // and a file name.
  1827. //
  1828. systemPartitionRegion = SpRegionFromArcName(
  1829. BootVars[SYSTEMPARTITION][i],
  1830. PartitionOrdinalCurrent,
  1831. NULL
  1832. );
  1833. ntPartitionRegion = SpRegionFromArcName(
  1834. BootVars[OSLOADPARTITION][i],
  1835. PartitionOrdinalCurrent,
  1836. NULL
  1837. );
  1838. //
  1839. // Take care of duplicate arc names for the same disk by searching
  1840. // and validating the NT directory is present on the partition
  1841. //
  1842. while (ntPartitionRegion &&
  1843. !SpIsNtInDirectory(ntPartitionRegion, BootVars[OSLOADFILENAME][i])) {
  1844. //
  1845. // Continue to look for same name region from the current
  1846. // searched region
  1847. //
  1848. ntPartitionRegion = SpRegionFromArcName(
  1849. BootVars[OSLOADPARTITION][i],
  1850. PartitionOrdinalCurrent,
  1851. ntPartitionRegion
  1852. );
  1853. }
  1854. loaderName = wcschr(BootVars[OSLOADER][i], L'\\');
  1855. //
  1856. // If all of the above worked, then add an internal-format boot
  1857. // entry for this ARC boot set.
  1858. //
  1859. if ((systemPartitionRegion != NULL) &&
  1860. (ntPartitionRegion != NULL) &&
  1861. (loaderName != NULL)) {
  1862. SpCreateBootEntry(
  1863. BE_STATUS_FROM_BOOT_INI,
  1864. systemPartitionRegion,
  1865. loaderName,
  1866. ntPartitionRegion,
  1867. BootVars[OSLOADFILENAME][i],
  1868. BootVars[OSLOADOPTIONS][i],
  1869. BootVars[LOADIDENTIFIER][i]
  1870. );
  1871. }
  1872. }
  1873. }
  1874. return TRUE;
  1875. }
  1876. VOID
  1877. SpUpdateRegionForBootEntries(
  1878. VOID
  1879. )
  1880. /*++
  1881. Routine Description:
  1882. Update the region pointers for all the given boot entries.
  1883. NOTE : The region pointers change with every commit so we
  1884. can't cache them across commits.
  1885. Arguments:
  1886. None.
  1887. Return Value:
  1888. None.
  1889. --*/
  1890. {
  1891. PSP_BOOT_ENTRY BootEntry;
  1892. //
  1893. // Walk through each boot entry and update its system partition region
  1894. // pointer and NT partition region pointer.
  1895. //
  1896. for (BootEntry = SpBootEntries; BootEntry != NULL; BootEntry = BootEntry->Next) {
  1897. if (!IS_BOOT_ENTRY_DELETED(BootEntry)) {
  1898. if (BootEntry->LoaderPartitionNtName != NULL) {
  1899. BootEntry->LoaderPartitionDiskRegion =
  1900. SpRegionFromNtName(BootEntry->LoaderPartitionNtName,
  1901. PartitionOrdinalCurrent);
  1902. } else {
  1903. BootEntry->LoaderPartitionDiskRegion = NULL;
  1904. }
  1905. if (BootEntry->OsPartitionNtName != NULL) {
  1906. BootEntry->OsPartitionDiskRegion =
  1907. SpRegionFromNtName(BootEntry->OsPartitionNtName,
  1908. PartitionOrdinalCurrent);
  1909. } else {
  1910. BootEntry->OsPartitionDiskRegion = NULL;
  1911. }
  1912. }
  1913. }
  1914. return;
  1915. } // SpUpdateRegionForBootEntries
  1916. VOID
  1917. SpCreateBootEntry (
  1918. IN ULONG_PTR Status,
  1919. IN PDISK_REGION BootFileRegion,
  1920. IN PWSTR BootFilePath,
  1921. IN PDISK_REGION OsLoadRegion,
  1922. IN PWSTR OsLoadPath,
  1923. IN PWSTR OsLoadOptions,
  1924. IN PWSTR FriendlyName
  1925. )
  1926. /*++
  1927. Routine Description:
  1928. Create an internal-format boot entry.
  1929. Arguments:
  1930. Status - The status to be assigned to the boot entry. This should be either
  1931. zero (for an entry already in NVRAM) or BE_STATUS_NEW for a new boot
  1932. entry. Entries marked BE_STATUS_NEW are written to NVRAM at the end
  1933. of textmode setup.
  1934. BootFileRegion - The disk region on which the OS loader resides.
  1935. BootFilePath - The volume-relative path to the OS loader. Must start with
  1936. a backslash.
  1937. OsLoadRegion - The disk region on which the OS resides.
  1938. OsLoadPath - The volume-relative path to the OS root directory (\WINDOWS).
  1939. Must start with a backslash.
  1940. OsLoadOptions - Boot options for the OS. Can be an empty string.
  1941. FriendlyName - The user-visible name for the boot entry. (This is ARC's
  1942. LOADIDENTIFIER.)
  1943. Return Value:
  1944. None. Only memory allocation failures are possible, and these are
  1945. handled out-of-band.
  1946. --*/
  1947. {
  1948. NTSTATUS status;
  1949. ULONG requiredLength;
  1950. ULONG osOptionsOffset;
  1951. ULONG osLoadOptionsLength;
  1952. ULONG osLoadPathOffset;
  1953. ULONG osLoadPathLength;
  1954. ULONG osOptionsLength;
  1955. ULONG friendlyNameOffset;
  1956. ULONG friendlyNameLength;
  1957. ULONG bootPathOffset;
  1958. ULONG bootPathLength;
  1959. PSP_BOOT_ENTRY myBootEntry;
  1960. PSP_BOOT_ENTRY previousBootEntry;
  1961. PSP_BOOT_ENTRY nextBootEntry;
  1962. PBOOT_ENTRY ntBootEntry;
  1963. PWINDOWS_OS_OPTIONS osOptions;
  1964. PFILE_PATH osLoadPath;
  1965. PWSTR friendlyName;
  1966. PFILE_PATH bootPath;
  1967. PWSTR p;
  1968. PWSTR bootFileDevice;
  1969. PWSTR osLoadDevice;
  1970. //
  1971. // Get NT names for the input disk regions.
  1972. //
  1973. bootFileDevice = SpMemAlloc(512);
  1974. SpNtNameFromRegion(BootFileRegion, bootFileDevice, 512, PartitionOrdinalCurrent);
  1975. osLoadDevice = SpMemAlloc(512);
  1976. SpNtNameFromRegion(OsLoadRegion, osLoadDevice, 512, PartitionOrdinalCurrent);
  1977. //
  1978. // Calculate how long the internal boot entry needs to be. This includes
  1979. // our internal structure, plus the BOOT_ENTRY structure that the NT APIs
  1980. // use.
  1981. //
  1982. // Our structure:
  1983. //
  1984. requiredLength = FIELD_OFFSET(SP_BOOT_ENTRY, NtBootEntry);
  1985. //
  1986. // Base part of NT structure:
  1987. //
  1988. requiredLength += FIELD_OFFSET(BOOT_ENTRY, OsOptions);
  1989. //
  1990. // Save offset to BOOT_ENTRY.OsOptions. Add in base part of
  1991. // WINDOWS_OS_OPTIONS. Calculate length in bytes of OsLoadOptions
  1992. // and add that in.
  1993. //
  1994. osOptionsOffset = requiredLength;
  1995. requiredLength += FIELD_OFFSET(WINDOWS_OS_OPTIONS, OsLoadOptions);
  1996. osLoadOptionsLength = (wcslen(OsLoadOptions) + 1) * sizeof(WCHAR);
  1997. requiredLength += osLoadOptionsLength;
  1998. //
  1999. // Round up to a ULONG boundary for the OS FILE_PATH in the
  2000. // WINDOWS_OS_OPTIONS. Save offset to OS FILE_PATH. Add in base part
  2001. // of FILE_PATH. Add in length in bytes of OS device NT name and OS
  2002. // directory. Calculate total length of OS FILE_PATH and of
  2003. // WINDOWS_OS_OPTIONS.
  2004. //
  2005. requiredLength = ALIGN_UP(requiredLength, ULONG);
  2006. osLoadPathOffset = requiredLength;
  2007. requiredLength += FIELD_OFFSET(FILE_PATH, FilePath);
  2008. requiredLength += (wcslen(osLoadDevice) + 1 + wcslen(OsLoadPath) + 1) * sizeof(WCHAR);
  2009. osLoadPathLength = requiredLength - osLoadPathOffset;
  2010. osOptionsLength = requiredLength - osOptionsOffset;
  2011. //
  2012. // Round up to a ULONG boundary for the friendly name in the BOOT_ENTRY.
  2013. // Save offset to friendly name. Calculate length in bytes of friendly name
  2014. // and add that in.
  2015. //
  2016. requiredLength = ALIGN_UP(requiredLength, ULONG);
  2017. friendlyNameOffset = requiredLength;
  2018. friendlyNameLength = (wcslen(FriendlyName) + 1) * sizeof(WCHAR);
  2019. requiredLength += friendlyNameLength;
  2020. //
  2021. // Round up to a ULONG boundary for the boot FILE_PATH in the BOOT_ENTRY.
  2022. // Save offset to boot FILE_PATH. Add in base part of FILE_PATH. Add in
  2023. // length in bytes of boot device NT name and boot file. Calculate total
  2024. // length of boot FILE_PATH.
  2025. //
  2026. requiredLength = ALIGN_UP(requiredLength, ULONG);
  2027. bootPathOffset = requiredLength;
  2028. requiredLength += FIELD_OFFSET(FILE_PATH, FilePath);
  2029. requiredLength += (wcslen(bootFileDevice) + 1 + wcslen(BootFilePath) + 1) * sizeof(WCHAR);
  2030. bootPathLength = requiredLength - bootPathOffset;
  2031. //
  2032. // Allocate memory for the boot entry.
  2033. //
  2034. myBootEntry = SpMemAlloc(requiredLength);
  2035. ASSERT(myBootEntry != NULL);
  2036. RtlZeroMemory(myBootEntry, requiredLength);
  2037. //
  2038. // Calculate addresses of various substructures using the saved offsets.
  2039. //
  2040. ntBootEntry = &myBootEntry->NtBootEntry;
  2041. osOptions = (PWINDOWS_OS_OPTIONS)ntBootEntry->OsOptions;
  2042. osLoadPath = (PFILE_PATH)((PUCHAR)myBootEntry + osLoadPathOffset);
  2043. friendlyName = (PWSTR)((PUCHAR)myBootEntry + friendlyNameOffset);
  2044. bootPath = (PFILE_PATH)((PUCHAR)myBootEntry + bootPathOffset);
  2045. //
  2046. // Fill in the internal-format structure.
  2047. //
  2048. myBootEntry->AllocationEnd = (PUCHAR)myBootEntry + requiredLength;
  2049. myBootEntry->Status = Status | BE_STATUS_ORDERED;
  2050. myBootEntry->FriendlyName = friendlyName;
  2051. myBootEntry->FriendlyNameLength = friendlyNameLength;
  2052. myBootEntry->OsLoadOptions = osOptions->OsLoadOptions;
  2053. myBootEntry->OsLoadOptionsLength = osLoadOptionsLength;
  2054. myBootEntry->LoaderPath = bootPath;
  2055. myBootEntry->OsPath = osLoadPath;
  2056. myBootEntry->LoaderPartitionDiskRegion = BootFileRegion;
  2057. myBootEntry->OsPartitionDiskRegion = OsLoadRegion;
  2058. //
  2059. // Fill in the base part of the NT boot entry.
  2060. //
  2061. ntBootEntry->Version = BOOT_ENTRY_VERSION;
  2062. ntBootEntry->Length = requiredLength - FIELD_OFFSET(SP_BOOT_ENTRY, NtBootEntry);
  2063. ntBootEntry->Attributes = BOOT_ENTRY_ATTRIBUTE_ACTIVE | BOOT_ENTRY_ATTRIBUTE_WINDOWS;
  2064. ntBootEntry->FriendlyNameOffset = (ULONG)((PUCHAR)friendlyName - (PUCHAR)ntBootEntry);
  2065. ntBootEntry->BootFilePathOffset = (ULONG)((PUCHAR)bootPath - (PUCHAR)ntBootEntry);
  2066. ntBootEntry->OsOptionsLength = osOptionsLength;
  2067. //
  2068. // Fill in the base part of the WINDOWS_OS_OPTIONS, including the
  2069. // OsLoadOptions.
  2070. //
  2071. strcpy(osOptions->Signature, WINDOWS_OS_OPTIONS_SIGNATURE);
  2072. osOptions->Version = WINDOWS_OS_OPTIONS_VERSION;
  2073. osOptions->Length = osOptionsLength;
  2074. osOptions->OsLoadPathOffset = (ULONG)((PUCHAR)osLoadPath - (PUCHAR)osOptions);
  2075. wcscpy(osOptions->OsLoadOptions, OsLoadOptions);
  2076. //
  2077. // Fill in the OS FILE_PATH.
  2078. //
  2079. osLoadPath->Version = FILE_PATH_VERSION;
  2080. osLoadPath->Length = osLoadPathLength;
  2081. osLoadPath->Type = FILE_PATH_TYPE_NT;
  2082. p = (PWSTR)osLoadPath->FilePath;
  2083. myBootEntry->OsPartitionNtName = p;
  2084. wcscpy(p, osLoadDevice);
  2085. p += wcslen(p) + 1;
  2086. myBootEntry->OsDirectory = p;
  2087. wcscpy(p, OsLoadPath);
  2088. //
  2089. // Copy the friendly name.
  2090. //
  2091. wcscpy(friendlyName, FriendlyName);
  2092. //
  2093. // Fill in the boot FILE_PATH.
  2094. //
  2095. bootPath->Version = FILE_PATH_VERSION;
  2096. bootPath->Length = bootPathLength;
  2097. bootPath->Type = FILE_PATH_TYPE_NT;
  2098. p = (PWSTR)bootPath->FilePath;
  2099. myBootEntry->LoaderPartitionNtName = p;
  2100. wcscpy(p, bootFileDevice);
  2101. p += wcslen(p) + 1;
  2102. myBootEntry->LoaderFile = p;
  2103. wcscpy(p, BootFilePath);
  2104. //
  2105. // Link the new boot entry into the list, after any removable media
  2106. // entries that are at the front of the list.
  2107. //
  2108. previousBootEntry = NULL;
  2109. nextBootEntry = SpBootEntries;
  2110. while ((nextBootEntry != NULL) &&
  2111. IS_BOOT_ENTRY_REMOVABLE_MEDIA(nextBootEntry)) {
  2112. previousBootEntry = nextBootEntry;
  2113. nextBootEntry = nextBootEntry->Next;
  2114. }
  2115. myBootEntry->Next = nextBootEntry;
  2116. if (previousBootEntry == NULL) {
  2117. SpBootEntries = myBootEntry;
  2118. } else {
  2119. previousBootEntry->Next = myBootEntry;
  2120. }
  2121. //
  2122. // Free local memory.
  2123. //
  2124. SpMemFree(bootFileDevice);
  2125. SpMemFree(osLoadDevice);
  2126. return;
  2127. } // SpCreateBootEntry
  2128. #if defined(EFI_NVRAM_ENABLED)
  2129. BOOLEAN
  2130. SpBuildHarddiskNameTranslations (
  2131. VOID
  2132. )
  2133. /*++
  2134. Routine Description:
  2135. Build a list of the translations of all \Device\HarddiskN\PartitionM
  2136. symbolic links to \Device\HarddiskVolumeN device names.
  2137. Arguments:
  2138. None.
  2139. Return Value:
  2140. BOOLEAN - FALSE if an unexpected error occurred.
  2141. --*/
  2142. {
  2143. NTSTATUS status;
  2144. OBJECT_ATTRIBUTES obja;
  2145. UNICODE_STRING unicodeString;
  2146. HANDLE deviceHandle;
  2147. HANDLE diskHandle;
  2148. HANDLE linkHandle;
  2149. PUCHAR buffer1;
  2150. PUCHAR buffer2;
  2151. BOOLEAN restartScan;
  2152. ULONG context1;
  2153. ULONG context2;
  2154. POBJECT_DIRECTORY_INFORMATION dirInfo1;
  2155. POBJECT_DIRECTORY_INFORMATION dirInfo2;
  2156. PWSTR linkName;
  2157. PWSTR p;
  2158. PHARDDISK_NAME_TRANSLATION translation;
  2159. //
  2160. // Allocate buffers for directory queries.
  2161. //
  2162. #define BUFFER_SIZE 2048
  2163. buffer1 = SpMemAlloc(BUFFER_SIZE);
  2164. buffer2 = SpMemAlloc(BUFFER_SIZE);
  2165. //
  2166. // Open the \Device directory.
  2167. //
  2168. INIT_OBJA(&obja, &unicodeString, L"\\device");
  2169. status = ZwOpenDirectoryObject(&deviceHandle, DIRECTORY_ALL_ACCESS, &obja);
  2170. if (!NT_SUCCESS(status)) {
  2171. ASSERT(FALSE);
  2172. goto cleanup;
  2173. }
  2174. restartScan = TRUE;
  2175. context1 = 0;
  2176. do {
  2177. //
  2178. // Search the \Device directory for HarddiskN subdirectories.
  2179. //
  2180. status = ZwQueryDirectoryObject(
  2181. deviceHandle,
  2182. buffer1,
  2183. BUFFER_SIZE,
  2184. TRUE,
  2185. restartScan,
  2186. &context1,
  2187. NULL
  2188. );
  2189. restartScan = FALSE;
  2190. if (!NT_SUCCESS(status)) {
  2191. if (status != STATUS_NO_MORE_ENTRIES) {
  2192. ASSERT(FALSE);
  2193. goto cleanup;
  2194. }
  2195. status = STATUS_SUCCESS;
  2196. break;
  2197. }
  2198. //
  2199. // We only care about directories with HarddiskN names.
  2200. //
  2201. dirInfo1 = (POBJECT_DIRECTORY_INFORMATION)buffer1;
  2202. if ((dirInfo1->Name.Length < sizeof(L"harddisk")) ||
  2203. (dirInfo1->TypeName.Length < (sizeof(L"Directory") - sizeof(WCHAR))) ||
  2204. (_wcsnicmp(dirInfo1->TypeName.Buffer,L"Directory",wcslen(L"Directory")) != 0)) {
  2205. continue;
  2206. }
  2207. SpStringToLower(dirInfo1->Name.Buffer);
  2208. if (wcsncmp(dirInfo1->Name.Buffer, L"harddisk", wcslen(L"harddisk")) != 0) {
  2209. continue;
  2210. }
  2211. p = dirInfo1->Name.Buffer + wcslen(L"Harddisk");
  2212. if (*p == 0) {
  2213. continue;
  2214. }
  2215. do {
  2216. if ((*p < L'0') || (*p > L'9')) {
  2217. break;
  2218. }
  2219. p++;
  2220. } while (*p != 0);
  2221. if (*p != 0) {
  2222. continue;
  2223. }
  2224. //
  2225. // We have the name of a \Device\HarddiskN directory. Open it and look
  2226. // for PartitionM names.
  2227. //
  2228. InitializeObjectAttributes(
  2229. &obja,
  2230. &dirInfo1->Name,
  2231. OBJ_CASE_INSENSITIVE,
  2232. deviceHandle,
  2233. NULL
  2234. );
  2235. status = ZwOpenDirectoryObject(&diskHandle, DIRECTORY_ALL_ACCESS, &obja);
  2236. if (!NT_SUCCESS(status)) {
  2237. goto cleanup;
  2238. }
  2239. restartScan = TRUE;
  2240. context2 = 0;
  2241. do {
  2242. //
  2243. // Search the \Device\HarddiskN directory for PartitionM symbolic
  2244. // links.
  2245. //
  2246. status = ZwQueryDirectoryObject(
  2247. diskHandle,
  2248. buffer2,
  2249. BUFFER_SIZE,
  2250. TRUE,
  2251. restartScan,
  2252. &context2,
  2253. NULL
  2254. );
  2255. restartScan = FALSE;
  2256. if (!NT_SUCCESS(status)) {
  2257. if (status != STATUS_NO_MORE_ENTRIES) {
  2258. ASSERT(FALSE);
  2259. goto cleanup;
  2260. }
  2261. status = STATUS_SUCCESS;
  2262. break;
  2263. }
  2264. //
  2265. // We only care about symbolic links with PartitionN names.
  2266. //
  2267. dirInfo2 = (POBJECT_DIRECTORY_INFORMATION)buffer2;
  2268. if ((dirInfo2->Name.Length < sizeof(L"partition")) ||
  2269. (dirInfo2->TypeName.Length < (sizeof(L"SymbolicLink") - sizeof(WCHAR))) ||
  2270. (_wcsnicmp(dirInfo2->TypeName.Buffer,L"SymbolicLink",wcslen(L"SymbolicLink")) != 0)) {
  2271. continue;
  2272. }
  2273. SpStringToLower(dirInfo2->Name.Buffer);
  2274. if (wcsncmp(dirInfo2->Name.Buffer, L"partition", wcslen(L"partition")) != 0) {
  2275. continue;
  2276. }
  2277. p = dirInfo2->Name.Buffer + wcslen(L"partition");
  2278. if ((*p == 0) || (*p == L'0')) { // skip partition0
  2279. continue;
  2280. }
  2281. do {
  2282. if ((*p < L'0') || (*p > L'9')) {
  2283. break;
  2284. }
  2285. p++;
  2286. } while (*p != 0);
  2287. if (*p != 0) {
  2288. continue;
  2289. }
  2290. //
  2291. // Open the \Device\HarddiskN\PartitionM symbolic link.
  2292. //
  2293. linkName = SpMemAlloc(sizeof(L"\\device") +
  2294. dirInfo1->Name.Length +
  2295. dirInfo2->Name.Length +
  2296. sizeof(WCHAR));
  2297. wcscpy(linkName, L"\\device");
  2298. SpConcatenatePaths(linkName, dirInfo1->Name.Buffer);
  2299. SpConcatenatePaths(linkName, dirInfo2->Name.Buffer);
  2300. //
  2301. // Query the link to get the link target.
  2302. //
  2303. status = SpQueryCanonicalName(linkName,
  2304. -1,
  2305. TemporaryBuffer,
  2306. sizeof(TemporaryBuffer));
  2307. if (!NT_SUCCESS(status)) {
  2308. ASSERT(FALSE);
  2309. SpMemFree(linkName);
  2310. goto cleanup;
  2311. }
  2312. //
  2313. // Create a translation entry.
  2314. //
  2315. translation = SpMemAlloc(sizeof(HARDDISK_NAME_TRANSLATION));
  2316. translation->Next = SpHarddiskNameTranslations;
  2317. SpHarddiskNameTranslations = translation;
  2318. translation->PartitionName = linkName;
  2319. translation->VolumeName = SpDupStringW(TemporaryBuffer);
  2320. } while (TRUE);
  2321. ZwClose(diskHandle);
  2322. } while (TRUE);
  2323. ASSERT(status == STATUS_SUCCESS);
  2324. cleanup:
  2325. SpMemFree(buffer1);
  2326. SpMemFree(buffer2);
  2327. return (NT_SUCCESS(status) ? TRUE : FALSE);
  2328. } // SpBuildHarddiskNameTranslations
  2329. NTSTATUS
  2330. SpGetBootEntryFilePath(
  2331. IN ULONG Id,
  2332. IN PWSTR LoaderPartitionNtName,
  2333. IN PWSTR LoaderFile,
  2334. OUT PWSTR* FilePath
  2335. )
  2336. /*++
  2337. Routine Description:
  2338. Construct a filepath including the loaderpartition name, the directory path to the
  2339. OS loader and a filename for the boot entry specified.
  2340. Arguments:
  2341. Id the boot entry id
  2342. LoaderPartitionNtName pointer to the string representing the disk partition
  2343. LoaderFile pointer to the string representing the path to the EFI OS loader
  2344. FilePath upon completion, this points to the completed filepath to the
  2345. boot entry file
  2346. Return Value:
  2347. NTSTATUS
  2348. --*/
  2349. {
  2350. NTSTATUS status;
  2351. WCHAR* p;
  2352. ULONG FilePathSize;
  2353. WCHAR idString[9];
  2354. //
  2355. // use the EFI variable name as the filename
  2356. //
  2357. swprintf( idString, L"Boot%04x", Id);
  2358. //
  2359. // determine the size of the final filepath
  2360. //
  2361. // Note: FilePathSize should be a little bigger than actually needed
  2362. // since we are including the full LoadFile string. Also, the '\'
  2363. // characters may be extra.
  2364. //
  2365. FilePathSize = (wcslen(LoaderPartitionNtName) * sizeof(WCHAR)) + // partition
  2366. sizeof(WCHAR) + // '\'
  2367. (wcslen(LoaderFile) * sizeof(WCHAR)) + // path
  2368. sizeof(WCHAR) + // '\'
  2369. (wcslen(idString) * sizeof(WCHAR)) + // new filename
  2370. sizeof(WCHAR); // null term.
  2371. ASSERT(FilePathSize > 0);
  2372. if (FilePathSize <= 0) {
  2373. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: invalid loader partition name and/or loader path\n"));
  2374. return STATUS_INVALID_PARAMETER;
  2375. }
  2376. *FilePath = SpMemAlloc(FilePathSize);
  2377. if (!*FilePath) {
  2378. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed to allocate memory for FilePath\n"));
  2379. return STATUS_NO_MEMORY;
  2380. }
  2381. wcscpy(*FilePath, LoaderPartitionNtName);
  2382. SpConcatenatePaths(*FilePath, LoaderFile);
  2383. // remove the os loader filename from the path
  2384. p = wcsrchr(*FilePath, L'\\');
  2385. if (p != NULL) {
  2386. p++;
  2387. } else {
  2388. // we could get here, but it would be wierd.
  2389. p = *FilePath;
  2390. wcscat(p, L"\\");
  2391. }
  2392. //
  2393. // insert the filename
  2394. //
  2395. wcscpy(p, idString);
  2396. ASSERT((wcslen(*FilePath) + 1) * sizeof(WCHAR) <= FilePathSize);
  2397. return STATUS_SUCCESS;
  2398. }
  2399. NTSTATUS
  2400. SpGetAndWriteBootEntry(
  2401. IN ULONG Id,
  2402. IN PWSTR BootEntryPath
  2403. )
  2404. /*++
  2405. Routine Description:
  2406. Get the boot entry from NVRAM for the given boot entry Id. Construct a filename
  2407. of the form BootXXXX, where XXXX = id. Put the file in the same directory as the
  2408. EFI OS loader. The directory is determined from the LoaderFile string.
  2409. Arguments:
  2410. bootEntry pointer to a SP_BOOT_ENTRY structure of the entry to write
  2411. BootEntryPath pinter to the ARC/NT style reference to the boot entry filename
  2412. Return Value:
  2413. NTSTATUS
  2414. --*/
  2415. {
  2416. NTSTATUS status;
  2417. WCHAR idString[9];
  2418. HANDLE hfile;
  2419. OBJECT_ATTRIBUTES oa;
  2420. IO_STATUS_BLOCK iostatus;
  2421. UCHAR* bootVar;
  2422. ULONG bootVarSize;
  2423. UNICODE_STRING uFilePath;
  2424. UINT64 BootNumber;
  2425. UINT64 BootSize;
  2426. GUID EfiBootVariablesGuid = EFI_GLOBAL_VARIABLE;
  2427. hfile = NULL;
  2428. //
  2429. // Retrieve the NVRAM entry for the Id specified
  2430. //
  2431. swprintf( idString, L"Boot%04x", Id);
  2432. bootVarSize = 0;
  2433. status = HalGetEnvironmentVariableEx(idString,
  2434. &EfiBootVariablesGuid,
  2435. NULL,
  2436. &bootVarSize,
  2437. NULL);
  2438. if (status != STATUS_BUFFER_TOO_SMALL) {
  2439. ASSERT(FALSE);
  2440. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed to get size for boot entry buffer.\n"));
  2441. goto Done;
  2442. } else {
  2443. bootVar = SpMemAlloc(bootVarSize);
  2444. if (!bootVar) {
  2445. status = STATUS_NO_MEMORY;
  2446. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed to allocate boot entry buffer.\n"));
  2447. goto Done;
  2448. }
  2449. status = HalGetEnvironmentVariableEx(idString,
  2450. &EfiBootVariablesGuid,
  2451. bootVar,
  2452. &bootVarSize,
  2453. NULL);
  2454. if (status != STATUS_SUCCESS) {
  2455. ASSERT(FALSE);
  2456. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed to get boot entry.\n"));
  2457. goto Done;
  2458. }
  2459. }
  2460. //
  2461. // open the file
  2462. //
  2463. INIT_OBJA(&oa, &uFilePath, BootEntryPath);
  2464. status = ZwCreateFile(&hfile,
  2465. GENERIC_WRITE,
  2466. &oa,
  2467. &iostatus,
  2468. NULL,
  2469. FILE_ATTRIBUTE_NORMAL,
  2470. 0,
  2471. FILE_OVERWRITE_IF,
  2472. FILE_SYNCHRONOUS_IO_NONALERT,
  2473. NULL,
  2474. 0
  2475. );
  2476. if ( ! NT_SUCCESS(status) ) {
  2477. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed to create boot entry recovery file.\n"));
  2478. goto Done;
  2479. }
  2480. //
  2481. // Write the bits to disk using the format required
  2482. // by base/efiutil/efinvram/savrstor.c
  2483. //
  2484. // [BootNumber][BootSize][BootEntry (of BootSize)]
  2485. //
  2486. //
  2487. // build the header info for the boot entry block
  2488. //
  2489. // [header] include the boot id
  2490. BootNumber = Id;
  2491. status = ZwWriteFile( hfile,
  2492. NULL,
  2493. NULL,
  2494. NULL,
  2495. &iostatus,
  2496. &BootNumber,
  2497. sizeof(BootNumber),
  2498. NULL,
  2499. NULL
  2500. );
  2501. if ( ! NT_SUCCESS(status) ) {
  2502. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed writing boot number to boot entry recovery file.\n"));
  2503. goto Done;
  2504. }
  2505. // [header] include the boot size
  2506. BootSize = bootVarSize;
  2507. status = ZwWriteFile( hfile,
  2508. NULL,
  2509. NULL,
  2510. NULL,
  2511. &iostatus,
  2512. &BootSize,
  2513. sizeof(BootSize),
  2514. NULL,
  2515. NULL
  2516. );
  2517. if ( ! NT_SUCCESS(status) ) {
  2518. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed writing boot entry size to boot entry recovery file.\n"));
  2519. goto Done;
  2520. }
  2521. // boot entry bits
  2522. status = ZwWriteFile( hfile,
  2523. NULL,
  2524. NULL,
  2525. NULL,
  2526. &iostatus,
  2527. bootVar,
  2528. bootVarSize,
  2529. NULL,
  2530. NULL
  2531. );
  2532. if ( ! NT_SUCCESS(status) ) {
  2533. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed writing boot entry to boot entry recovery file.\n"));
  2534. goto Done;
  2535. }
  2536. Done:
  2537. //
  2538. // We are done
  2539. //
  2540. if (bootVar) {
  2541. SpMemFree(bootVar);
  2542. }
  2543. if (hfile) {
  2544. ZwClose( hfile );
  2545. }
  2546. return status;
  2547. }
  2548. BOOLEAN
  2549. SpFlushEfiBootEntries (
  2550. VOID
  2551. )
  2552. /*++
  2553. Routine Description:
  2554. Write boot entry changes back to NVRAM.
  2555. Arguments:
  2556. None.
  2557. Return Value:
  2558. BOOLEAN - FALSE if an unexpected error occurred.
  2559. --*/
  2560. {
  2561. PSP_BOOT_ENTRY bootEntry;
  2562. ULONG count;
  2563. PULONG order;
  2564. ULONG i;
  2565. NTSTATUS status;
  2566. PWSTR BootEntryFilePath;
  2567. ASSERT(SpIsEfi());
  2568. //
  2569. // Walk the list of boot entries, looking for entries that have been
  2570. // deleted. Delete these entries from NVRAM. Do not delete entries that
  2571. // are both new AND deleted; these are entries that have never been
  2572. // written to NVRAM.
  2573. //
  2574. for (bootEntry = SpBootEntries; bootEntry != NULL; bootEntry = bootEntry->Next) {
  2575. if (IS_BOOT_ENTRY_DELETED(bootEntry) &&
  2576. !IS_BOOT_ENTRY_NEW(bootEntry)) {
  2577. ASSERT(IS_BOOT_ENTRY_WINDOWS(bootEntry));
  2578. //
  2579. // Delete this boot entry.
  2580. //
  2581. status = ZwDeleteBootEntry(bootEntry->NtBootEntry.Id);
  2582. if (!NT_SUCCESS(status)) {
  2583. return FALSE;
  2584. }
  2585. }
  2586. }
  2587. //
  2588. // Walk the list of boot entries, looking for entries that have are new.
  2589. // Add these entries to NVRAM. Do not write entries that are both new AND
  2590. // deleted.
  2591. //
  2592. for (bootEntry = SpBootEntries; bootEntry != NULL; bootEntry = bootEntry->Next) {
  2593. if (IS_BOOT_ENTRY_NEW(bootEntry) &&
  2594. !IS_BOOT_ENTRY_DELETED(bootEntry)) {
  2595. ASSERT(IS_BOOT_ENTRY_WINDOWS(bootEntry));
  2596. //
  2597. // Add this boot entry.
  2598. //
  2599. status = ZwAddBootEntry(&bootEntry->NtBootEntry, &bootEntry->NtBootEntry.Id);
  2600. if (!NT_SUCCESS(status)) {
  2601. return FALSE;
  2602. }
  2603. //
  2604. // get the location we are going to store a copy of the NVRAM boot entry
  2605. //
  2606. BootEntryFilePath = NULL;
  2607. status = SpGetBootEntryFilePath(bootEntry->NtBootEntry.Id,
  2608. bootEntry->LoaderPartitionNtName,
  2609. bootEntry->LoaderFile,
  2610. &BootEntryFilePath
  2611. );
  2612. if (!NT_SUCCESS(status)) {
  2613. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed getting boot entry filepath.\n"));
  2614. } else {
  2615. ASSERT(BootEntryFilePath);
  2616. //
  2617. // Fetch the bits from the newly created NVRAM entry and
  2618. // write them as a file in the the EFI load path
  2619. //
  2620. status = SpGetAndWriteBootEntry(bootEntry->NtBootEntry.Id,
  2621. BootEntryFilePath
  2622. );
  2623. if (!NT_SUCCESS(status)) {
  2624. KdPrintEx((DPFLTR_SETUP_ID, DPFLTR_ERROR_LEVEL, "SETUP: Failed boot entry recovery file.\n"));
  2625. }
  2626. //
  2627. // We are done with the boot entry filepath
  2628. //
  2629. SpMemFree(BootEntryFilePath);
  2630. }
  2631. //
  2632. // Remember the ID of the new boot entry as the entry to be booted
  2633. // immediately on the next boot.
  2634. //
  2635. SpBootOptions->NextBootEntryId = bootEntry->NtBootEntry.Id;
  2636. }
  2637. }
  2638. //
  2639. // Build the new boot order list. Insert all boot entries with
  2640. // BE_STATUS_ORDERED into the list. (Don't insert deleted entries.)
  2641. //
  2642. count = 0;
  2643. bootEntry = SpBootEntries;
  2644. while (bootEntry != NULL) {
  2645. if (IS_BOOT_ENTRY_ORDERED(bootEntry) && !IS_BOOT_ENTRY_DELETED(bootEntry)) {
  2646. count++;
  2647. }
  2648. bootEntry = bootEntry->Next;
  2649. }
  2650. order = SpMemAlloc(count * sizeof(ULONG));
  2651. count = 0;
  2652. bootEntry = SpBootEntries;
  2653. while (bootEntry != NULL) {
  2654. if (IS_BOOT_ENTRY_ORDERED(bootEntry) && !IS_BOOT_ENTRY_DELETED(bootEntry)) {
  2655. order[count++] = bootEntry->NtBootEntry.Id;
  2656. }
  2657. bootEntry = bootEntry->Next;
  2658. }
  2659. //
  2660. // Write the new boot entry order list to NVRAM.
  2661. //
  2662. status = ZwSetBootEntryOrder(order, count);
  2663. SpMemFree(order);
  2664. if (!NT_SUCCESS(status)) {
  2665. return FALSE;
  2666. }
  2667. //
  2668. // Write the new timeout value to NVRAM.
  2669. //
  2670. // Set the boot entry we added to be booted automatically on
  2671. // the next boot, without waiting for a timeout at the boot menu.
  2672. //
  2673. // NB: SpCreateBootEntry() sets SpBootOptions->NextBootEntryId.
  2674. //
  2675. SpBootOptions->Timeout = Timeout;
  2676. status = ZwSetBootOptions(
  2677. SpBootOptions,
  2678. BOOT_OPTIONS_FIELD_TIMEOUT | BOOT_OPTIONS_FIELD_NEXT_BOOT_ENTRY_ID
  2679. );
  2680. if (!NT_SUCCESS(status)) {
  2681. return FALSE;
  2682. }
  2683. return TRUE;
  2684. } // SpFlushEfiBootEntries
  2685. BOOLEAN
  2686. SpReadAndConvertEfiBootEntries (
  2687. VOID
  2688. )
  2689. /*++
  2690. Routine Description:
  2691. Read boot entries from EFI NVRAM and convert them into our internal format.
  2692. Arguments:
  2693. None.
  2694. Return Value:
  2695. BOOLEAN - FALSE if an unexpected error occurred.
  2696. --*/
  2697. {
  2698. NTSTATUS status;
  2699. ULONG length;
  2700. PBOOT_ENTRY_LIST bootEntries;
  2701. PBOOT_ENTRY_LIST bootEntryList;
  2702. PBOOT_ENTRY bootEntry;
  2703. PBOOT_ENTRY bootEntryCopy;
  2704. PSP_BOOT_ENTRY myBootEntry;
  2705. PSP_BOOT_ENTRY previousEntry;
  2706. PWINDOWS_OS_OPTIONS osOptions;
  2707. LONG i;
  2708. PULONG order;
  2709. ULONG count;
  2710. //
  2711. // SpStartSetup() does not expect our caller, SpInitBootVars(), to fail.
  2712. // So textmode is going to continue even if we have failures here.
  2713. // Therefore we need to leave here in a consistent state. That means
  2714. // that we MUST allocate a buffer for SpBootOptions, even if we can't
  2715. // get the real information from the kernel.
  2716. //
  2717. //
  2718. // Get the global system boot options.
  2719. //
  2720. length = 0;
  2721. status = ZwQueryBootOptions(NULL, &length);
  2722. if (status != STATUS_BUFFER_TOO_SMALL) {
  2723. ASSERT(FALSE);
  2724. if (status == STATUS_SUCCESS) {
  2725. status = STATUS_UNSUCCESSFUL;
  2726. }
  2727. } else {
  2728. SpBootOptions = SpMemAlloc(length);
  2729. status = ZwQueryBootOptions(SpBootOptions, &length);
  2730. if (status != STATUS_SUCCESS) {
  2731. ASSERT(FALSE);
  2732. }
  2733. }
  2734. if (status != STATUS_SUCCESS) {
  2735. //
  2736. // An unexpected error occurred reading the boot options. Create
  2737. // a fake boot options structure.
  2738. //
  2739. if (SpBootOptions != NULL) {
  2740. SpMemFree(SpBootOptions);
  2741. }
  2742. length = FIELD_OFFSET(BOOT_OPTIONS,HeadlessRedirection) + sizeof(WCHAR);
  2743. SpBootOptions = SpMemAlloc(length);
  2744. RtlZeroMemory(SpBootOptions, length);
  2745. SpBootOptions->Version = BOOT_OPTIONS_VERSION;
  2746. SpBootOptions->Length = length;
  2747. }
  2748. //
  2749. // Get the system boot order list.
  2750. //
  2751. count = 0;
  2752. status = ZwQueryBootEntryOrder(NULL, &count);
  2753. if (status != STATUS_BUFFER_TOO_SMALL) {
  2754. if (status == STATUS_SUCCESS) {
  2755. //
  2756. // There are no entries in the boot order list. Strange but
  2757. // possible.
  2758. //
  2759. count = 0;
  2760. } else {
  2761. //
  2762. // An unexpected error occurred. Just pretend that the boot
  2763. // entry order list is empty.
  2764. //
  2765. ASSERT(FALSE);
  2766. count = 0;
  2767. }
  2768. }
  2769. if (count != 0) {
  2770. order = SpMemAlloc(count * sizeof(ULONG));
  2771. status = ZwQueryBootEntryOrder(order, &count);
  2772. if (status != STATUS_SUCCESS) {
  2773. //
  2774. // An unexpected error occurred. Just pretend that the boot
  2775. // entry order list is empty.
  2776. //
  2777. ASSERT(FALSE);
  2778. count = 0;
  2779. }
  2780. }
  2781. //
  2782. // Get all existing boot entries.
  2783. //
  2784. length = 0;
  2785. status = ZwEnumerateBootEntries(NULL, &length);
  2786. if (status != STATUS_BUFFER_TOO_SMALL) {
  2787. if (status == STATUS_SUCCESS) {
  2788. //
  2789. // Somehow there are no boot entries in NVRAM. Handle this
  2790. // by just creating an empty list.
  2791. //
  2792. length = 0;
  2793. } else {
  2794. //
  2795. // An unexpected error occurred. Just pretend that no boot
  2796. // entries exist.
  2797. //
  2798. ASSERT(FALSE);
  2799. length = 0;
  2800. }
  2801. }
  2802. if (length == 0) {
  2803. ASSERT(SpBootEntries == NULL);
  2804. } else {
  2805. bootEntries = SpMemAlloc(length);
  2806. status = ZwEnumerateBootEntries(bootEntries, &length);
  2807. if (status != STATUS_SUCCESS) {
  2808. ASSERT(FALSE);
  2809. return FALSE;
  2810. }
  2811. //
  2812. // Convert the boot entries into our internal representation.
  2813. //
  2814. bootEntryList = bootEntries;
  2815. previousEntry = NULL;
  2816. while (TRUE) {
  2817. bootEntry = &bootEntryList->BootEntry;
  2818. //
  2819. // Calculate the length of our internal structure. This includes
  2820. // the base part of SP_BOOT_ENTRY plus the NT BOOT_ENTRY.
  2821. //
  2822. length = FIELD_OFFSET(SP_BOOT_ENTRY, NtBootEntry) + bootEntry->Length;
  2823. myBootEntry = SpMemAlloc(length);
  2824. ASSERT(myBootEntry != NULL);
  2825. RtlZeroMemory(myBootEntry, length);
  2826. //
  2827. // Copy the NT BOOT_ENTRY into the allocated buffer.
  2828. //
  2829. bootEntryCopy = &myBootEntry->NtBootEntry;
  2830. memcpy(bootEntryCopy, bootEntry, bootEntry->Length);
  2831. //
  2832. // Fill in the base part of the structure.
  2833. //
  2834. myBootEntry->Next = NULL;
  2835. myBootEntry->AllocationEnd = (PUCHAR)myBootEntry + length - 1;
  2836. myBootEntry->FriendlyName = ADD_OFFSET(bootEntryCopy, FriendlyNameOffset);
  2837. myBootEntry->FriendlyNameLength = (wcslen(myBootEntry->FriendlyName) + 1) * sizeof(WCHAR);
  2838. myBootEntry->LoaderPath = ADD_OFFSET(bootEntryCopy, BootFilePathOffset);
  2839. //
  2840. // If this is an NT boot entry, translate the file paths.
  2841. //
  2842. osOptions = (PWINDOWS_OS_OPTIONS)bootEntryCopy->OsOptions;
  2843. if (IS_BOOT_ENTRY_WINDOWS(myBootEntry)) {
  2844. PSP_BOOT_ENTRY bootEntry2;
  2845. myBootEntry->OsLoadOptions = osOptions->OsLoadOptions;
  2846. myBootEntry->OsLoadOptionsLength = (wcslen(myBootEntry->OsLoadOptions) + 1) * sizeof(WCHAR);
  2847. myBootEntry->OsPath = ADD_OFFSET(osOptions, OsLoadPathOffset);
  2848. //
  2849. // Translate the OS FILE_PATH and the boot FILE_PATH. Note that
  2850. // the translation can fail when the target device is not present.
  2851. //
  2852. SpTranslateFilePathToRegion(
  2853. myBootEntry->OsPath,
  2854. &myBootEntry->OsPartitionDiskRegion,
  2855. &myBootEntry->OsPartitionNtName,
  2856. &myBootEntry->OsDirectory
  2857. );
  2858. SpTranslateFilePathToRegion(
  2859. myBootEntry->LoaderPath,
  2860. &myBootEntry->LoaderPartitionDiskRegion,
  2861. &myBootEntry->LoaderPartitionNtName,
  2862. &myBootEntry->LoaderFile
  2863. );
  2864. }
  2865. //
  2866. // Link the new entry into the list.
  2867. //
  2868. if (previousEntry != NULL) {
  2869. previousEntry->Next = myBootEntry;
  2870. } else {
  2871. SpBootEntries = myBootEntry;
  2872. }
  2873. previousEntry = myBootEntry;
  2874. //
  2875. // Move to the next entry in the enumeration list, if any.
  2876. //
  2877. if (bootEntryList->NextEntryOffset == 0) {
  2878. break;
  2879. }
  2880. bootEntryList = ADD_OFFSET(bootEntryList, NextEntryOffset);
  2881. }
  2882. //
  2883. // Free the enumeration buffer.
  2884. //
  2885. SpMemFree(bootEntries);
  2886. }
  2887. //
  2888. // Boot entries are returned in an unspecified order. They are currently
  2889. // in the SpBootEntries list in the order in which they were returned.
  2890. // Sort the boot entry list based on the boot order. Do this by walking
  2891. // the boot order array backwards, reinserting the entry corresponding to
  2892. // each element of the array at the head of the list.
  2893. //
  2894. for (i = (LONG)count - 1; i >= 0; i--) {
  2895. for (previousEntry = NULL, myBootEntry = SpBootEntries;
  2896. myBootEntry != NULL;
  2897. previousEntry = myBootEntry, myBootEntry = myBootEntry->Next) {
  2898. if (myBootEntry->NtBootEntry.Id == order[i] ) {
  2899. //
  2900. // We found the boot entry with this ID. If it's not already
  2901. // at the front of the list, move it there.
  2902. //
  2903. myBootEntry->Status |= BE_STATUS_ORDERED;
  2904. if (previousEntry != NULL) {
  2905. previousEntry->Next = myBootEntry->Next;
  2906. myBootEntry->Next = SpBootEntries;
  2907. SpBootEntries = myBootEntry;
  2908. } else {
  2909. ASSERT(SpBootEntries == myBootEntry);
  2910. }
  2911. break;
  2912. }
  2913. }
  2914. }
  2915. if (count != 0) {
  2916. SpMemFree(order);
  2917. }
  2918. return TRUE;
  2919. } // SpReadAndConvertEfiBootEntries
  2920. ULONG
  2921. SpSafeWcslen (
  2922. IN PWSTR String,
  2923. IN PWSTR Max
  2924. )
  2925. /*++
  2926. Routine Description:
  2927. Calculate the length of a null-terminated string in a safe manner,
  2928. avoiding walking off the end of the buffer if the string is not
  2929. properly terminated.
  2930. Arguments:
  2931. String - Address of string.
  2932. Max - Address of first byte beyond the maximum legal address for the
  2933. string. In other words, the address of the first byte past the end
  2934. of the buffer in which the string is contained.
  2935. Return Value:
  2936. ULONG - Length of the string, in characters, not including the null
  2937. terminator. If the string is not terminated before the end of
  2938. the buffer, 0xffffffff is returned.
  2939. --*/
  2940. {
  2941. PWSTR p = String;
  2942. //
  2943. // Walk through the string, looking for either the end of the buffer
  2944. // or a null terminator.
  2945. //
  2946. while ((p < Max) && (*p != 0)) {
  2947. p++;
  2948. }
  2949. //
  2950. // If we didn't reach the end of the buffer, then we found a null
  2951. // terminator. Return the length of the string, in characters.
  2952. //
  2953. if (p < Max) {
  2954. return (ULONG)(p - String);
  2955. }
  2956. //
  2957. // The string is not properly terminated. Return an error indicator.
  2958. //
  2959. return 0xffffffff;
  2960. } // SpSafeWcslen
  2961. VOID
  2962. SpTranslateFilePathToRegion (
  2963. IN PFILE_PATH FilePath,
  2964. OUT PDISK_REGION *DiskRegion,
  2965. OUT PWSTR *PartitionNtName,
  2966. OUT PWSTR *PartitionRelativePath
  2967. )
  2968. /*++
  2969. Routine Description:
  2970. Translate a FILE_PATH to a pointer to a disk region and the path
  2971. relative to the region.
  2972. Arguments:
  2973. FilePath - Address of FILE_PATH.
  2974. DiskRegion - Returns the address of the disk region described by
  2975. FilePath. NULL is returned if the matching disk region cannot
  2976. be found.
  2977. PartitionNtName - Returns the NT name associated with the disk region.
  2978. NULL is returned if the file path cannot be translated into NT
  2979. format.
  2980. PartitionRelativePath - Returns the volume-relative path of the file
  2981. or directory described by the FilePath. NULL is returned if the
  2982. file path cannot be translated into NT format.
  2983. Return Value:
  2984. None.
  2985. --*/
  2986. {
  2987. NTSTATUS status;
  2988. ULONG length;
  2989. PFILE_PATH ntFilePath;
  2990. PWSTR p;
  2991. PWSTR q;
  2992. PHARDDISK_NAME_TRANSLATION translation;
  2993. //
  2994. // Translate the file path into NT format. (It is probably in EFI format.)
  2995. //
  2996. length = 0;
  2997. status = ZwTranslateFilePath(
  2998. FilePath,
  2999. FILE_PATH_TYPE_NT,
  3000. NULL,
  3001. &length
  3002. );
  3003. if (status != STATUS_BUFFER_TOO_SMALL) {
  3004. *PartitionNtName = NULL;
  3005. *DiskRegion = NULL;
  3006. *PartitionRelativePath = NULL;
  3007. return;
  3008. }
  3009. ntFilePath = SpMemAlloc(length);
  3010. status = ZwTranslateFilePath(
  3011. FilePath,
  3012. FILE_PATH_TYPE_NT,
  3013. ntFilePath,
  3014. &length
  3015. );
  3016. if (status != STATUS_SUCCESS) {
  3017. ASSERT(FALSE);
  3018. *PartitionNtName = NULL;
  3019. *DiskRegion = NULL;
  3020. *PartitionRelativePath = NULL;
  3021. SpMemFree(ntFilePath);
  3022. return;
  3023. }
  3024. //
  3025. // NtTranslateFilePath returns a name of the form \Device\HarddiskVolumeN.
  3026. // We need to have a name of the form \Device\HardiskN\PartitionM. (This is
  3027. // because all of the ARC<->NT translations use the latter form.) Use the
  3028. // translation list built by SpBuildHarddiskNameTranslations to do the
  3029. // translation.
  3030. //
  3031. // If the returned name doesn't include "HarddiskVolume", or if no
  3032. // translation is found, use the returned name and hope for the best.
  3033. //
  3034. p = (PWSTR)ntFilePath->FilePath;
  3035. q = p;
  3036. if (wcsstr(q, L"Volume") != NULL){
  3037. for ( translation = SpHarddiskNameTranslations;
  3038. translation != NULL;
  3039. translation = translation->Next ) {
  3040. if (_wcsicmp(translation->VolumeName, q) == 0) {
  3041. break;
  3042. }
  3043. }
  3044. if (translation != NULL) {
  3045. q = translation->PartitionName;
  3046. }
  3047. }
  3048. //
  3049. // We now have the file path in NT format. Get the disk region that
  3050. // corresponds to the NT device name. Return the obtained information.
  3051. //
  3052. *PartitionNtName = SpDupStringW(q);
  3053. *DiskRegion = SpRegionFromNtName(q, PartitionOrdinalCurrent);
  3054. p += wcslen(p) + 1;
  3055. *PartitionRelativePath = SpDupStringW(p);
  3056. //
  3057. // Free local memory.
  3058. //
  3059. SpMemFree(ntFilePath);
  3060. return;
  3061. }
  3062. #endif // defined(EFI_NVRAM_ENABLED)
  3063. NTSTATUS
  3064. SpAddNTInstallToBootList(
  3065. IN PVOID SifHandle,
  3066. IN PDISK_REGION SystemPartitionRegion,
  3067. IN PWSTR SystemPartitionDirectory,
  3068. IN PDISK_REGION NtPartitionRegion,
  3069. IN PWSTR Sysroot,
  3070. IN PWSTR OsLoadOptions, OPTIONAL
  3071. IN PWSTR LoadIdentifier OPTIONAL
  3072. )
  3073. /*++
  3074. Routine Description:
  3075. This routine takes the core components of a boot set and passes
  3076. them on to SpAddUserDefinedInstallationToBootList, which does
  3077. the real work of constructing a boot set. After the new boot
  3078. set is created, the boot vars are flushed - the exact implementation
  3079. of the flush depends on the architecture. On amd64/x86, we'll
  3080. have a new boot.ini after this routine is done.
  3081. Arguments:
  3082. SifHandle - pointer to the setup sif file
  3083. Return Value:
  3084. STATUS_SUCCESS if the NT install was successfully added to the
  3085. boot list
  3086. if there was an error, the status is returned
  3087. --*/
  3088. {
  3089. NTSTATUS status;
  3090. //
  3091. // create the new user defined boot set
  3092. //
  3093. status = SpAddUserDefinedInstallationToBootList(SifHandle,
  3094. SystemPartitionRegion,
  3095. SystemPartitionDirectory,
  3096. NtPartitionRegion,
  3097. Sysroot,
  3098. OsLoadOptions,
  3099. LoadIdentifier
  3100. );
  3101. if (! NT_SUCCESS(status)) {
  3102. KdPrintEx((DPFLTR_SETUP_ID,
  3103. DPFLTR_ERROR_LEVEL,
  3104. "SpExportBootEntries: failed while installing new boot set: Status = %lx\n",
  3105. status
  3106. ));
  3107. return status;
  3108. }
  3109. //
  3110. // write the new boot set out
  3111. //
  3112. if (SpFlushBootVars() == FALSE) {
  3113. KdPrintEx((DPFLTR_SETUP_ID,
  3114. DPFLTR_ERROR_LEVEL,
  3115. "SpAddDiscoveredNTInstallToBootList: failed flushing boot vars\n"
  3116. ));
  3117. status = STATUS_UNSUCCESSFUL;
  3118. } else {
  3119. status = STATUS_SUCCESS;
  3120. }
  3121. return status;
  3122. }
  3123. NTSTATUS
  3124. SpAddUserDefinedInstallationToBootList(
  3125. IN PVOID SifHandle,
  3126. IN PDISK_REGION SystemPartitionRegion,
  3127. IN PWSTR SystemPartitionDirectory,
  3128. IN PDISK_REGION NtPartitionRegion,
  3129. IN PWSTR Sysroot,
  3130. IN PWSTR OsLoadOptions, OPTIONAL
  3131. IN PWSTR LoadIdentifier OPTIONAL
  3132. )
  3133. /*++
  3134. Routine Description:
  3135. This routine is based on SpAddInstallationToBootList, with the major
  3136. differences being:
  3137. there is no processing of the load options
  3138. the user can specifiy the loadIdentifier
  3139. Return Value:
  3140. STATUS_SUCCESS if the NT install was successfully added to the
  3141. boot list
  3142. if there was an error, the status is returned
  3143. --*/
  3144. {
  3145. PWSTR BootVars[MAXBOOTVARS];
  3146. PWSTR SystemPartitionArcName;
  3147. PWSTR TargetPartitionArcName;
  3148. PWSTR tmp;
  3149. PWSTR tmp2;
  3150. PWSTR locOsLoadOptions;
  3151. PWSTR locLoadIdentifier;
  3152. ULONG Signature;
  3153. ENUMARCPATHTYPE ArcPathType;
  3154. NTSTATUS status;
  3155. status = STATUS_SUCCESS;
  3156. ArcPathType = PrimaryArcPath;
  3157. tmp2 = TemporaryBuffer + (sizeof(TemporaryBuffer) / sizeof(WCHAR) / 2);
  3158. if (!SpIsEfi()) {
  3159. //
  3160. // Get an ARC name for the system partition.
  3161. //
  3162. if (SystemPartitionRegion != NULL) {
  3163. SpArcNameFromRegion(
  3164. SystemPartitionRegion,
  3165. tmp2,
  3166. sizeof(TemporaryBuffer)/2,
  3167. PartitionOrdinalOnDisk,
  3168. PrimaryArcPath
  3169. );
  3170. SystemPartitionArcName = SpDupStringW(tmp2);
  3171. } else {
  3172. SystemPartitionArcName = NULL;
  3173. }
  3174. //
  3175. // Get an ARC name for the target partition.
  3176. //
  3177. //
  3178. // If the partition is on a SCSI disk that has more than 1024 cylinders
  3179. // and the partition has sectors located on cylinders beyond cylinder
  3180. // 1024, the get the arc name in the secondary format. See also
  3181. // spcopy.c!SpCreateNtbootddSys().
  3182. //
  3183. if(
  3184. !SpIsArc() &&
  3185. #if defined(REMOTE_BOOT)
  3186. !RemoteBootSetup &&
  3187. #endif // defined(REMOTE_BOOT)
  3188. #if defined(_AMD64_) || defined(_X86_)
  3189. !SpUseBIOSToBoot(NtPartitionRegion, NULL, SifHandle) &&
  3190. #endif // defined(_AMD64_) || defined(_X86_)
  3191. (HardDisks[NtPartitionRegion->DiskNumber].ScsiMiniportShortname[0]) ) {
  3192. ArcPathType = SecondaryArcPath;
  3193. } else {
  3194. ArcPathType = PrimaryArcPath;
  3195. }
  3196. SpArcNameFromRegion(
  3197. NtPartitionRegion,
  3198. tmp2,
  3199. sizeof(TemporaryBuffer)/2,
  3200. PartitionOrdinalOnDisk,
  3201. ArcPathType
  3202. );
  3203. TargetPartitionArcName = SpDupStringW(tmp2);
  3204. }
  3205. //
  3206. // Tweak the load identifier if necessary
  3207. //
  3208. if (LoadIdentifier) {
  3209. if(!SpIsArc()) {
  3210. //
  3211. // Need quotation marks around the description on amd64/x86.
  3212. //
  3213. locLoadIdentifier = SpMemAlloc((wcslen(LoadIdentifier)+3)*sizeof(WCHAR));
  3214. locLoadIdentifier[0] = L'\"';
  3215. wcscpy(locLoadIdentifier+1,LoadIdentifier);
  3216. wcscat(locLoadIdentifier,L"\"");
  3217. } else {
  3218. locLoadIdentifier = SpDupStringW(LoadIdentifier);
  3219. }
  3220. } else {
  3221. locLoadIdentifier = SpDupStringW(L"");
  3222. }
  3223. ASSERT(locLoadIdentifier);
  3224. //
  3225. // Tweak the load options if necessary
  3226. //
  3227. if (OsLoadOptions) {
  3228. locOsLoadOptions = SpDupStringW(OsLoadOptions);
  3229. } else {
  3230. locOsLoadOptions = SpDupStringW(L"");
  3231. }
  3232. ASSERT(locOsLoadOptions);
  3233. //
  3234. // Create a new internal-format boot entry.
  3235. //
  3236. tmp = TemporaryBuffer;
  3237. wcscpy(tmp,SystemPartitionDirectory);
  3238. SpConcatenatePaths(
  3239. tmp,
  3240. #if defined(_AMD64_) || defined(_X86_)
  3241. SpIsArc() ? L"arcldr.exe" : L"ntldr"
  3242. #elif defined(_IA64_)
  3243. L"ia64ldr.efi"
  3244. #else
  3245. L"osloader.exe"
  3246. #endif // defined(_AMD64_) || defined(_X86_)
  3247. );
  3248. tmp = SpDupStringW(tmp);
  3249. SpCreateBootEntry(
  3250. BE_STATUS_NEW,
  3251. SystemPartitionRegion,
  3252. tmp,
  3253. NtPartitionRegion,
  3254. Sysroot,
  3255. locOsLoadOptions,
  3256. locLoadIdentifier
  3257. );
  3258. SpMemFree(tmp);
  3259. //
  3260. // If not on an EFI machine, add a new ARC-style boot set.
  3261. //
  3262. if (!SpIsEfi()) {
  3263. BootVars[OSLOADOPTIONS] = locOsLoadOptions;
  3264. BootVars[LOADIDENTIFIER] = locLoadIdentifier;
  3265. //
  3266. // OSLOADER is the system partition path + the system partition directory +
  3267. // osloader.exe. (ntldr on amd64 or x86 machines).
  3268. //
  3269. if (SystemPartitionRegion != NULL) {
  3270. tmp = TemporaryBuffer;
  3271. wcscpy(tmp,SystemPartitionArcName);
  3272. SpConcatenatePaths(tmp,SystemPartitionDirectory);
  3273. SpConcatenatePaths(
  3274. tmp,
  3275. #if defined(_AMD64_) || defined(_X86_)
  3276. (SpIsArc() ? L"arcldr.exe" : L"ntldr")
  3277. #elif defined(_IA64_)
  3278. L"ia64ldr.efi"
  3279. #else
  3280. L"osloader.exe"
  3281. #endif // defined(_AMD64_) || defined(_X86_)
  3282. );
  3283. BootVars[OSLOADER] = SpDupStringW(tmp);
  3284. } else {
  3285. BootVars[OSLOADER] = SpDupStringW(L"");
  3286. }
  3287. //
  3288. // OSLOADPARTITION is the ARC name of the windows nt partition.
  3289. //
  3290. BootVars[OSLOADPARTITION] = TargetPartitionArcName;
  3291. //
  3292. // OSLOADFILENAME is sysroot.
  3293. //
  3294. BootVars[OSLOADFILENAME] = Sysroot;
  3295. //
  3296. // SYSTEMPARTITION is the ARC name of the system partition.
  3297. //
  3298. if (SystemPartitionRegion != NULL) {
  3299. BootVars[SYSTEMPARTITION] = SystemPartitionArcName;
  3300. } else {
  3301. BootVars[SYSTEMPARTITION] = L"";
  3302. }
  3303. //
  3304. // get the disk signature
  3305. //
  3306. if ((NtPartitionRegion->DiskNumber != 0xffffffff) && HardDisks[NtPartitionRegion->DiskNumber].Signature) {
  3307. Signature = HardDisks[NtPartitionRegion->DiskNumber].Signature;
  3308. } else {
  3309. Signature = 0;
  3310. }
  3311. //
  3312. // Add the boot set and make it the default.
  3313. //
  3314. SpAddBootSet(BootVars, TRUE, Signature);
  3315. SpMemFree(BootVars[OSLOADER]);
  3316. }
  3317. //
  3318. // Free memory allocated.
  3319. //
  3320. if (locLoadIdentifier) {
  3321. SpMemFree(locLoadIdentifier);
  3322. }
  3323. if (!SpIsEfi()) {
  3324. if (SystemPartitionArcName) {
  3325. SpMemFree(SystemPartitionArcName);
  3326. }
  3327. if (TargetPartitionArcName) {
  3328. SpMemFree(TargetPartitionArcName);
  3329. }
  3330. }
  3331. return status;
  3332. }
  3333. NTSTATUS
  3334. SpExportBootEntries(
  3335. IN OUT PLIST_ENTRY BootEntries,
  3336. OUT PULONG BootEntryCnt
  3337. )
  3338. /*++
  3339. Routine Description:
  3340. This routine compiles a safely exportable string represenation
  3341. of the boot options.
  3342. Arguments:
  3343. BootEntries - returns pointing to the head of the linked list
  3344. containing the exported boot entries
  3345. BootEntriesCnt - returns with the # of boot entries exported
  3346. Return Value:
  3347. STATUS_SUCCESS if the boot entries were successfully exported
  3348. if there was an error, the status is returned
  3349. --*/
  3350. {
  3351. PSP_BOOT_ENTRY bootEntry;
  3352. PSP_EXPORTED_BOOT_ENTRY ebootEntry;
  3353. *BootEntryCnt = 0;
  3354. //
  3355. // make sure we were given the list head
  3356. //
  3357. ASSERT(BootEntries);
  3358. if (!BootEntries) {
  3359. KdPrintEx((DPFLTR_SETUP_ID,
  3360. DPFLTR_ERROR_LEVEL,
  3361. "SpExportBootEntries: pointer to boot entry list is NULL\n"
  3362. ));
  3363. return STATUS_INVALID_PARAMETER;
  3364. }
  3365. //
  3366. // make sure the list is empty
  3367. //
  3368. ASSERT(IsListEmpty(BootEntries));
  3369. if (! IsListEmpty(BootEntries)) {
  3370. KdPrintEx((DPFLTR_SETUP_ID,
  3371. DPFLTR_ERROR_LEVEL,
  3372. "SpExportBootEntries: incoming boot entry list should be empty\n"
  3373. ));
  3374. return STATUS_INVALID_PARAMETER;
  3375. }
  3376. //
  3377. // for each boot entry, collect a subset of information and compile
  3378. // it in an exportable (safe) string form
  3379. //
  3380. for (bootEntry = SpBootEntries; bootEntry != NULL; bootEntry = bootEntry->Next) {
  3381. //
  3382. // allocate the node...
  3383. //
  3384. ebootEntry = SpMemAlloc(sizeof(SP_EXPORTED_BOOT_ENTRY));
  3385. ASSERT(ebootEntry);
  3386. if (ebootEntry == NULL) {
  3387. KdPrintEx((DPFLTR_SETUP_ID,
  3388. DPFLTR_ERROR_LEVEL,
  3389. "SpExportBootEntries: failed allocationg new exported boot entry\n"
  3390. ));
  3391. return STATUS_NO_MEMORY;
  3392. }
  3393. RtlZeroMemory( ebootEntry, sizeof(SP_EXPORTED_BOOT_ENTRY) );
  3394. //
  3395. // map selected fields from SpBootEntries to our export
  3396. //
  3397. ebootEntry->LoadIdentifier = SpDupStringW(bootEntry->FriendlyName);
  3398. ebootEntry->OsLoadOptions = SpDupStringW(bootEntry->OsLoadOptions);
  3399. ebootEntry->DriverLetter = bootEntry->OsPartitionDiskRegion->DriveLetter;
  3400. ebootEntry->OsDirectory = SpDupStringW(bootEntry->OsDirectory);
  3401. InsertTailList( BootEntries, &ebootEntry->ListEntry );
  3402. ++*BootEntryCnt;
  3403. }
  3404. if (*BootEntryCnt == 0) {
  3405. ASSERT(IsListEmpty(BootEntries));
  3406. if(! IsListEmpty(BootEntries)) {
  3407. KdPrintEx((DPFLTR_SETUP_ID,
  3408. DPFLTR_ERROR_LEVEL,
  3409. "SpExportBootEntries: exported boot entry list should be empty\n"
  3410. ));
  3411. return STATUS_UNSUCCESSFUL;
  3412. }
  3413. } else {
  3414. ASSERT(! IsListEmpty(BootEntries));
  3415. if(IsListEmpty(BootEntries)) {
  3416. KdPrintEx((DPFLTR_SETUP_ID,
  3417. DPFLTR_ERROR_LEVEL,
  3418. "SpExportBootEntries: exported boot entry list should NOT be empty\n"
  3419. ));
  3420. return STATUS_UNSUCCESSFUL;
  3421. }
  3422. }
  3423. return STATUS_SUCCESS;
  3424. }
  3425. NTSTATUS
  3426. SpFreeExportedBootEntries(
  3427. IN PLIST_ENTRY BootEntries,
  3428. IN ULONG BootEntryCnt
  3429. )
  3430. /*++
  3431. Routine Description:
  3432. A convenience routine to free the exported boot entries
  3433. Arguments:
  3434. BootEntries - points to the head of the linked list
  3435. containing the exported boot entries
  3436. BootEntriesCnt - the # of boot entries exported
  3437. Return Value:
  3438. STATUS_SUCCESS if the exported boot entries were successfully freed
  3439. if there was an error, the status is returned
  3440. --*/
  3441. {
  3442. PSP_EXPORTED_BOOT_ENTRY bootEntry;
  3443. PLIST_ENTRY listEntry;
  3444. ULONG cnt;
  3445. NTSTATUS status;
  3446. cnt = 0;
  3447. while ( !IsListEmpty(BootEntries) ) {
  3448. listEntry = RemoveHeadList(BootEntries);
  3449. bootEntry = CONTAINING_RECORD(listEntry,
  3450. SP_EXPORTED_BOOT_ENTRY,
  3451. ListEntry
  3452. );
  3453. if (bootEntry->LoadIdentifier) {
  3454. SpMemFree(bootEntry->LoadIdentifier);
  3455. }
  3456. if (bootEntry->OsLoadOptions) {
  3457. SpMemFree(bootEntry->OsLoadOptions);
  3458. }
  3459. if (bootEntry->OsDirectory) {
  3460. SpMemFree(bootEntry->OsDirectory);
  3461. }
  3462. SpMemFree(bootEntry);
  3463. cnt++;
  3464. }
  3465. ASSERT(cnt == BootEntryCnt);
  3466. if (cnt == BootEntryCnt) {
  3467. status = STATUS_SUCCESS;
  3468. } else {
  3469. KdPrintEx((DPFLTR_SETUP_ID,
  3470. DPFLTR_ERROR_LEVEL,
  3471. "SpFreeExportedBootEntries: incorrect # of boot entries freed\n"
  3472. ));
  3473. status = STATUS_UNSUCCESSFUL;
  3474. }
  3475. return status;
  3476. }
  3477. NTSTATUS
  3478. SpSetRedirectSwitchMode(
  3479. IN RedirectSwitchesModeEnum mode,
  3480. IN PCHAR redirectSwitch,
  3481. IN PCHAR redirectBaudRateSwitch
  3482. )
  3483. /*++
  3484. Routine Description:
  3485. This routine is used to manage how the redirect switches
  3486. are set in the boot configuration (amd64/x86 ==> boot.ini)
  3487. Depending on the mode chosen, the user may specify
  3488. which parameters they want to set or if they just
  3489. want the default (legacy) behavior.
  3490. NOTE:
  3491. The user specified switches are copied into globals for
  3492. use by the Flush routines.
  3493. The global, RedirectSwitchesMode, is set and remains set
  3494. after this routine returns. All subsequent FlushBootVars
  3495. will use this mode.
  3496. Arguments:
  3497. mode - how we affect the redirect switches
  3498. redirectSwitch - the user defined redirect parameter
  3499. redirectBaudRateSwitch - the user defined baudrate paramtere
  3500. Return Value:
  3501. STATUS_SUCCESS if the redirect values were successfully set
  3502. if there was an error, the status is returned
  3503. --*/
  3504. {
  3505. NTSTATUS status;
  3506. //
  3507. // set the mode and user defined parameters
  3508. //
  3509. RedirectSwitchesMode = mode;
  3510. //
  3511. // null the redirect switches by default
  3512. //
  3513. RedirectSwitches.port[0] = '\0';
  3514. RedirectSwitches.baudrate[0] = '\0';
  3515. //
  3516. // get copies of the user defined switches if specified
  3517. //
  3518. if (redirectSwitch) {
  3519. strncpy(RedirectSwitches.port,
  3520. redirectSwitch,
  3521. MAXSIZE_REDIRECT_SWITCH);
  3522. }
  3523. if (redirectBaudRateSwitch) {
  3524. strncpy(RedirectSwitches.baudrate,
  3525. redirectBaudRateSwitch,
  3526. MAXSIZE_REDIRECT_SWITCH);
  3527. }
  3528. //
  3529. // update the boot options using the specified mode
  3530. //
  3531. if (SpFlushBootVars() == FALSE) {
  3532. KdPrintEx((DPFLTR_SETUP_ID,
  3533. DPFLTR_ERROR_LEVEL,
  3534. "SpAddDiscoveredNTInstallToBootList: failed flushing boot vars\n"
  3535. ));
  3536. status = STATUS_UNSUCCESSFUL;
  3537. } else {
  3538. status = STATUS_SUCCESS;
  3539. }
  3540. return status;
  3541. }
  3542. NTSTATUS
  3543. SpSetDefaultBootEntry(
  3544. ULONG BootEntryNumber
  3545. )
  3546. /*++
  3547. Routine Description:
  3548. Set the Default boot entry to the user specified boot entry.
  3549. Arguments:
  3550. BootEntryNumber - the position of the boot entry in the list
  3551. which is intended to become the default.
  3552. This number should be >= 1.
  3553. Return Value:
  3554. STATUS_SUCCESS if the default was successfully set
  3555. STATUS_NOT_FOUND if the specified boot entry was not found
  3556. or is missing
  3557. if there was an error, the status is returned
  3558. --*/
  3559. {
  3560. PSP_BOOT_ENTRY bootEntry;
  3561. NTSTATUS status;
  3562. ULONG BootEntryCount;
  3563. //
  3564. // Find the user specified boot entry
  3565. //
  3566. BootEntryCount = 1;
  3567. for (bootEntry = SpBootEntries;
  3568. (bootEntry != NULL) && (BootEntryCount != BootEntryNumber);
  3569. bootEntry = bootEntry->Next) {
  3570. ++BootEntryCount;
  3571. }
  3572. ASSERT(BootEntryCount == BootEntryNumber);
  3573. ASSERT(bootEntry);
  3574. //
  3575. // if we have found our match, then set the Default
  3576. //
  3577. if ((bootEntry != NULL) &&
  3578. (BootEntryCount == BootEntryNumber)) {
  3579. PDISK_REGION Region;
  3580. //
  3581. // point to the disk region with the sig info
  3582. //
  3583. Region = bootEntry->OsPartitionDiskRegion;
  3584. ASSERT(Region);
  3585. if (! Region) {
  3586. KdPrintEx((DPFLTR_SETUP_ID,
  3587. DPFLTR_ERROR_LEVEL,
  3588. "SpSetDefaultBootEntry: new default partition region is NULL\n"
  3589. ));
  3590. return STATUS_UNSUCCESSFUL;
  3591. }
  3592. //
  3593. // Free the previous Default
  3594. //
  3595. if( Default ) {
  3596. SpMemFree( Default );
  3597. }
  3598. Default = SpMemAlloc( MAX_PATH * sizeof(WCHAR) );
  3599. ASSERT( Default );
  3600. if (! Default) {
  3601. KdPrintEx((DPFLTR_SETUP_ID,
  3602. DPFLTR_ERROR_LEVEL,
  3603. "SpSetDefaultBootEntry: failed to allocate new Default\n"
  3604. ));
  3605. return STATUS_UNSUCCESSFUL;
  3606. }
  3607. //
  3608. // fetch the arc name for the region
  3609. //
  3610. SpArcNameFromRegion(
  3611. Region,
  3612. TemporaryBuffer,
  3613. sizeof(TemporaryBuffer)/2,
  3614. PartitionOrdinalOnDisk,
  3615. PrimaryArcPath
  3616. );
  3617. //
  3618. // store the new partition and directory info
  3619. //
  3620. wcscpy( Default, TemporaryBuffer);
  3621. SpConcatenatePaths(Default, bootEntry->OsDirectory);
  3622. //
  3623. // get the disk signature of the new default disk
  3624. //
  3625. if ((Region->DiskNumber != 0xffffffff) && HardDisks[Region->DiskNumber].Signature) {
  3626. DefaultSignature = HardDisks[Region->DiskNumber].Signature;
  3627. } else {
  3628. DefaultSignature = 0;
  3629. }
  3630. //
  3631. // update the boot options using the specified mode
  3632. //
  3633. if(SpFlushBootVars() == FALSE) {
  3634. KdPrintEx((DPFLTR_SETUP_ID,
  3635. DPFLTR_ERROR_LEVEL,
  3636. "SpSetDefaultBootEntry: failed flushing boot vars\n"
  3637. ));
  3638. status = STATUS_UNSUCCESSFUL;
  3639. } else {
  3640. status = STATUS_SUCCESS;
  3641. }
  3642. } else {
  3643. KdPrintEx((DPFLTR_SETUP_ID,
  3644. DPFLTR_ERROR_LEVEL,
  3645. "SpSetDefaultBootEntry: failed to find specified boot entry to use as default\n"
  3646. ));
  3647. status = STATUS_NOT_FOUND;
  3648. }
  3649. return status;
  3650. }
  3651. #if defined(EFI_NVRAM_ENABLED)
  3652. NTSTATUS
  3653. SpUpdateDriverEntry(
  3654. IN PCWSTR DriverName,
  3655. IN PCWSTR FriendlyName,
  3656. IN PCWSTR SrcNtDevice,
  3657. IN PCWSTR SrcDir,
  3658. IN PCWSTR DestNtDevice OPTIONAL,
  3659. IN PCWSTR DestDir OPTIONAL
  3660. )
  3661. /*++
  3662. Routine Description:
  3663. Updates the driver entry for the specified driver.
  3664. If there's no driver entry for the driver, it creates a new one and copies the driver from the source location to the
  3665. destination location. If there is already a driver entry, the function will not change it; if necessary, the latest
  3666. version of the driver will be copied from the source location to the location pointed to by the entry.
  3667. Arguments:
  3668. DriverName - the file name of the driver (no path)
  3669. FriendlyName - if the function needs to create a new driver entry, this will be its description
  3670. SrcNtDevice - NT device name of the location where the driver should be copied from
  3671. SrcDir - path (relative to SrcNtDevice) to the location where the driver should be copied from
  3672. DestNtDevice - NT device name of the location where the driver should be copied to. If NULL, SrcNTDevice will be used.
  3673. DestDir - path( relative to DestNtDevice) to the location where the driver should be copied to. If NULL, SrcDir
  3674. will be used. If the function needs to create a new driver entry, it will point to DestNTDevice\DestDir.
  3675. Return value:
  3676. STATUS_SUCCESS if successful, otherwise an error status.
  3677. --*/
  3678. {
  3679. NTSTATUS Status = STATUS_SUCCESS;
  3680. PWSTR SrcPath = NULL; // path to the source driver file
  3681. PWSTR DestPath = NULL; // path to the destination file
  3682. PWSTR SrcFullNtPath = NULL; // full nt path (device + path) for source
  3683. PWSTR DestFullNtPath = NULL; // full nt path (device + path) for destination
  3684. PEFI_DRIVER_ENTRY_LIST DriverList = NULL; // list of driver entries
  3685. PEFI_DRIVER_ENTRY DriverEntry = NULL; // the new entry to be added
  3686. PFILE_PATH DriverOptionPath = NULL; // file path to the existing driver entry
  3687. PWSTR OldDriverDevice = NULL; // nt device of the existing driver file
  3688. PWSTR OldDriverPath = NULL; // path to the existing driver file
  3689. PWSTR OldDriverFullPath = NULL; // full nt path to the existing driver file
  3690. PULONG DriverEntryOrder = NULL; // holds the array of driver entries
  3691. ULONG EntryId; // ID of the existing or newly added driver entry
  3692. BOOLEAN SameSrcDest; // true if the source and destination dirs are the same
  3693. ULONG Length = 0;
  3694. if(NULL == DriverName || NULL == FriendlyName || NULL == SrcNtDevice || NULL == SrcDir) {
  3695. Status = STATUS_INVALID_PARAMETER;
  3696. goto exit;
  3697. }
  3698. if(NULL == DestNtDevice) {
  3699. DestNtDevice = SrcNtDevice;
  3700. }
  3701. if(NULL == DestDir) {
  3702. DestDir = SrcDir;
  3703. }
  3704. Status = ZwEnumerateDriverEntries(NULL, &Length);
  3705. if(!NT_SUCCESS(Status)) {
  3706. PEFI_DRIVER_ENTRY_LIST Entry;
  3707. BOOLEAN bContinue;
  3708. if(Status != STATUS_BUFFER_TOO_SMALL) {
  3709. goto exit;
  3710. }
  3711. ASSERT(Length != 0);
  3712. DriverList = (PEFI_DRIVER_ENTRY_LIST) SpMemAlloc(Length);
  3713. Status = ZwEnumerateDriverEntries(DriverList, &Length);
  3714. if(!NT_SUCCESS(Status)) {
  3715. goto exit;
  3716. }
  3717. //
  3718. // Search the list of entries for our driver
  3719. //
  3720. bContinue = TRUE;
  3721. for(Entry = DriverList; bContinue; Entry = (PEFI_DRIVER_ENTRY_LIST) ((PCHAR) Entry + Entry->NextEntryOffset)) {
  3722. PFILE_PATH FilePath = (PFILE_PATH) ((PCHAR) &Entry->DriverEntry + Entry->DriverEntry.DriverFilePathOffset);
  3723. ULONG PathLength;
  3724. PCWSTR FileName;
  3725. bContinue = (Entry->NextEntryOffset != 0);
  3726. EntryId = Entry->DriverEntry.Id;
  3727. if(FilePath->Type != FILE_PATH_TYPE_NT) {
  3728. PVOID Buffer;
  3729. PathLength = 0;
  3730. Status = ZwTranslateFilePath(FilePath, FILE_PATH_TYPE_NT, NULL, &PathLength);
  3731. if(NT_SUCCESS(Status)) {
  3732. Status = STATUS_UNSUCCESSFUL;
  3733. }
  3734. if(STATUS_BUFFER_TOO_SMALL == Status) {
  3735. ASSERT(PathLength != 0);
  3736. if(DriverOptionPath != NULL) {
  3737. SpMemFree(DriverOptionPath);
  3738. }
  3739. DriverOptionPath = SpMemAlloc(PathLength);
  3740. Status = ZwTranslateFilePath(FilePath, FILE_PATH_TYPE_NT, DriverOptionPath, &PathLength);
  3741. }
  3742. if(!NT_SUCCESS(Status)) {
  3743. if(STATUS_OBJECT_PATH_NOT_FOUND == Status || STATUS_OBJECT_NAME_NOT_FOUND == Status) {
  3744. //
  3745. // This entry is stale; remove it
  3746. //
  3747. ZwDeleteDriverEntry(EntryId);
  3748. }
  3749. continue;
  3750. }
  3751. FilePath = DriverOptionPath;
  3752. }
  3753. PathLength = wcslen((PCWSTR) DriverOptionPath->FilePath) + 1;
  3754. FileName = wcsrchr((PCWSTR) DriverOptionPath->FilePath + PathLength, L'\\');
  3755. if(FileName != NULL && 0 == _wcsicmp(DriverName, FileName + 1)) {
  3756. OldDriverDevice = SpDupStringW((PCWSTR) DriverOptionPath->FilePath);
  3757. OldDriverPath = SpDupStringW((PCWSTR) DriverOptionPath->FilePath + PathLength);
  3758. wcscpy(TemporaryBuffer, OldDriverDevice);
  3759. SpConcatenatePaths(TemporaryBuffer, OldDriverPath);
  3760. OldDriverFullPath = SpDupStringW(TemporaryBuffer);
  3761. break;
  3762. }
  3763. }
  3764. }
  3765. //
  3766. // Build the NT paths for source and dest
  3767. //
  3768. wcscpy(TemporaryBuffer, SrcDir);
  3769. SpConcatenatePaths(TemporaryBuffer, DriverName);
  3770. SrcPath = SpDupStringW(TemporaryBuffer);
  3771. wcscpy(TemporaryBuffer, SrcNtDevice);
  3772. SpConcatenatePaths(TemporaryBuffer, SrcPath);
  3773. SrcFullNtPath = SpDupStringW(TemporaryBuffer);
  3774. wcscpy(TemporaryBuffer, DestDir);
  3775. SpConcatenatePaths(TemporaryBuffer, DriverName);
  3776. DestPath = SpDupStringW(TemporaryBuffer);
  3777. wcscpy(TemporaryBuffer, DestNtDevice);
  3778. SpConcatenatePaths(TemporaryBuffer, DestPath);
  3779. DestFullNtPath = SpDupStringW(TemporaryBuffer);
  3780. //
  3781. // Note that there can be different ways to specify the NT path so
  3782. // the caller should not use different forms for source and destination.
  3783. //
  3784. SameSrcDest = (0 == _wcsicmp(SrcFullNtPath, DestFullNtPath));
  3785. if(OldDriverFullPath != NULL) {
  3786. //
  3787. // There is already an entry for our driver; compare the versions
  3788. //
  3789. ULONGLONG VersionOld;
  3790. ULONGLONG VersionNew;
  3791. Status = SpGetFileVersionFromPath(OldDriverFullPath, &VersionOld);
  3792. if(STATUS_OBJECT_NAME_NOT_FOUND == Status || STATUS_OBJECT_PATH_NOT_FOUND == Status)
  3793. {
  3794. //
  3795. // This entry is stale; remove it
  3796. //
  3797. ZwDeleteDriverEntry(EntryId);
  3798. goto create_entry;
  3799. }
  3800. if(!NT_SUCCESS(Status)) {
  3801. goto exit;
  3802. }
  3803. Status = SpGetFileVersionFromPath(SrcFullNtPath, &VersionNew);
  3804. if(!NT_SUCCESS(Status)) {
  3805. goto exit;
  3806. }
  3807. if(VersionOld < VersionNew) {
  3808. //
  3809. // Copy the new driver and leave the driver entry alone
  3810. //
  3811. Status = SpCopyFileUsingNames((PWSTR) SrcFullNtPath, OldDriverFullPath, 0, COPY_NODECOMP);
  3812. }
  3813. } else {
  3814. ULONG FriendlyNameOffset;
  3815. ULONG FriendlyNameLength;
  3816. ULONG NtDeviceLength;
  3817. ULONG DestPathLength;
  3818. ULONG FilePathLength;
  3819. ULONG EntryLength;
  3820. PFILE_PATH FilePath;
  3821. create_entry:
  3822. //
  3823. // Copy the driver to its destination if not already there.
  3824. //
  3825. if(!SameSrcDest) {
  3826. //
  3827. // Make sure the dest dir is present; if this fails, the file copy will fail too
  3828. //
  3829. SpCreateDirectory(DestNtDevice, NULL, DestDir, 0, CREATE_DIRECTORY_FLAG_SKIPPABLE);
  3830. Status = SpCopyFileUsingNames((PWSTR) SrcFullNtPath, (PWSTR) DestFullNtPath, 0, COPY_NODECOMP);
  3831. if(!NT_SUCCESS(Status)) {
  3832. goto exit;
  3833. }
  3834. }
  3835. //
  3836. // Add a new driver entry
  3837. //
  3838. FriendlyNameOffset = ALIGN_UP(sizeof(EFI_DRIVER_ENTRY), WCHAR);
  3839. FriendlyNameLength = (wcslen(FriendlyName) + 1) * sizeof(WCHAR);
  3840. NtDeviceLength = (wcslen(DestNtDevice) + 1) * sizeof(WCHAR);
  3841. DestPathLength = (wcslen(DestPath) + 1) * sizeof(WCHAR);
  3842. FilePathLength = FIELD_OFFSET(FILE_PATH, FilePath) + NtDeviceLength + DestPathLength;
  3843. EntryLength = FriendlyNameOffset + ALIGN_UP(FriendlyNameLength, ULONG) + FilePathLength;
  3844. DriverEntry = SpMemAlloc(EntryLength);
  3845. DriverEntry->Version = EFI_DRIVER_ENTRY_VERSION;
  3846. DriverEntry->Length = EntryLength;
  3847. DriverEntry->FriendlyNameOffset = FriendlyNameOffset;
  3848. DriverEntry->DriverFilePathOffset = FriendlyNameOffset + ALIGN_UP(FriendlyNameLength, ULONG);
  3849. RtlCopyMemory((PCHAR) DriverEntry + DriverEntry->FriendlyNameOffset, FriendlyName, FriendlyNameLength);
  3850. FilePath = (PFILE_PATH) ((PCHAR) DriverEntry + DriverEntry->DriverFilePathOffset);
  3851. FilePath->Version = FILE_PATH_VERSION;
  3852. FilePath->Length = FilePathLength;
  3853. FilePath->Type = FILE_PATH_TYPE_NT;
  3854. RtlCopyMemory(FilePath->FilePath, DestNtDevice, NtDeviceLength);
  3855. RtlCopyMemory(FilePath->FilePath + NtDeviceLength, DestPath, DestPathLength);
  3856. Status = ZwAddDriverEntry(DriverEntry, &EntryId);
  3857. if(!NT_SUCCESS(Status)) {
  3858. goto exit;
  3859. }
  3860. Length = 0;
  3861. Status = ZwQueryDriverEntryOrder(NULL, &Length);
  3862. if(!NT_SUCCESS(Status) && Status != STATUS_BUFFER_TOO_SMALL) {
  3863. goto exit;
  3864. }
  3865. DriverEntryOrder = (PULONG) SpMemAlloc((Length + 1) * sizeof(ULONG));
  3866. if(Length != 0) {
  3867. Status = ZwQueryDriverEntryOrder(DriverEntryOrder, &Length);
  3868. if(!NT_SUCCESS(Status)) {
  3869. goto exit;
  3870. }
  3871. }
  3872. DriverEntryOrder[Length] = EntryId;
  3873. Status = ZwSetDriverEntryOrder(DriverEntryOrder, Length + 1);
  3874. }
  3875. //
  3876. // Delete the source file
  3877. //
  3878. if(!SameSrcDest) {
  3879. SpDeleteFile(SrcFullNtPath, NULL, NULL);
  3880. }
  3881. exit:
  3882. if(SrcPath != NULL) {
  3883. SpMemFree(SrcPath);
  3884. }
  3885. if(DestPath != NULL) {
  3886. SpMemFree(DestPath);
  3887. }
  3888. if(SrcFullNtPath != NULL) {
  3889. SpMemFree(SrcFullNtPath);
  3890. }
  3891. if(DestFullNtPath != NULL) {
  3892. SpMemFree(DestFullNtPath);
  3893. }
  3894. if(DriverList != NULL) {
  3895. SpMemFree(DriverList);
  3896. }
  3897. if(OldDriverDevice != NULL) {
  3898. SpMemFree(OldDriverDevice);
  3899. }
  3900. if(OldDriverPath != NULL) {
  3901. SpMemFree(OldDriverPath);
  3902. }
  3903. if(OldDriverFullPath != NULL) {
  3904. SpMemFree(OldDriverFullPath);
  3905. }
  3906. if(DriverEntry != NULL) {
  3907. SpMemFree(DriverEntry);
  3908. }
  3909. if(DriverOptionPath != NULL) {
  3910. SpMemFree(DriverOptionPath);
  3911. }
  3912. if(DriverEntryOrder != NULL) {
  3913. SpMemFree(DriverEntryOrder);
  3914. }
  3915. return Status;
  3916. }
  3917. #endif // EFI_NVRAM_ENABLED