bootman.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610
  1. /*++
  2. Copyright (c) 2014 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. bootman.c
  9. Abstract:
  10. This module loads the selected operating system loader into memory and
  11. jumps to it.
  12. Author:
  13. Evan Green 21-Feb-2014
  14. Environment:
  15. Boot
  16. --*/
  17. //
  18. // ------------------------------------------------------------------- Includes
  19. //
  20. #include <minoca/kernel/kernel.h>
  21. #include <minoca/lib/fat/fat.h>
  22. #include "firmware.h"
  23. #include "bootlib.h"
  24. #include <minoca/lib/basevid.h>
  25. #include "bootman.h"
  26. //
  27. // ---------------------------------------------------------------- Definitions
  28. //
  29. #define BOOT_MANAGER_BINARY_NAME_MAX_SIZE 16
  30. #define BOOT_MANAGER_MODULE_BUFFER_SIZE \
  31. (sizeof(DEBUG_MODULE) + sizeof(BOOT_MANAGER_BINARY_NAME_MAX_SIZE))
  32. #define BOOT_MANAGER_NAME "Minoca Boot Manager"
  33. //
  34. // ----------------------------------------------- Internal Function Prototypes
  35. //
  36. KSTATUS
  37. BmpLoadBootConfiguration (
  38. PVOID BootDevice,
  39. PBOOT_CONFIGURATION_CONTEXT Context,
  40. PBOOT_ENTRY *SelectedBootEntry
  41. );
  42. //
  43. // ------------------------------------------------------ Data Type Definitions
  44. //
  45. //
  46. // -------------------------------------------------------------------- Globals
  47. //
  48. //
  49. // Set this to TRUE to enable debugging in the boot manager.
  50. //
  51. BOOL BmDebug = FALSE;
  52. DEBUG_MODULE BmModule;
  53. LIST_ENTRY BmLoadedImageList;
  54. //
  55. // Carve off memory to store the loader module, including its string.
  56. //
  57. UCHAR BmModuleBuffer[BOOT_MANAGER_MODULE_BUFFER_SIZE];
  58. //
  59. // ------------------------------------------------------------------ Functions
  60. //
  61. INT
  62. BmMain (
  63. PBOOT_INITIALIZATION_BLOCK Parameters
  64. )
  65. /*++
  66. Routine Description:
  67. This routine is the entry point for the boot manager program.
  68. Arguments:
  69. Parameters - Supplies a pointer to the application parameters.
  70. Return Value:
  71. On success, this function does not return.
  72. On failure, this function returns the step number on which it failed. This
  73. provides an indication as to where in the boot process it failed.
  74. --*/
  75. {
  76. PCSTR ApplicationName;
  77. INT ApplicationReturn;
  78. UINTN BaseDifference;
  79. BOOT_CONFIGURATION_CONTEXT BootConfigurationContext;
  80. PBOOT_VOLUME BootDevice;
  81. PBOOT_ENTRY BootEntry;
  82. PDEBUG_DEVICE_DESCRIPTION DebugDevice;
  83. PDEBUG_MODULE DebugModule;
  84. PLOADED_IMAGE LoaderImage;
  85. PCSTR LoaderName;
  86. UINTN LoaderNameSize;
  87. PBOOT_INITIALIZATION_BLOCK LoaderParameters;
  88. ULONG LoadFlags;
  89. ULONG ModuleNameLength;
  90. PBOOT_VOLUME OsDevice;
  91. KSTATUS Status;
  92. ULONG Step;
  93. BootDevice = NULL;
  94. DebugDevice = NULL;
  95. LoaderParameters = NULL;
  96. Step = 0;
  97. //
  98. // Perform just enough firmware initialization to get to the debugger. Not
  99. // much happens here, as this is all undebuggable.
  100. //
  101. Status = FwInitialize(0, Parameters);
  102. if (!KSUCCESS(Status)) {
  103. goto MainEnd;
  104. }
  105. //
  106. // Perform very basic processor initialization, preparing it to take
  107. // exceptions and use the serial port.
  108. //
  109. Step += 1;
  110. BoInitializeProcessor();
  111. Step += 1;
  112. BoHlBootInitialize(&DebugDevice, NULL);
  113. if (BoFirmwareDebugDevice != NULL) {
  114. DebugDevice = BoFirmwareDebugDevice;
  115. }
  116. Step += 1;
  117. DebugModule = (PDEBUG_MODULE)BmModuleBuffer;
  118. //
  119. // Initialize the debugging subsystem.
  120. //
  121. RtlZeroMemory(&BmModuleBuffer, sizeof(BmModuleBuffer));
  122. ApplicationName = (PVOID)(UINTN)(Parameters->ApplicationName);
  123. ModuleNameLength = RtlStringLength(ApplicationName) + 1;
  124. if (ModuleNameLength > BOOT_MANAGER_BINARY_NAME_MAX_SIZE) {
  125. ModuleNameLength = BOOT_MANAGER_BINARY_NAME_MAX_SIZE;
  126. }
  127. DebugModule->StructureSize = sizeof(DEBUG_MODULE) + ModuleNameLength -
  128. (ANYSIZE_ARRAY * sizeof(CHAR));
  129. RtlStringCopy(DebugModule->BinaryName, ApplicationName, ModuleNameLength);
  130. DebugModule->LowestAddress =
  131. (PVOID)(UINTN)(Parameters->ApplicationLowestAddress);
  132. DebugModule->Size = Parameters->ApplicationSize;
  133. BoProductName = BOOT_MANAGER_NAME;
  134. if (BmDebug != FALSE) {
  135. Status = KdInitialize(DebugDevice, DebugModule);
  136. if (!KSUCCESS(Status)) {
  137. goto MainEnd;
  138. }
  139. }
  140. //
  141. // Initialize the firmware layer.
  142. //
  143. Step += 1;
  144. Status = FwInitialize(1, Parameters);
  145. if (!KSUCCESS(Status)) {
  146. goto MainEnd;
  147. }
  148. //
  149. // Mount the boot device.
  150. //
  151. Step += 1;
  152. Status = BoOpenBootVolume(Parameters->DriveNumber,
  153. Parameters->PartitionOffset,
  154. NULL,
  155. &BootDevice);
  156. if (!KSUCCESS(Status)) {
  157. FwPrintString(0, 0, "Failed to open boot volume.");
  158. goto MainEnd;
  159. }
  160. //
  161. // Load the boot configuration information.
  162. //
  163. Step += 1;
  164. Status = BmpLoadBootConfiguration(BootDevice,
  165. &BootConfigurationContext,
  166. &BootEntry);
  167. if (!KSUCCESS(Status)) {
  168. FwPrintString(0, 0, "Failed to load Boot Configuration.");
  169. goto MainEnd;
  170. }
  171. //
  172. // Close the boot volume and open the OS volume. It is possible these are
  173. // the same.
  174. //
  175. Step += 1;
  176. Status = BoCloseVolume(BootDevice);
  177. BootDevice = NULL;
  178. if (!KSUCCESS(Status)) {
  179. goto MainEnd;
  180. }
  181. Step += 1;
  182. if (BootEntry == NULL) {
  183. FwPrintString(0, 0, "No boot entry selected.");
  184. Status = STATUS_NO_DATA_AVAILABLE;
  185. goto MainEnd;
  186. }
  187. Step += 1;
  188. Status = BoOpenVolume(BootEntry->PartitionId, &OsDevice);
  189. if (!KSUCCESS(Status)) {
  190. FwPrintString(0, 0, "Failed to open OS volume.");
  191. goto MainEnd;
  192. }
  193. //
  194. // Load the loader.
  195. //
  196. Step += 1;
  197. Status = BmpInitializeImageSupport(OsDevice, BootEntry);
  198. if (!KSUCCESS(Status)) {
  199. goto MainEnd;
  200. }
  201. LoaderName = BootEntry->LoaderPath;
  202. LoaderNameSize = RtlStringLength(BootEntry->LoaderPath);
  203. LoadFlags = IMAGE_LOAD_FLAG_IGNORE_INTERPRETER |
  204. IMAGE_LOAD_FLAG_PRIMARY_EXECUTABLE |
  205. IMAGE_LOAD_FLAG_NO_STATIC_CONSTRUCTORS |
  206. IMAGE_LOAD_FLAG_BIND_NOW;
  207. Status = ImLoad(&BmLoadedImageList,
  208. BootEntry->LoaderPath,
  209. NULL,
  210. NULL,
  211. NULL,
  212. LoadFlags,
  213. &LoaderImage,
  214. NULL);
  215. if (!KSUCCESS(Status)) {
  216. FwPrintString(0, 0, "Failed to load OS loader.");
  217. goto MainEnd;
  218. }
  219. //
  220. // Initialize the boot parameters.
  221. //
  222. Step += 1;
  223. LoaderParameters = BoAllocateMemory(sizeof(BOOT_INITIALIZATION_BLOCK));
  224. if (LoaderParameters == NULL) {
  225. Status = STATUS_INSUFFICIENT_RESOURCES;
  226. goto MainEnd;
  227. }
  228. Step += 1;
  229. RtlZeroMemory(LoaderParameters, sizeof(BOOT_INITIALIZATION_BLOCK));
  230. LoaderParameters->Version = BOOT_INITIALIZATION_BLOCK_VERSION;
  231. LoaderParameters->BootConfigurationFile =
  232. (UINTN)(BootConfigurationContext.FileData);
  233. LoaderParameters->BootConfigurationFileSize =
  234. BootConfigurationContext.FileDataSize;
  235. LoaderParameters->BootEntryId = BootEntry->Id;
  236. LoaderParameters->BootEntryFlags = BootEntry->Flags;
  237. LoaderParameters->StackTop = Parameters->StackTop;
  238. LoaderParameters->StackSize = Parameters->StackSize;
  239. LoaderParameters->Flags = Parameters->Flags |
  240. BOOT_INITIALIZATION_FLAG_SCREEN_CLEAR;
  241. if (LoaderImage->Format == ImageElf64) {
  242. LoaderParameters->Flags |= BOOT_INITIALIZATION_FLAG_64BIT;
  243. }
  244. BaseDifference = LoaderImage->BaseDifference;
  245. //
  246. // Set the file name and base address of the loader.
  247. //
  248. ApplicationName = RtlStringFindCharacterRight(LoaderName,
  249. '/',
  250. LoaderNameSize);
  251. if (ApplicationName == NULL) {
  252. ApplicationName = LoaderName;
  253. } else {
  254. ApplicationName += 1;
  255. }
  256. LoaderParameters->ApplicationName = (UINTN)ApplicationName;
  257. LoaderParameters->ApplicationLowestAddress =
  258. (UINTN)LoaderImage->PreferredLowestAddress + BaseDifference;
  259. LoaderParameters->ApplicationSize = LoaderImage->Size;
  260. LoaderParameters->ApplicationArguments = (UINTN)BootEntry->LoaderArguments;
  261. Status = BmpFwInitializeBootBlock(LoaderParameters, OsDevice);
  262. if (!KSUCCESS(Status)) {
  263. goto MainEnd;
  264. }
  265. Step += 1;
  266. Status = BoCloseVolume(OsDevice);
  267. if (!KSUCCESS(Status)) {
  268. goto MainEnd;
  269. }
  270. Step += 1;
  271. KdDisconnect();
  272. //
  273. // Launch the boot application. Hopefully this does not return.
  274. //
  275. Step += 1;
  276. ApplicationReturn = BmpFwTransferToBootApplication(LoaderParameters,
  277. LoaderImage->EntryPoint);
  278. Step += 1;
  279. //
  280. // The loader prints on the first two lines, so leave those alone.
  281. //
  282. FwPrintString(0, 3, "Boot Application returned ");
  283. FwPrintHexInteger(26, 3, ApplicationReturn);
  284. //
  285. // Unload the image.
  286. //
  287. ImImageReleaseReference(LoaderImage);
  288. LoaderImage = NULL;
  289. //
  290. // Destroy the initialization block.
  291. //
  292. if (LoaderParameters != NULL) {
  293. if (LoaderParameters->ReservedRegions != (UINTN)NULL) {
  294. BoFreeMemory((PVOID)(UINTN)(LoaderParameters->ReservedRegions));
  295. }
  296. BoFreeMemory(LoaderParameters);
  297. LoaderParameters = NULL;
  298. }
  299. Status = STATUS_SUCCESS;
  300. MainEnd:
  301. //
  302. // The loader prints on the first two lines, and the "application returned"
  303. // message occurs on the third, so start on the fourth.
  304. //
  305. FwPrintString(0, 4, "Boot Manager Failed: ");
  306. FwPrintHexInteger(21, 4, Status);
  307. FwPrintString(0, 5, "Step: ");
  308. FwPrintInteger(6, 5, Step);
  309. FwDestroy();
  310. return Step;
  311. }
  312. PVOID
  313. BoExpandHeap (
  314. PMEMORY_HEAP Heap,
  315. UINTN Size,
  316. UINTN Tag
  317. )
  318. /*++
  319. Routine Description:
  320. This routine is called when the heap wants to expand and get more space.
  321. Arguments:
  322. Heap - Supplies a pointer to the heap to allocate from.
  323. Size - Supplies the size of the allocation request, in bytes.
  324. Tag - Supplies a 32-bit tag to associate with this allocation for debugging
  325. purposes. These are usually four ASCII characters so as to stand out
  326. when a poor developer is looking at a raw memory dump. It could also be
  327. a return address.
  328. Return Value:
  329. Returns a pointer to the allocation if successful, or NULL if the
  330. allocation failed.
  331. --*/
  332. {
  333. ULONG AllocationSize;
  334. ULONG PageSize;
  335. PHYSICAL_ADDRESS PhysicalAddress;
  336. PVOID PhysicalPointer;
  337. KSTATUS Status;
  338. PhysicalPointer = NULL;
  339. if (Size == 0) {
  340. return NULL;
  341. }
  342. PageSize = MmPageSize();
  343. //
  344. // Attempt to allocate new pages to satisfy the allocation.
  345. //
  346. AllocationSize = ALIGN_RANGE_UP(Size, PageSize);
  347. Status = FwAllocatePages(&PhysicalAddress,
  348. AllocationSize,
  349. PageSize,
  350. MemoryTypeLoaderTemporary);
  351. if (!KSUCCESS(Status)) {
  352. goto ExpandHeapEnd;
  353. }
  354. ASSERT((UINTN)PhysicalAddress == PhysicalAddress);
  355. PhysicalPointer = (PVOID)(UINTN)PhysicalAddress;
  356. ExpandHeapEnd:
  357. return PhysicalPointer;
  358. }
  359. //
  360. // --------------------------------------------------------- Internal Functions
  361. //
  362. KSTATUS
  363. BmpLoadBootConfiguration (
  364. PVOID BootDevice,
  365. PBOOT_CONFIGURATION_CONTEXT Context,
  366. PBOOT_ENTRY *SelectedBootEntry
  367. )
  368. /*++
  369. Routine Description:
  370. This routine loads and read the boot configuration information.
  371. Arguments:
  372. BootDevice - Supplies the open handle to the boot partition.
  373. Context - Supplies a pointer where the initialized boot configuration
  374. context will be returned.
  375. SelectedBootEntry - Supplies a pointer where a pointer to the selected
  376. boot entry will be returned on success.
  377. Return Value:
  378. Status code.
  379. --*/
  380. {
  381. FILE_PROPERTIES DirectoryProperties;
  382. PVOID FileData;
  383. UINTN FileDataSize;
  384. ULONGLONG ModificationDate;
  385. KSTATUS Status;
  386. *SelectedBootEntry = NULL;
  387. FileData = NULL;
  388. Status = BoLookupPath(BootDevice,
  389. NULL,
  390. BOOT_CONFIGURATION_FILE_PATH,
  391. &DirectoryProperties);
  392. if (!KSUCCESS(Status)) {
  393. goto LoadBootConfigurationEnd;
  394. }
  395. Status = BoLoadFile(BootDevice,
  396. &(DirectoryProperties.FileId),
  397. BOOT_CONFIGURATION_FILE_NAME,
  398. &FileData,
  399. &FileDataSize,
  400. &ModificationDate);
  401. if (!KSUCCESS(Status)) {
  402. goto LoadBootConfigurationEnd;
  403. }
  404. //
  405. // Initialize the boot configuration context.
  406. //
  407. RtlZeroMemory(Context, sizeof(BOOT_CONFIGURATION_CONTEXT));
  408. Context->AllocateFunction = BoAllocateMemory;
  409. Context->FreeFunction = BoFreeMemory;
  410. Context->FileData = FileData;
  411. Context->FileDataSize = FileDataSize;
  412. Status = BcInitializeContext(Context);
  413. if (!KSUCCESS(Status)) {
  414. goto LoadBootConfigurationEnd;
  415. }
  416. //
  417. // Read and parse the boot configuration file data.
  418. //
  419. Status = BcReadBootConfigurationFile(Context);
  420. if (!KSUCCESS(Status)) {
  421. goto LoadBootConfigurationEnd;
  422. }
  423. //
  424. // If there's no boot once entry, then fill out the default and return.
  425. //
  426. *SelectedBootEntry = Context->GlobalConfiguration.DefaultBootEntry;
  427. if (Context->GlobalConfiguration.BootOnce == NULL) {
  428. Status = STATUS_SUCCESS;
  429. goto LoadBootConfigurationEnd;
  430. }
  431. //
  432. // There is a boot once entry. Save it as the selected boot entry, then
  433. // work to write out the boot configuration with the boot once field
  434. // cleared.
  435. //
  436. *SelectedBootEntry = Context->GlobalConfiguration.BootOnce;
  437. Context->GlobalConfiguration.BootOnce = NULL;
  438. Status = BcWriteBootConfigurationFile(Context);
  439. if (!KSUCCESS(Status)) {
  440. goto LoadBootConfigurationEnd;
  441. }
  442. Status = BoStoreFile(BootDevice,
  443. DirectoryProperties.FileId,
  444. BOOT_CONFIGURATION_FILE_NAME,
  445. sizeof(BOOT_CONFIGURATION_FILE_NAME),
  446. Context->FileData,
  447. Context->FileDataSize,
  448. ModificationDate);
  449. if (!KSUCCESS(Status)) {
  450. goto LoadBootConfigurationEnd;
  451. }
  452. Status = STATUS_SUCCESS;
  453. LoadBootConfigurationEnd:
  454. if (!KSUCCESS(Status)) {
  455. RtlDebugPrint("Failed to load Boot Configuration: %d.\n", Status);
  456. if (FileData != NULL) {
  457. BoFreeMemory(FileData);
  458. }
  459. }
  460. return Status;
  461. }