bconf.c 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354
  1. /*++
  2. Copyright (c) 2014 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. bconf.c
  5. Abstract:
  6. This module implements the Boot Configuration Library.
  7. Author:
  8. Evan Green 20-Feb-2014
  9. Environment:
  10. Any
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include <minoca/kernel/kernel.h>
  16. #include <minoca/lib/bconf.h>
  17. #include "bconfp.h"
  18. //
  19. // --------------------------------------------------------------------- Macros
  20. //
  21. //
  22. // Define some macros for memory allocation.
  23. //
  24. #define BcAllocate(_Context, _Size) (_Context)->AllocateFunction(_Size)
  25. #define BcFree(_Context, _Memory) (_Context)->FreeFunction(_Memory)
  26. //
  27. // ---------------------------------------------------------------- Definitions
  28. //
  29. //
  30. // Define the initial string table allocation size.
  31. //
  32. #define INITIAL_BOOT_CONFIGURATION_STRING_TABLE_SIZE 1024
  33. //
  34. // ------------------------------------------------------ Data Type Definitions
  35. //
  36. //
  37. // ----------------------------------------------- Internal Function Prototypes
  38. //
  39. KSTATUS
  40. BcpParseBootEntry (
  41. PBOOT_CONFIGURATION_CONTEXT Context,
  42. ULONG EntryIndex,
  43. PBOOT_ENTRY *Entry
  44. );
  45. VOID
  46. BcpDestroyBootEntries (
  47. PBOOT_CONFIGURATION_CONTEXT Context
  48. );
  49. KSTATUS
  50. BcpValidateHeader (
  51. PBOOT_CONFIGURATION_CONTEXT Context
  52. );
  53. KSTATUS
  54. BcpReadString (
  55. PBOOT_CONFIGURATION_CONTEXT Context,
  56. ULONG StringOffset,
  57. PSTR *String
  58. );
  59. KSTATUS
  60. BcpAddToStringTable (
  61. PBOOT_CONFIGURATION_CONTEXT Context,
  62. PSTR String,
  63. PULONG StringIndex,
  64. PVOID *StringTable,
  65. PULONG StringTableSize,
  66. PULONG StringTableCapacity
  67. );
  68. PSTR
  69. BcpCopyString (
  70. PBOOT_CONFIGURATION_CONTEXT Context,
  71. PSTR String
  72. );
  73. //
  74. // -------------------------------------------------------------------- Globals
  75. //
  76. //
  77. // ------------------------------------------------------------------ Functions
  78. //
  79. KSTATUS
  80. BcInitializeContext (
  81. PBOOT_CONFIGURATION_CONTEXT Context
  82. )
  83. /*++
  84. Routine Description:
  85. This routine initializes the given boot configuration context.
  86. Arguments:
  87. Context - Supplies a pointer to the context to initialize. The caller must
  88. have filled in the allocate and free functions, optionally filled in
  89. the file data, and zeroed the rest of the structure.
  90. Return Value:
  91. Status code.
  92. --*/
  93. {
  94. if ((Context->AllocateFunction == NULL) ||
  95. (Context->FreeFunction == NULL)) {
  96. ASSERT(FALSE);
  97. return STATUS_INVALID_PARAMETER;
  98. }
  99. Context->BootEntries = NULL;
  100. Context->BootEntryCount = 0;
  101. return STATUS_SUCCESS;
  102. }
  103. VOID
  104. BcDestroyContext (
  105. PBOOT_CONFIGURATION_CONTEXT Context
  106. )
  107. /*++
  108. Routine Description:
  109. This routine destroys the given boot configuration context. It will free
  110. all resources contained in the structure, including the file data.
  111. Arguments:
  112. Context - Supplies a pointer to the context to destroy.
  113. Return Value:
  114. None.
  115. --*/
  116. {
  117. BcpDestroyBootEntries(Context);
  118. if (Context->FileData != NULL) {
  119. BcFree(Context, Context->FileData);
  120. Context->FileData = NULL;
  121. }
  122. Context->FileDataSize = 0;
  123. return;
  124. }
  125. VOID
  126. BcDestroyBootEntry (
  127. PBOOT_CONFIGURATION_CONTEXT Context,
  128. PBOOT_ENTRY Entry
  129. )
  130. /*++
  131. Routine Description:
  132. This routine destroys the given boot entry, freeing all its resources.
  133. Arguments:
  134. Context - Supplies a pointer to the context containing the entry.
  135. Entry - Supplies a pointer to the entry to destroy.
  136. Return Value:
  137. None.
  138. --*/
  139. {
  140. if (Entry->Name != NULL) {
  141. BcFree(Context, Entry->Name);
  142. }
  143. if (Entry->LoaderArguments != NULL) {
  144. BcFree(Context, Entry->LoaderArguments);
  145. }
  146. if (Entry->KernelArguments != NULL) {
  147. BcFree(Context, Entry->KernelArguments);
  148. }
  149. if (Entry->LoaderPath != NULL) {
  150. BcFree(Context, Entry->LoaderPath);
  151. }
  152. if (Entry->KernelPath != NULL) {
  153. BcFree(Context, Entry->KernelPath);
  154. }
  155. if (Entry->SystemPath != NULL) {
  156. BcFree(Context, Entry->SystemPath);
  157. }
  158. BcFree(Context, Entry);
  159. return;
  160. }
  161. KSTATUS
  162. BcReadBootConfigurationFile (
  163. PBOOT_CONFIGURATION_CONTEXT Context
  164. )
  165. /*++
  166. Routine Description:
  167. This routine parses the boot configuration file out into boot entries that
  168. can be manipulated by consumers of this library.
  169. Arguments:
  170. Context - Supplies a pointer to the context. The file data and file data
  171. size must have been filled in by the caller.
  172. Return Value:
  173. Status code.
  174. --*/
  175. {
  176. PBOOT_ENTRY *EntryArray;
  177. ULONG EntryIndex;
  178. PBOOT_CONFIGURATION_HEADER Header;
  179. KSTATUS Status;
  180. EntryArray = NULL;
  181. Header = NULL;
  182. //
  183. // Destroy any previous boot entries.
  184. //
  185. BcpDestroyBootEntries(Context);
  186. if ((Context->FileData == NULL) || (Context->FileDataSize == 0)) {
  187. Status = STATUS_NOT_INITIALIZED;
  188. goto ReadBootConfigurationFileEnd;
  189. }
  190. //
  191. // Validate the header (which checksums the whole file as well).
  192. //
  193. Status = BcpValidateHeader(Context);
  194. if (!KSUCCESS(Status)) {
  195. goto ReadBootConfigurationFileEnd;
  196. }
  197. Header = Context->FileData;
  198. Context->GlobalConfiguration.Key = Header->Key;
  199. Context->GlobalConfiguration.DefaultBootEntry = NULL;
  200. Context->GlobalConfiguration.BootOnce = NULL;
  201. Context->GlobalConfiguration.Timeout = Header->Timeout;
  202. EntryArray = BcAllocate(Context, sizeof(PVOID) * Header->EntryCount);
  203. if (EntryArray == NULL) {
  204. Status = STATUS_INSUFFICIENT_RESOURCES;
  205. goto ReadBootConfigurationFileEnd;
  206. }
  207. RtlZeroMemory(EntryArray, sizeof(PVOID) * Header->EntryCount);
  208. for (EntryIndex = 0; EntryIndex < Header->EntryCount; EntryIndex += 1) {
  209. Status = BcpParseBootEntry(Context,
  210. EntryIndex,
  211. &(EntryArray[EntryIndex]));
  212. if (!KSUCCESS(Status)) {
  213. goto ReadBootConfigurationFileEnd;
  214. }
  215. }
  216. //
  217. // Set the new array of boot entries.
  218. //
  219. Context->BootEntries = EntryArray;
  220. Context->BootEntryCount = Header->EntryCount;
  221. Status = STATUS_SUCCESS;
  222. ReadBootConfigurationFileEnd:
  223. if (!KSUCCESS(Status)) {
  224. if (EntryArray != NULL) {
  225. for (EntryIndex = 0;
  226. EntryIndex < Header->EntryCount;
  227. EntryIndex += 1) {
  228. BcDestroyBootEntry(Context, EntryArray[EntryIndex]);
  229. }
  230. BcFree(Context, EntryArray);
  231. EntryArray = NULL;
  232. }
  233. }
  234. return Status;
  235. }
  236. KSTATUS
  237. BcWriteBootConfigurationFile (
  238. PBOOT_CONFIGURATION_CONTEXT Context
  239. )
  240. /*++
  241. Routine Description:
  242. This routine writes the boot entries into the file buffer.
  243. Arguments:
  244. Context - Supplies a pointer to the context. If there is existing file
  245. data it will be freed, and new file data will be allocated.
  246. Return Value:
  247. Status code.
  248. --*/
  249. {
  250. UINTN AllocationSize;
  251. PBOOT_ENTRY Entry;
  252. ULONG EntryIndex;
  253. PBOOT_CONFIGURATION_ENTRY FileEntries;
  254. PBOOT_CONFIGURATION_ENTRY FileEntry;
  255. PBOOT_CONFIGURATION_HEADER FinalHeader;
  256. BOOT_CONFIGURATION_HEADER Header;
  257. PVOID NewFileData;
  258. KSTATUS Status;
  259. PVOID StringTable;
  260. ULONG StringTableCapacity;
  261. ULONG StringTableSize;
  262. FileEntries = NULL;
  263. NewFileData = NULL;
  264. StringTable = NULL;
  265. StringTableCapacity = 0;
  266. StringTableSize = 0;
  267. //
  268. // Initialize the header.
  269. //
  270. RtlZeroMemory(&Header, sizeof(BOOT_CONFIGURATION_HEADER));
  271. Header.DefaultEntry = -1;
  272. Header.BootOnce = -1;
  273. //
  274. // Create the boot entries array.
  275. //
  276. AllocationSize = Context->BootEntryCount * sizeof(BOOT_CONFIGURATION_ENTRY);
  277. FileEntries = BcAllocate(Context, AllocationSize);
  278. if (FileEntries == NULL) {
  279. Status = STATUS_INSUFFICIENT_RESOURCES;
  280. goto WriteBootConfigurationFileEnd;
  281. }
  282. RtlZeroMemory(FileEntries, AllocationSize);
  283. //
  284. // Create each of the boot entries, adding strings to the table along the
  285. // way.
  286. //
  287. for (EntryIndex = 0;
  288. EntryIndex < Context->BootEntryCount;
  289. EntryIndex += 1) {
  290. Entry = Context->BootEntries[EntryIndex];
  291. FileEntry = &(FileEntries[EntryIndex]);
  292. //
  293. // Re-number the boot entry ID numbers. Also save this new ID back into
  294. // the boot entry.
  295. //
  296. FileEntry->Id = EntryIndex + 1;
  297. Entry->Id = FileEntry->Id;
  298. FileEntry->Flags = Entry->Flags;
  299. FileEntry->DebugDevice = Entry->DebugDevice;
  300. RtlCopyMemory(&(FileEntry->DiskId),
  301. &(Entry->DiskId),
  302. BOOT_DISK_ID_SIZE);
  303. RtlCopyMemory(&(FileEntry->PartitionId),
  304. &(Entry->PartitionId),
  305. BOOT_PARTITION_ID_SIZE);
  306. Status = BcpAddToStringTable(Context,
  307. Entry->Name,
  308. &(FileEntry->Name),
  309. &StringTable,
  310. &StringTableSize,
  311. &StringTableCapacity);
  312. if (!KSUCCESS(Status)) {
  313. goto WriteBootConfigurationFileEnd;
  314. }
  315. Status = BcpAddToStringTable(Context,
  316. Entry->LoaderArguments,
  317. &(FileEntry->LoaderArguments),
  318. &StringTable,
  319. &StringTableSize,
  320. &StringTableCapacity);
  321. if (!KSUCCESS(Status)) {
  322. goto WriteBootConfigurationFileEnd;
  323. }
  324. Status = BcpAddToStringTable(Context,
  325. Entry->KernelArguments,
  326. &(FileEntry->KernelArguments),
  327. &StringTable,
  328. &StringTableSize,
  329. &StringTableCapacity);
  330. if (!KSUCCESS(Status)) {
  331. goto WriteBootConfigurationFileEnd;
  332. }
  333. Status = BcpAddToStringTable(Context,
  334. Entry->LoaderPath,
  335. &(FileEntry->LoaderPath),
  336. &StringTable,
  337. &StringTableSize,
  338. &StringTableCapacity);
  339. if (!KSUCCESS(Status)) {
  340. goto WriteBootConfigurationFileEnd;
  341. }
  342. Status = BcpAddToStringTable(Context,
  343. Entry->KernelPath,
  344. &(FileEntry->KernelPath),
  345. &StringTable,
  346. &StringTableSize,
  347. &StringTableCapacity);
  348. if (!KSUCCESS(Status)) {
  349. goto WriteBootConfigurationFileEnd;
  350. }
  351. Status = BcpAddToStringTable(Context,
  352. Entry->SystemPath,
  353. &(FileEntry->SystemPath),
  354. &StringTable,
  355. &StringTableSize,
  356. &StringTableCapacity);
  357. if (!KSUCCESS(Status)) {
  358. goto WriteBootConfigurationFileEnd;
  359. }
  360. //
  361. // If this is the default or boot once entries, fill in the ID now.
  362. //
  363. if (Context->GlobalConfiguration.DefaultBootEntry == Entry) {
  364. Header.DefaultEntry = FileEntry->Id;
  365. }
  366. if (Context->GlobalConfiguration.BootOnce == Entry) {
  367. Header.BootOnce = FileEntry->Id;
  368. }
  369. }
  370. Header.Magic = BOOT_CONFIGURATION_HEADER_MAGIC;
  371. Header.Version = BOOT_CONFIGURATION_VERSION;
  372. Header.Key = Context->GlobalConfiguration.Key + 1;
  373. Header.EntriesOffset = sizeof(BOOT_CONFIGURATION_HEADER);
  374. Header.EntrySize = sizeof(BOOT_CONFIGURATION_ENTRY);
  375. Header.EntryCount = Context->BootEntryCount;
  376. Header.StringsOffset = Header.EntriesOffset +
  377. (Header.EntrySize * Header.EntryCount);
  378. Header.StringsSize = StringTableSize;
  379. Header.TotalSize = Header.StringsOffset + Header.StringsSize;
  380. Header.Timeout = Context->GlobalConfiguration.Timeout;
  381. //
  382. // Allocate and write out the new file data.
  383. //
  384. NewFileData = BcAllocate(Context, Header.TotalSize);
  385. if (NewFileData == NULL) {
  386. Status = STATUS_INSUFFICIENT_RESOURCES;
  387. goto WriteBootConfigurationFileEnd;
  388. }
  389. RtlCopyMemory(NewFileData, &Header, sizeof(BOOT_CONFIGURATION_HEADER));
  390. RtlCopyMemory(NewFileData + Header.EntriesOffset,
  391. FileEntries,
  392. Header.EntrySize * Header.EntryCount);
  393. RtlCopyMemory(NewFileData + Header.StringsOffset,
  394. StringTable,
  395. Header.StringsSize);
  396. //
  397. // Compute the CRC32 over the entire buffer.
  398. //
  399. FinalHeader = NewFileData;
  400. FinalHeader->Crc32 = RtlComputeCrc32(0, NewFileData, Header.TotalSize);
  401. //
  402. // Free the old file data if there was any, and install this new data.
  403. //
  404. if (Context->FileData != NULL) {
  405. BcFree(Context, Context->FileData);
  406. }
  407. Context->FileData = NewFileData;
  408. Context->FileDataSize = Header.TotalSize;
  409. NewFileData = NULL;
  410. Status = STATUS_SUCCESS;
  411. WriteBootConfigurationFileEnd:
  412. if (NewFileData != NULL) {
  413. BcFree(Context, NewFileData);
  414. }
  415. if (FileEntries != NULL) {
  416. BcFree(Context, FileEntries);
  417. }
  418. if (StringTable != NULL) {
  419. BcFree(Context, StringTable);
  420. }
  421. return Status;
  422. }
  423. KSTATUS
  424. BcCreateDefaultBootConfiguration (
  425. PBOOT_CONFIGURATION_CONTEXT Context,
  426. UCHAR DiskId[BOOT_DISK_ID_SIZE],
  427. UCHAR PartitionId[BOOT_PARTITION_ID_SIZE]
  428. )
  429. /*++
  430. Routine Description:
  431. This routine sets up the boot configuration data, creating a single default
  432. entry.
  433. Arguments:
  434. Context - Supplies a pointer to the boot configuration context.
  435. DiskId - Supplies the disk ID of the boot entry.
  436. PartitionId - Supplies the partition ID of the boot entry.
  437. Return Value:
  438. Returns a pointer to the new boot entry on success.
  439. NULL on allocation failure.
  440. --*/
  441. {
  442. PBOOT_ENTRY *Entries;
  443. KSTATUS Status;
  444. //
  445. // Create a new boot entry array, and then create a single default boot
  446. // entry.
  447. //
  448. Status = STATUS_INSUFFICIENT_RESOURCES;
  449. Entries = BcAllocate(Context, sizeof(PVOID));
  450. if (Entries == NULL) {
  451. goto CreateDefaultBootConfigurationEnd;
  452. }
  453. Entries[0] = BcCreateDefaultBootEntry(Context, NULL, DiskId, PartitionId);
  454. if (Entries[0] == NULL) {
  455. goto CreateDefaultBootConfigurationEnd;
  456. }
  457. BcpDestroyBootEntries(Context);
  458. Context->GlobalConfiguration.DefaultBootEntry = Entries[0];
  459. Context->GlobalConfiguration.BootOnce = NULL;
  460. Context->GlobalConfiguration.Timeout = BOOT_DEFAULT_TIMEOUT;
  461. Context->BootEntries = Entries;
  462. Context->BootEntryCount = 1;
  463. Entries = NULL;
  464. Status = STATUS_SUCCESS;
  465. CreateDefaultBootConfigurationEnd:
  466. if (Entries != NULL) {
  467. if (Entries[0] != NULL) {
  468. BcDestroyBootEntry(Context, Entries[0]);
  469. }
  470. BcFree(Context, Entries);
  471. }
  472. return Status;
  473. }
  474. PBOOT_ENTRY
  475. BcCreateDefaultBootEntry (
  476. PBOOT_CONFIGURATION_CONTEXT Context,
  477. PSTR Name,
  478. UCHAR DiskId[BOOT_DISK_ID_SIZE],
  479. UCHAR PartitionId[BOOT_PARTITION_ID_SIZE]
  480. )
  481. /*++
  482. Routine Description:
  483. This routine creates a new boot entry with the default values.
  484. Arguments:
  485. Context - Supplies a pointer to the boot configuration context.
  486. Name - Supplies an optional pointer to a string containing the name of the
  487. entry. A copy of this string will be made. If no name is supplied, a
  488. default name will be used.
  489. DiskId - Supplies the disk ID of the boot entry.
  490. PartitionId - Supplies the partition ID of the boot entry.
  491. Return Value:
  492. Returns a pointer to the new boot entry on success.
  493. NULL on allocation failure.
  494. --*/
  495. {
  496. PBOOT_ENTRY Entry;
  497. KSTATUS Status;
  498. Status = STATUS_INSUFFICIENT_RESOURCES;
  499. Entry = BcAllocate(Context, sizeof(BOOT_ENTRY));
  500. if (Entry == NULL) {
  501. goto CreateDefaultBootEntryEnd;
  502. }
  503. RtlZeroMemory(Entry, sizeof(BOOT_ENTRY));
  504. Entry->Id = 0;
  505. RtlCopyMemory(Entry->DiskId, DiskId, sizeof(Entry->DiskId));
  506. RtlCopyMemory(Entry->PartitionId, PartitionId, sizeof(Entry->PartitionId));
  507. if (Name != NULL) {
  508. Entry->Name = BcpCopyString(Context, Name);
  509. } else {
  510. Entry->Name = BcpCopyString(Context, BOOT_DEFAULT_NAME);
  511. }
  512. if (Entry->Name == NULL) {
  513. goto CreateDefaultBootEntryEnd;
  514. }
  515. Entry->LoaderPath = BcpCopyString(Context, BOOT_DEFAULT_LOADER_PATH);
  516. if (Entry->LoaderPath == NULL) {
  517. goto CreateDefaultBootEntryEnd;
  518. }
  519. Entry->KernelPath = BcpCopyString(Context, BOOT_DEFAULT_KERNEL_PATH);
  520. if (Entry->KernelPath == NULL) {
  521. goto CreateDefaultBootEntryEnd;
  522. }
  523. Entry->SystemPath = BcpCopyString(Context, BOOT_DEFAULT_SYSTEM_PATH);
  524. if (Entry->SystemPath == NULL) {
  525. goto CreateDefaultBootEntryEnd;
  526. }
  527. Status = STATUS_SUCCESS;
  528. CreateDefaultBootEntryEnd:
  529. if (!KSUCCESS(Status)) {
  530. if (Entry != NULL) {
  531. BcDestroyBootEntry(Context, Entry);
  532. Entry = NULL;
  533. }
  534. }
  535. return Entry;
  536. }
  537. PBOOT_ENTRY
  538. BcCopyBootEntry (
  539. PBOOT_CONFIGURATION_CONTEXT Context,
  540. PBOOT_ENTRY Source
  541. )
  542. /*++
  543. Routine Description:
  544. This routine creates a new boot entry based on an existing one.
  545. Arguments:
  546. Context - Supplies a pointer to the boot configuration context.
  547. Source - Supplies a pointer to the boot entry to copy.
  548. Return Value:
  549. Returns a pointer to the new boot entry on success.
  550. NULL on allocation failure.
  551. --*/
  552. {
  553. PBOOT_ENTRY Entry;
  554. KSTATUS Status;
  555. PSTR String;
  556. Status = STATUS_INSUFFICIENT_RESOURCES;
  557. Entry = BcAllocate(Context, sizeof(BOOT_ENTRY));
  558. if (Entry == NULL) {
  559. goto CreateDefaultBootEntryEnd;
  560. }
  561. RtlZeroMemory(Entry, sizeof(BOOT_ENTRY));
  562. Entry->Id = Source->Id;
  563. RtlCopyMemory(Entry->DiskId, Source->DiskId, sizeof(Entry->DiskId));
  564. RtlCopyMemory(Entry->PartitionId,
  565. Source->PartitionId,
  566. sizeof(Entry->PartitionId));
  567. Entry->Flags = Source->Flags;
  568. Entry->DebugDevice = Source->DebugDevice;
  569. String = Source->Name;
  570. if (String == NULL) {
  571. String = BOOT_DEFAULT_NAME;
  572. }
  573. Entry->Name = BcpCopyString(Context, String);
  574. if (Entry->Name == NULL) {
  575. goto CreateDefaultBootEntryEnd;
  576. }
  577. String = Source->LoaderArguments;
  578. if (String != NULL) {
  579. Entry->LoaderArguments = BcpCopyString(Context, String);
  580. if (Entry->LoaderArguments == NULL) {
  581. goto CreateDefaultBootEntryEnd;
  582. }
  583. }
  584. String = Source->KernelArguments;
  585. if (String != NULL) {
  586. Entry->KernelArguments = BcpCopyString(Context, String);
  587. if (Entry->KernelArguments == NULL) {
  588. goto CreateDefaultBootEntryEnd;
  589. }
  590. }
  591. String = Source->LoaderPath;
  592. if (String == NULL) {
  593. String = BOOT_DEFAULT_LOADER_PATH;
  594. }
  595. Entry->LoaderPath = BcpCopyString(Context, String);
  596. if (Entry->LoaderPath == NULL) {
  597. goto CreateDefaultBootEntryEnd;
  598. }
  599. String = Source->KernelPath;
  600. if (String == NULL) {
  601. String = BOOT_DEFAULT_KERNEL_PATH;
  602. }
  603. Entry->KernelPath = BcpCopyString(Context, String);
  604. if (Entry->KernelPath == NULL) {
  605. goto CreateDefaultBootEntryEnd;
  606. }
  607. String = Source->SystemPath;
  608. if (String == NULL) {
  609. String = BOOT_DEFAULT_SYSTEM_PATH;
  610. }
  611. Entry->SystemPath = BcpCopyString(Context, String);
  612. if (Entry->SystemPath == NULL) {
  613. goto CreateDefaultBootEntryEnd;
  614. }
  615. Status = STATUS_SUCCESS;
  616. CreateDefaultBootEntryEnd:
  617. if (!KSUCCESS(Status)) {
  618. if (Entry != NULL) {
  619. BcDestroyBootEntry(Context, Entry);
  620. Entry = NULL;
  621. }
  622. }
  623. return Entry;
  624. }
  625. //
  626. // --------------------------------------------------------- Internal Functions
  627. //
  628. KSTATUS
  629. BcpParseBootEntry (
  630. PBOOT_CONFIGURATION_CONTEXT Context,
  631. ULONG EntryIndex,
  632. PBOOT_ENTRY *Entry
  633. )
  634. /*++
  635. Routine Description:
  636. This routine parses a single boot entry.
  637. Arguments:
  638. Context - Supplies a pointer to the boot configuration context.
  639. EntryIndex - Supplies the entry number to parse.
  640. Entry - Supplies a pointer where a pointer to the entry will be returned
  641. on success.
  642. Return Value:
  643. Status code.
  644. --*/
  645. {
  646. PBOOT_CONFIGURATION_ENTRY FileEntry;
  647. PBOOT_CONFIGURATION_HEADER Header;
  648. PBOOT_ENTRY NewEntry;
  649. KSTATUS Status;
  650. NewEntry = BcAllocate(Context, sizeof(BOOT_ENTRY));
  651. if (NewEntry == NULL) {
  652. Status = STATUS_INSUFFICIENT_RESOURCES;
  653. goto ParseBootEntryEnd;
  654. }
  655. RtlZeroMemory(NewEntry, sizeof(BOOT_ENTRY));
  656. Header = Context->FileData;
  657. FileEntry = Context->FileData + Header->EntriesOffset +
  658. (EntryIndex * Header->EntrySize);
  659. NewEntry->Id = FileEntry->Id;
  660. NewEntry->Flags = FileEntry->Flags;
  661. NewEntry->DebugDevice = FileEntry->DebugDevice;
  662. RtlCopyMemory(&(NewEntry->DiskId), &(FileEntry->DiskId), BOOT_DISK_ID_SIZE);
  663. RtlCopyMemory(&(NewEntry->PartitionId),
  664. &(FileEntry->PartitionId),
  665. BOOT_PARTITION_ID_SIZE);
  666. Status = BcpReadString(Context, FileEntry->Name, &(NewEntry->Name));
  667. if (!KSUCCESS(Status)) {
  668. goto ParseBootEntryEnd;
  669. }
  670. Status = BcpReadString(Context,
  671. FileEntry->LoaderArguments,
  672. &(NewEntry->LoaderArguments));
  673. if (!KSUCCESS(Status)) {
  674. goto ParseBootEntryEnd;
  675. }
  676. Status = BcpReadString(Context,
  677. FileEntry->KernelArguments,
  678. &(NewEntry->KernelArguments));
  679. if (!KSUCCESS(Status)) {
  680. goto ParseBootEntryEnd;
  681. }
  682. Status = BcpReadString(Context,
  683. FileEntry->LoaderPath,
  684. &(NewEntry->LoaderPath));
  685. if (!KSUCCESS(Status)) {
  686. goto ParseBootEntryEnd;
  687. }
  688. Status = BcpReadString(Context,
  689. FileEntry->KernelPath,
  690. &(NewEntry->KernelPath));
  691. if (!KSUCCESS(Status)) {
  692. goto ParseBootEntryEnd;
  693. }
  694. Status = BcpReadString(Context,
  695. FileEntry->SystemPath,
  696. &(NewEntry->SystemPath));
  697. if (!KSUCCESS(Status)) {
  698. goto ParseBootEntryEnd;
  699. }
  700. //
  701. // If the IDs match for the default boot entry or boot once entry, set
  702. // those pointers now.
  703. //
  704. if (Header->DefaultEntry == FileEntry->Id) {
  705. Context->GlobalConfiguration.DefaultBootEntry = NewEntry;
  706. }
  707. if (Header->BootOnce == FileEntry->Id) {
  708. Context->GlobalConfiguration.BootOnce = NewEntry;
  709. }
  710. Status = STATUS_SUCCESS;
  711. ParseBootEntryEnd:
  712. if (!KSUCCESS(Status)) {
  713. if (NewEntry != NULL) {
  714. BcDestroyBootEntry(Context, NewEntry);
  715. NewEntry = NULL;
  716. }
  717. }
  718. *Entry = NewEntry;
  719. return Status;
  720. }
  721. VOID
  722. BcpDestroyBootEntries (
  723. PBOOT_CONFIGURATION_CONTEXT Context
  724. )
  725. /*++
  726. Routine Description:
  727. This routine destroys the boot entries in the given context.
  728. Arguments:
  729. Context - Supplies a pointer to the context.
  730. Return Value:
  731. None.
  732. --*/
  733. {
  734. PBOOT_ENTRY Entry;
  735. ULONG EntryIndex;
  736. for (EntryIndex = 0;
  737. EntryIndex < Context->BootEntryCount;
  738. EntryIndex += 1) {
  739. Entry = Context->BootEntries[EntryIndex];
  740. if (Entry != NULL) {
  741. BcDestroyBootEntry(Context, Entry);
  742. }
  743. }
  744. if (Context->BootEntries != NULL) {
  745. BcFree(Context, Context->BootEntries);
  746. }
  747. Context->BootEntries = NULL;
  748. Context->BootEntryCount = 0;
  749. return;
  750. }
  751. KSTATUS
  752. BcpValidateHeader (
  753. PBOOT_CONFIGURATION_CONTEXT Context
  754. )
  755. /*++
  756. Routine Description:
  757. This routine performs sanity checks on the boot configuration file to make
  758. sure it's valid.
  759. Arguments:
  760. Context - Supplies a pointer to the context. The file data must be filled
  761. in.
  762. Return Value:
  763. Status code.
  764. --*/
  765. {
  766. ULONG ComputedCrc;
  767. PBOOT_CONFIGURATION_HEADER Header;
  768. ULONG HeaderCrc;
  769. PUCHAR LastCharacter;
  770. ULONG MaxEntries;
  771. KSTATUS Status;
  772. Status = STATUS_FILE_CORRUPT;
  773. if (Context->FileDataSize < sizeof(BOOT_CONFIGURATION_HEADER)) {
  774. goto ValidateHeaderEnd;
  775. }
  776. //
  777. // Check that the header is there.
  778. //
  779. Header = Context->FileData;
  780. if ((Header->Magic != BOOT_CONFIGURATION_HEADER_MAGIC) ||
  781. (Header->TotalSize < sizeof(BOOT_CONFIGURATION_HEADER))) {
  782. goto ValidateHeaderEnd;
  783. }
  784. //
  785. // Make sure the size reported is at least as big as the data buffer here.
  786. //
  787. if (Header->TotalSize > Context->FileDataSize) {
  788. Status = STATUS_DATA_LENGTH_MISMATCH;
  789. goto ValidateHeaderEnd;
  790. }
  791. //
  792. // Sanity check the offsets.
  793. //
  794. if ((Header->EntrySize >= Header->TotalSize) ||
  795. (Header->EntriesOffset >= Header->TotalSize) ||
  796. (Header->StringsOffset >= Header->TotalSize) ||
  797. (Header->StringsSize > Header->TotalSize) ||
  798. (Header->StringsOffset + Header->StringsSize > Header->TotalSize) ||
  799. (Header->StringsSize == 0)) {
  800. goto ValidateHeaderEnd;
  801. }
  802. //
  803. // Perform an estimate on the maximum entries that could possibly fit to
  804. // weed out multiplication overflow issues below.
  805. //
  806. MaxEntries = Header->TotalSize / Header->EntrySize;
  807. if (Header->EntryCount >= MaxEntries) {
  808. goto ValidateHeaderEnd;
  809. }
  810. //
  811. // Now that it's at least reasonable, check the actual bounds.
  812. //
  813. if ((Header->EntriesOffset + (Header->EntryCount * Header->EntrySize)) >
  814. Header->TotalSize) {
  815. goto ValidateHeaderEnd;
  816. }
  817. //
  818. // Ensure the last character of the string table is a terminator.
  819. //
  820. LastCharacter = (PUCHAR)Header + Header->StringsOffset +
  821. Header->StringsSize - 1;
  822. if (*LastCharacter != '\0') {
  823. Status = STATUS_INVALID_SEQUENCE;
  824. goto ValidateHeaderEnd;
  825. }
  826. //
  827. // Compute the CRC of the whole file.
  828. //
  829. HeaderCrc = Header->Crc32;
  830. Header->Crc32 = 0;
  831. ComputedCrc = RtlComputeCrc32(0, Header, Header->TotalSize);
  832. Header->Crc32 = HeaderCrc;
  833. if (ComputedCrc != HeaderCrc) {
  834. Status = STATUS_CHECKSUM_MISMATCH;
  835. goto ValidateHeaderEnd;
  836. }
  837. //
  838. // Lookin real good.
  839. //
  840. Status = STATUS_SUCCESS;
  841. ValidateHeaderEnd:
  842. return Status;
  843. }
  844. KSTATUS
  845. BcpReadString (
  846. PBOOT_CONFIGURATION_CONTEXT Context,
  847. ULONG StringOffset,
  848. PSTR *String
  849. )
  850. /*++
  851. Routine Description:
  852. This routine reads a string out of the given string table.
  853. Arguments:
  854. Context - Supplies a pointer to the boot configuration context.
  855. StringOffset - Supplies the offset into the string table.
  856. String - Supplies a pointer where a pointer to a copy of the string will be
  857. returned on success. The caller is responsible for freeing this memory
  858. when finished.
  859. Return Value:
  860. Status code.
  861. --*/
  862. {
  863. PBOOT_CONFIGURATION_HEADER Header;
  864. PSTR TableString;
  865. *String = NULL;
  866. Header = Context->FileData;
  867. if (StringOffset >= Header->StringsSize) {
  868. return STATUS_BUFFER_OVERRUN;
  869. }
  870. TableString = Context->FileData + Header->StringsOffset + StringOffset;
  871. *String = BcpCopyString(Context, TableString);
  872. if (*String == NULL) {
  873. return STATUS_INSUFFICIENT_RESOURCES;
  874. }
  875. return STATUS_SUCCESS;
  876. }
  877. KSTATUS
  878. BcpAddToStringTable (
  879. PBOOT_CONFIGURATION_CONTEXT Context,
  880. PSTR String,
  881. PULONG StringIndex,
  882. PVOID *StringTable,
  883. PULONG StringTableSize,
  884. PULONG StringTableCapacity
  885. )
  886. /*++
  887. Routine Description:
  888. This routine adds a string to the given string table.
  889. Arguments:
  890. Context - Supplies a pointer to the boot configuration context.
  891. String - Supplies a pointer to the string to add.
  892. StringIndex - Supplies a pointer where the index of the given string in
  893. the string table will be returned.
  894. StringTable - Supplies a pointer that on input contains the string table.
  895. This may get reallocated if the string table needs to expand.
  896. StringTableSize - Supplies a pointer that on input contains the size of the
  897. string table in bytes. On output, this value will be updated to contain
  898. the new size of the string table with this new string added.
  899. StringTableCapacity - Supplies a pointer that on input contains the size of
  900. the string table allocation. This may be updated if the string table
  901. is reallocated.
  902. Return Value:
  903. Status code.
  904. --*/
  905. {
  906. ULONG Length;
  907. PUCHAR NewBuffer;
  908. ULONG NewCapacity;
  909. if (String == NULL) {
  910. String = "";
  911. }
  912. Length = RtlStringLength(String);
  913. //
  914. // Reallocate if needed.
  915. //
  916. if (*StringTableSize + Length + 1 > *StringTableCapacity) {
  917. if (*StringTableCapacity == 0) {
  918. NewCapacity = INITIAL_BOOT_CONFIGURATION_STRING_TABLE_SIZE;
  919. } else {
  920. NewCapacity = *StringTableCapacity * 2;
  921. }
  922. while (NewCapacity < *StringTableSize + Length + 1) {
  923. ASSERT(NewCapacity * 2 > NewCapacity);
  924. NewCapacity *= 2;
  925. }
  926. NewBuffer = BcAllocate(Context, NewCapacity);
  927. if (NewBuffer == NULL) {
  928. return STATUS_INSUFFICIENT_RESOURCES;
  929. }
  930. //
  931. // If the string table is just being created, populate it with the
  932. // empty string. Otherwise, copy the previous contents over.
  933. //
  934. if (*StringTableSize == 0) {
  935. *NewBuffer = '\0';
  936. *StringTableSize = 1;
  937. } else {
  938. RtlCopyMemory(NewBuffer, *StringTable, *StringTableSize);
  939. BcFree(Context, *StringTable);
  940. }
  941. *StringTableCapacity = NewCapacity;
  942. *StringTable = NewBuffer;
  943. }
  944. //
  945. // If the length is zero, reuse the empty string. Otherwise, add this
  946. // onto the end.
  947. //
  948. if (Length != 0) {
  949. *StringIndex = *StringTableSize;
  950. RtlCopyMemory(*StringTable + *StringTableSize, String, Length + 1);
  951. *StringTableSize += Length + 1;
  952. } else {
  953. *StringIndex = 0;
  954. }
  955. return STATUS_SUCCESS;
  956. }
  957. PSTR
  958. BcpCopyString (
  959. PBOOT_CONFIGURATION_CONTEXT Context,
  960. PSTR String
  961. )
  962. /*++
  963. Routine Description:
  964. This routine allocates and copies the given string.
  965. Arguments:
  966. Context - Supplies a pointer to the boot configuration context.
  967. String - Supplies a pointer to the string to copy.
  968. Return Value:
  969. Returns a pointer to a newly allocated copy of the given string. The caller
  970. is responsible for freeing this memory when finished with it.
  971. NULL on allocation failure.
  972. --*/
  973. {
  974. PSTR Copy;
  975. UINTN Length;
  976. Length = RtlStringLength(String);
  977. Copy = BcAllocate(Context, Length + 1);
  978. if (Copy == NULL) {
  979. return NULL;
  980. }
  981. RtlCopyMemory(Copy, String, Length + 1);
  982. return Copy;
  983. }