[BOOTLIB]:
[reactos.git] / reactos / boot / environ / app / bootmgr / bootmgr.c
1 /*
2 * COPYRIGHT: See COPYING.ARM in the top level directory
3 * PROJECT: ReactOS UEFI Boot Manager
4 * FILE: boot/environ/app/bootmgr/bootmgr.c
5 * PURPOSE: Boot Manager Entrypoint
6 * PROGRAMMER: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include "bootmgr.h"
12
13 /* DATA VARIABLES ************************************************************/
14
15 DEFINE_GUID(GUID_WINDOWS_BOOTMGR,
16 0x9DEA862C,
17 0x5CDD,
18 0x4E70,
19 0xAC, 0xC1, 0xF3, 0x2B, 0x34, 0x4D, 0x47, 0x95);
20
21 ULONGLONG ApplicationStartTime;
22 ULONGLONG PostTime;
23 GUID BmApplicationIdentifier;
24 PWCHAR BootDirectory;
25
26 BL_BOOT_ERROR BmpErrorBuffer;
27 PBL_BOOT_ERROR BmpInternalBootError;
28 BL_PACKED_BOOT_ERROR BmpPackedBootError;
29
30 BOOLEAN BmBootIniUsed;
31 WCHAR BmpFileNameBuffer[128];
32 PWCHAR ParentFileName = L"";
33
34 /* FUNCTIONS *****************************************************************/
35
36 NTSTATUS
37 BmpFwGetApplicationDirectoryPath (
38 _In_ PUNICODE_STRING ApplicationDirectoryPath
39 )
40 {
41 NTSTATUS Status;
42 ULONG i, AppPathLength;
43 PWCHAR ApplicationPath, PathCopy;
44
45 /* Clear the incoming string */
46 ApplicationDirectoryPath->Length = 0;
47 ApplicationDirectoryPath->MaximumLength = 0;
48 ApplicationDirectoryPath->Buffer = 0;
49
50 /* Get the boot application path */
51 ApplicationPath = NULL;
52 Status = BlGetBootOptionString(BlpApplicationEntry.BcdData,
53 BcdLibraryString_ApplicationPath,
54 &ApplicationPath);
55 if (NT_SUCCESS(Status))
56 {
57 /* Calculate the length of the application path */
58 for (i = wcslen(ApplicationPath) - 1; i > 0; i--)
59 {
60 /* Keep going until the path separator */
61 if (ApplicationPath[i] == OBJ_NAME_PATH_SEPARATOR)
62 {
63 break;
64 }
65 }
66
67 /* Check if we have space for one more character */
68 AppPathLength = i + 1;
69 if (AppPathLength < i)
70 {
71 /* Nope, we'll overflow */
72 AppPathLength = -1;
73 Status = STATUS_INTEGER_OVERFLOW;
74 }
75 else
76 {
77 /* Go ahead */
78 Status = STATUS_SUCCESS;
79 }
80
81 /* No overflow? */
82 if (NT_SUCCESS(Status))
83 {
84 /* Check if it's safe to multiply by two */
85 if ((AppPathLength * sizeof(WCHAR)) > 0xFFFFFFFF)
86 {
87 /* Nope */
88 AppPathLength = -1;
89 Status = STATUS_INTEGER_OVERFLOW;
90 }
91 else
92 {
93 /* We're good, do the multiplication */
94 Status = STATUS_SUCCESS;
95 AppPathLength *= sizeof(WCHAR);
96 }
97
98 /* Allocate a copy for the string */
99 if (NT_SUCCESS(Status))
100 {
101 PathCopy = BlMmAllocateHeap(AppPathLength);
102 if (PathCopy)
103 {
104 /* NULL-terminate it */
105 RtlCopyMemory(PathCopy,
106 ApplicationPath,
107 AppPathLength - sizeof(UNICODE_NULL));
108 PathCopy[AppPathLength] = UNICODE_NULL;
109
110 /* Finally, initialize the outoing string */
111 RtlInitUnicodeString(ApplicationDirectoryPath, PathCopy);
112 }
113 else
114 {
115 /* No memory, fail */
116 Status = STATUS_NO_MEMORY;
117 }
118 }
119 }
120 }
121
122 /* Check if we had an application path */
123 if (ApplicationPath)
124 {
125 /* No longer need this, free it */
126 BlMmFreeHeap(ApplicationPath);
127 }
128
129 /* All done! */
130 return Status;
131 }
132
133 NTSTATUS
134 BmFwInitializeBootDirectoryPath (
135 VOID
136 )
137 {
138 PWCHAR FinalPath;
139 NTSTATUS Status;
140 PWCHAR BcdDirectory;
141 UNICODE_STRING BcdPath;
142 ULONG FinalSize;
143 ULONG FileHandle, DeviceHandle;
144
145 /* Initialize everything for failure */
146 BcdPath.MaximumLength = 0;
147 BcdPath.Buffer = NULL;
148 BcdDirectory = NULL;
149 FinalPath = NULL;
150 FileHandle = -1;
151 DeviceHandle = -1;
152
153 /* Try to open the boot device */
154 Status = BlpDeviceOpen(BlpBootDevice, 1u, 0, &DeviceHandle);
155 if (!NT_SUCCESS(Status))
156 {
157 EfiPrintf(L"Device open failed: %lx\r\n", Status);
158 goto Quickie;
159 }
160
161 /* Get the directory path */
162 Status = BmpFwGetApplicationDirectoryPath(&BcdPath);
163 BcdDirectory = BcdPath.Buffer;
164 if (!NT_SUCCESS(Status))
165 {
166 EfiPrintf(L"path failed: %lx\n", Status);
167 goto Quickie;
168 }
169
170 /* Add the BCD file name to it */
171 FinalSize = BcdPath.MaximumLength + sizeof(L"\\BCD") - sizeof(UNICODE_NULL);
172 if (FinalSize < BcdPath.MaximumLength)
173 {
174 goto Quickie;
175 }
176
177 /* Allocate space for the final path */
178 FinalPath = BlMmAllocateHeap(FinalSize);
179 if (!FinalPath)
180 {
181 goto Quickie;
182 }
183
184 /* Build it */
185 RtlZeroMemory(FinalPath, FinalSize);
186 RtlCopyMemory(FinalPath, BcdDirectory, BcdPath.MaximumLength);
187 wcsncat(FinalPath, L"\\BCD", FinalSize / sizeof(WCHAR));
188
189 /* Try to open the file */
190 EfiPrintf(L"Opening: %s\r\n", FinalPath);
191 Status = BlFileOpen(DeviceHandle, FinalPath, 1, &FileHandle);
192 if (!NT_SUCCESS(Status))
193 {
194 BootDirectory = BcdDirectory;
195 goto Quickie;
196 }
197
198 /* Save the boot directory */
199 BootDirectory = L"\\EFI\\Microsoft\\Boot";
200
201 Quickie:
202 /* Free all the allocations we made */
203 if (BcdDirectory)
204 {
205 Status = BlMmFreeHeap(BcdDirectory);
206 }
207 if (FinalPath)
208 {
209 Status = BlMmFreeHeap(FinalPath);
210 }
211
212 /* Close the BCD file */
213 if (FileHandle != -1)
214 {
215 Status = BlFileClose(FileHandle);
216 }
217
218 /* Close the boot device */
219 if (DeviceHandle != -1)
220 {
221 Status = BlDeviceClose(DeviceHandle);
222 }
223
224 /* Return back to the caller */
225 return Status;
226 }
227
228 NTSTATUS
229 BmOpenBootIni (
230 VOID
231 )
232 {
233 /* Don't yet handled boot.ini */
234 return STATUS_NOT_FOUND;
235 }
236
237 ULONG
238 BmpFatalErrorMessageFilter (
239 _In_ NTSTATUS ErrorStatus,
240 _Out_ PULONG ErrorResourceId
241 )
242 {
243 ULONG Result;
244
245 /* Assume no message for now, check for known status message */
246 Result = 0;
247 switch (ErrorStatus)
248 {
249 /* Convert each status to a resource ID */
250 case STATUS_UNEXPECTED_IO_ERROR:
251 *ErrorResourceId = 9017;
252 Result = 1;
253 break;
254 case STATUS_IMAGE_CHECKSUM_MISMATCH:
255 *ErrorResourceId = 9018;
256 break;
257 case STATUS_INVALID_IMAGE_WIN_64:
258 *ErrorResourceId = 9016;
259 break;
260 case 0xC0000428:
261 *ErrorResourceId = 9019;
262 Result = 2;
263 break;
264 case 0xC0210000:
265 *ErrorResourceId = 9013;
266 break;
267 }
268
269 /* Return the type of message */
270 return Result;
271 }
272
273 VOID
274 BmErrorPurge (
275 VOID
276 )
277 {
278 /* Check if a boot error is present */
279 if (BmpPackedBootError.BootError)
280 {
281 /* Purge it */
282 BlMmFreeHeap(BmpPackedBootError.BootError);
283 BmpPackedBootError.BootError = NULL;
284 }
285
286 /* Zero out the packed buffer */
287 BmpPackedBootError.Size = 0;
288 BmpInternalBootError = NULL;
289 RtlZeroMemory(&BmpErrorBuffer, sizeof(BmpErrorBuffer));
290 }
291
292 VOID
293 BmpErrorLog (
294 _In_ ULONG ErrorCode,
295 _In_ NTSTATUS ErrorStatus,
296 _In_ ULONG ErrorMsgId,
297 _In_ PWCHAR FileName,
298 _In_ ULONG HelpMsgId
299 )
300 {
301 PWCHAR ErrorMsgString;
302
303 /* Check if we already had an error */
304 if (BmpInternalBootError)
305 {
306 /* Purge it */
307 BmErrorPurge();
308 }
309
310 /* Find the string for this error ID */
311 ErrorMsgString = BlResourceFindMessage(ErrorMsgId);
312 if (ErrorMsgString)
313 {
314 /* Fill out the error buffer */
315 BmpErrorBuffer.Unknown1 = 0;
316 BmpErrorBuffer.Unknown2 = 0;
317 BmpErrorBuffer.ErrorString = ErrorMsgString;
318 BmpErrorBuffer.FileName = FileName;
319 BmpErrorBuffer.ErrorCode = ErrorCode;
320 BmpErrorBuffer.ErrorStatus = ErrorStatus;
321 BmpErrorBuffer.HelpMsgId = HelpMsgId;
322 BmpInternalBootError = &BmpErrorBuffer;
323 }
324 }
325
326 VOID
327 BmFatalErrorEx (
328 _In_ ULONG ErrorCode,
329 _In_ ULONG_PTR Parameter1,
330 _In_ ULONG_PTR Parameter2,
331 _In_ ULONG_PTR Parameter3,
332 _In_ ULONG_PTR Parameter4
333 )
334 {
335 PWCHAR FileName, Buffer;
336 NTSTATUS ErrorStatus;
337 WCHAR FormatString[256];
338 ULONG ErrorResourceId, ErrorHelpId;
339 BOOLEAN Restart, NoError;
340
341 /* Assume no buffer for now */
342 Buffer = NULL;
343
344 /* Check what error code is being raised */
345 switch (ErrorCode)
346 {
347 /* Error reading the BCD */
348 case BL_FATAL_ERROR_BCD_READ:
349
350 /* Check if we have a name for the BCD file */
351 if (Parameter1)
352 {
353 /* Check if the name fits into our buffer */
354 FileName = (PWCHAR)Parameter1;
355 if (wcslen(FileName) < sizeof(BmpFileNameBuffer))
356 {
357 /* Copy it in there */
358 Buffer = BmpFileNameBuffer;
359 wcsncpy(BmpFileNameBuffer,
360 FileName,
361 RTL_NUMBER_OF(BmpFileNameBuffer));
362 }
363 }
364
365 /* If we don't have a buffer, use an empty one */
366 if (!Buffer)
367 {
368 Buffer = ParentFileName;
369 }
370
371 /* The NTSTATUS code is in parameter 2*/
372 ErrorStatus = (NTSTATUS)Parameter2;
373
374 /* Build the error string */
375 swprintf(FormatString,
376 L"\nAn error occurred (%08x) while attempting"
377 L"to read the boot configuration data file %s\n",
378 ErrorStatus,
379 Buffer);
380
381 /* Select the resource ID message */
382 ErrorResourceId = 9002;
383 break;
384
385 default:
386
387 /* The rest is not yet handled */
388 EfiPrintf(L"Unexpected fatal error: %lx\n", ErrorCode);
389 while (1);
390 break;
391 }
392
393 /* Check if the BCD option for restart is set */
394 BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
395 BcdLibraryBoolean_RestartOnFailure,
396 &Restart);
397 if (Restart)
398 {
399 /* Yes, so no error should be shown since we'll auto-restart */
400 NoError = TRUE;
401 }
402 else
403 {
404 /* Check if the option for not showing errors is set in the BCD */
405 BlGetBootOptionBoolean(BlpApplicationEntry.BcdData,
406 BcdBootMgrBoolean_NoErrorDisplay,
407 &NoError);
408 }
409
410 /* Do we want an error? */
411 if (!NoError)
412 {
413 /* Yep, print it and then raise an error */
414 BlStatusPrint(FormatString);
415 BlStatusError(1, ErrorCode, Parameter1, Parameter2, Parameter3);
416 }
417
418 /* Get the help message ID */
419 ErrorHelpId = BmpFatalErrorMessageFilter(ErrorStatus, &ErrorResourceId);
420 BmpErrorLog(ErrorCode, ErrorStatus, ErrorResourceId, Buffer, ErrorHelpId);
421 }
422
423 NTSTATUS
424 BmpFwGetFullPath (
425 _In_ PWCHAR FileName,
426 _Out_ PWCHAR* FullPath
427 )
428 {
429 NTSTATUS Status;
430 ULONG BootDirLength, BootDirLengthWithNul;
431 ULONG PathLength, FullPathLength;
432
433 /* Compute the length of the directory, and add a NUL */
434 BootDirLength = wcslen(BootDirectory);
435 BootDirLengthWithNul = BootDirLength + 1;
436 if (BootDirLengthWithNul < BootDirLength)
437 {
438 /* This would overflow */
439 BootDirLengthWithNul = -1;
440 Status = STATUS_INTEGER_OVERFLOW;
441 }
442 else
443 {
444 /* We have space */
445 Status = STATUS_SUCCESS;
446 }
447
448 /* Fail on overflow */
449 if (!NT_SUCCESS(Status))
450 {
451 goto Quickie;
452 }
453
454 /* Add the length of the file, make sure it fits */
455 PathLength = wcslen(FileName);
456 FullPathLength = PathLength + BootDirLength;
457 if (FullPathLength < PathLength)
458 {
459 /* Nope */
460 FullPathLength = -1;
461 Status = STATUS_INTEGER_OVERFLOW;
462 }
463 else
464 {
465 /* All good */
466 Status = STATUS_SUCCESS;
467 }
468
469 /* Fail on overflow */
470 if (!NT_SUCCESS(Status))
471 {
472 goto Quickie;
473 }
474
475 /* Allocate the full path */
476 FullPathLength = FullPathLength * sizeof(WCHAR);
477 *FullPath = BlMmAllocateHeap(FullPathLength);
478 if (*FullPath)
479 {
480 /* Copy the directory followed by the file name */
481 wcsncpy(*FullPath, BootDirectory, FullPathLength / sizeof(WCHAR));
482 wcsncat(*FullPath, FileName, FullPathLength / sizeof(WCHAR));
483 }
484 else
485 {
486 /* Bail out since we have no memory */
487 Status = STATUS_NO_MEMORY;
488 }
489
490 Quickie:
491 /* Return to caller */
492 return Status;
493 }
494
495 NTSTATUS
496 BmOpenDataStore (
497 _Out_ PHANDLE Handle
498 )
499 {
500 NTSTATUS Status;
501 PBL_DEVICE_DESCRIPTOR BcdDevice;
502 PWCHAR BcdPath, FullPath, PathBuffer;
503 BOOLEAN HavePath;
504 ULONG PathLength, PathLengthWithNul, FullSize;
505 PVOID FinalBuffer;
506 UNICODE_STRING BcdString;
507
508 /* Initialize variables */
509 PathBuffer = NULL;
510 BcdDevice = NULL;
511 BcdPath = NULL;
512 HavePath = FALSE;
513
514 /* Check if a boot.ini file exists */
515 Status = BmOpenBootIni();
516 if (NT_SUCCESS(Status))
517 {
518 BmBootIniUsed = TRUE;
519 }
520
521 /* Check on which device the BCD is */
522 Status = BlGetBootOptionDevice(BlpApplicationEntry.BcdData,
523 BcdBootMgrDevice_BcdDevice,
524 &BcdDevice,
525 NULL);
526 if (!NT_SUCCESS(Status))
527 {
528 /* It's not on a custom device, so it must be where we are */
529 Status = BlGetBootOptionDevice(BlpApplicationEntry.BcdData,
530 BcdLibraryDevice_ApplicationDevice,
531 &BcdDevice,
532 NULL);
533 if (!NT_SUCCESS(Status))
534 {
535 /* This BCD option is required */
536 goto Quickie;
537 }
538 }
539
540 /* Next, check what file contains the BCD */
541 Status = BlGetBootOptionString(BlpApplicationEntry.BcdData,
542 BcdBootMgrString_BcdFilePath,
543 &BcdPath);
544 if (NT_SUCCESS(Status))
545 {
546 /* We don't handle custom BCDs yet */
547 EfiPrintf(L"Not handled\n");
548 Status = STATUS_NOT_IMPLEMENTED;
549 goto Quickie;
550 }
551
552 /* Now check if the BCD is on a remote share */
553 if (BcdDevice->DeviceType == UdpDevice)
554 {
555 /* Nope. Nope. Nope */
556 EfiPrintf(L"Not handled\n");
557 Status = STATUS_NOT_IMPLEMENTED;
558 goto Quickie;
559 }
560
561 /* Otherwise, compute the hardcoded path of the BCD */
562 Status = BmpFwGetFullPath(L"\\BCD", &FullPath);
563 EfiPrintf(L"Status: %lx %s\r\n", Status, FullPath);
564 if (!NT_SUCCESS(Status))
565 {
566 /* User the raw path */
567 PathBuffer = BcdPath;
568 }
569 else
570 {
571 /* Use the path we got */
572 PathBuffer = FullPath;
573 HavePath = TRUE;
574 }
575
576 /* Check if we failed to get the BCD path */
577 if (!NT_SUCCESS(Status))
578 {
579 goto Quickie;
580 }
581
582 /* Add a NUL to the path, make sure it'll fit */
583 Status = STATUS_SUCCESS;
584 PathLength = wcslen(PathBuffer);
585 PathLengthWithNul = PathLength + 1;
586 if (PathLengthWithNul < PathLength)
587 {
588 PathLengthWithNul = -1;
589 Status = STATUS_INTEGER_OVERFLOW;
590 }
591
592 /* Bail out if it doesn't fit */
593 if (!NT_SUCCESS(Status))
594 {
595 goto Quickie;
596 }
597
598 /* Now add the size of the path to the device path, check if it fits */
599 PathLengthWithNul = PathLengthWithNul * sizeof(WCHAR);
600 FullSize = PathLengthWithNul + BcdDevice->Size;
601 if (FullSize < BcdDevice->Size)
602 {
603 FullSize = -1;
604 Status = STATUS_INTEGER_OVERFLOW;
605 }
606 else
607 {
608 Status = STATUS_SUCCESS;
609 }
610
611 /* Bail out if it doesn't fit */
612 if (!NT_SUCCESS(Status))
613 {
614 goto Quickie;
615 }
616
617 /* Allocate a final structure to hold both entities */
618 FinalBuffer = BlMmAllocateHeap(FullSize);
619 if (!FinalBuffer)
620 {
621 Status = STATUS_NO_MEMORY;
622 goto Quickie;
623 }
624
625 /* Copy the device path and file path into the final buffer */
626 RtlCopyMemory(FinalBuffer, BcdDevice, BcdDevice->Size);
627 RtlCopyMemory((PVOID)((ULONG_PTR)FinalBuffer + BcdDevice->Size),
628 PathBuffer,
629 PathLengthWithNul);
630
631 /* Now tell the BCD engine to open the store */
632 BcdString.Length = FullSize;
633 BcdString.MaximumLength = FullSize;
634 BcdString.Buffer = FinalBuffer;
635 Status = BcdOpenStoreFromFile(&BcdString, Handle);
636
637 /* Free our final buffer */
638 BlMmFreeHeap(FinalBuffer);
639
640 Quickie:
641 /* Did we allocate a device? */
642 if (BcdDevice)
643 {
644 /* Free it */
645 BlMmFreeHeap(BcdDevice);
646 }
647
648 /* Is this the failure path? */
649 if (!NT_SUCCESS(Status))
650 {
651 /* Raise a fatal error */
652 BmFatalErrorEx(1, (ULONG_PTR)PathBuffer, Status, 0, 0);
653 }
654
655 /* Did we get an allocated path? */
656 if ((PathBuffer) && (HavePath))
657 {
658 /* Free it */
659 BlMmFreeHeap(PathBuffer);
660 }
661
662 /* Return back to the caller */
663 return Status;
664 }
665
666 /*++
667 * @name BmMain
668 *
669 * The BmMain function implements the Windows Boot Application entrypoint for
670 * the Boot Manager.
671 *
672 * @param BootParameters
673 * Pointer to the Boot Application Parameter Block.
674 *
675 * @return NT_SUCCESS if the image was loaded correctly, relevant error code
676 * otherwise.
677 *
678 *--*/
679 NTSTATUS
680 BmMain (
681 _In_ PBOOT_APPLICATION_PARAMETER_BLOCK BootParameters
682 )
683 {
684 NTSTATUS Status;
685 BL_LIBRARY_PARAMETERS LibraryParameters;
686 PBL_RETURN_ARGUMENTS ReturnArguments;
687 BOOLEAN RebootOnError;
688 PGUID AppIdentifier;
689 HANDLE BcdHandle;
690
691 EfiPrintf(L"ReactOS UEFI Boot Manager Initializing...\n");
692
693 /* Reading the BCD can change this later on */
694 RebootOnError = FALSE;
695
696 /* Save the start/end-of-POST time */
697 ApplicationStartTime = __rdtsc();
698 PostTime = ApplicationStartTime;
699
700 /* Setup the boot library parameters for this application */
701 BlSetupDefaultParameters(&LibraryParameters);
702 LibraryParameters.TranslationType = BlNone;
703 LibraryParameters.LibraryFlags = 0x400 | 0x8;
704 LibraryParameters.MinimumAllocationCount = 16;
705 LibraryParameters.MinimumHeapSize = 512 * 1024;
706
707 /* Initialize the boot library */
708 Status = BlInitializeLibrary(BootParameters, &LibraryParameters);
709 if (!NT_SUCCESS(Status))
710 {
711 /* Check for failure due to invalid application entry */
712 if (Status != STATUS_INVALID_PARAMETER_9)
713 {
714 /* Specifically print out what happened */
715 EfiPrintf(L"BlInitializeLibrary failed 0x%x\r\n", Status);
716 }
717
718 /* Go to exit path */
719 goto Quickie;
720 }
721
722 /* Get the application identifier */
723 AppIdentifier = BlGetApplicationIdentifier();
724 if (!AppIdentifier)
725 {
726 /* None was given, so set our default one */
727 AppIdentifier = (PGUID)&GUID_WINDOWS_BOOTMGR;
728 }
729
730 /* Save our identifier */
731 BmApplicationIdentifier = *AppIdentifier;
732
733 /* Initialize the file system to open a handle to our root boot directory */
734 BmFwInitializeBootDirectoryPath();
735
736 /* Load and initialize the boot configuration database (BCD) */
737 Status = BmOpenDataStore(&BcdHandle);
738 EfiPrintf(L"BCD Open: %lx\r\n", Status);
739
740 /* do more stuff!! */
741 EfiPrintf(L"We are A-OK!\r\n");
742 EfiStall(10000000);
743
744 Quickie:
745 /* Check if we should reboot */
746 if ((RebootOnError) ||
747 (BlpApplicationEntry.Flags & BL_APPLICATION_ENTRY_REBOOT_ON_ERROR))
748 {
749 /* Reboot the box */
750 BlFwReboot();
751 Status = STATUS_SUCCESS;
752 }
753 else
754 {
755 /* Return back to the caller with the error argument encoded */
756 ReturnArguments = (PVOID)((ULONG_PTR)BootParameters + BootParameters->ReturnArgumentsOffset);
757 ReturnArguments->Version = BL_RETURN_ARGUMENTS_VERSION;
758 ReturnArguments->Status = Status;
759
760 /* Tear down the boot library*/
761 BlDestroyLibrary();
762 }
763
764 /* Return back status */
765 return Status;
766 }
767