• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2 Reading/writing MBR/DBR.
3   NOTE:
4     If we write MBR to disk, we just update the MBR code and the partition table wouldn't be over written.
5     If we process DBR, we will patch MBR to set first partition active if no active partition exists.
6 
7 Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
8 This program and the accompanying materials
9 are licensed and made available under the terms and conditions of the BSD License
10 which accompanies this distribution.  The full text of the license may be found at
11 http://opensource.org/licenses/bsd-license.php
12 
13 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 
16 **/
17 
18 #include <windows.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <Common/UefiBaseTypes.h>
22 
23 #include "ParseInf.h"
24 #include "EfiUtilityMsgs.h"
25 #include "CommonLib.h"
26 
27 //
28 // Utility Name
29 //
30 #define UTILITY_NAME  "GenBootSector"
31 
32 //
33 // Utility version information
34 //
35 #define UTILITY_MAJOR_VERSION 0
36 #define UTILITY_MINOR_VERSION 2
37 
38 #define MAX_DRIVE                             26
39 #define PARTITION_TABLE_OFFSET                0x1BE
40 
41 #define SIZE_OF_PARTITION_ENTRY               0x10
42 
43 #define PARTITION_ENTRY_STARTLBA_OFFSET       8
44 
45 #define PARTITION_ENTRY_NUM                   4
46 
47 INT
48 GetDrvNumOffset (
49   IN VOID *BootSector
50   );
51 
52 typedef enum {
53   PatchTypeUnknown,
54   PatchTypeFloppy,
55   PatchTypeIde,
56   PatchTypeUsb,
57   PatchTypeFileImage   // input and output are all file image, patching action is same as PatchTypeFloppy
58 } PATCH_TYPE;
59 
60 typedef enum {
61   PathUnknown,
62   PathFile,
63   PathFloppy,
64   PathUsb,
65   PathIde
66 } PATH_TYPE;
67 
68 typedef enum {
69   ErrorSuccess,
70   ErrorFileCreate,
71   ErrorFileReadWrite,
72   ErrorNoMbr,
73   ErrorFatType,
74   ErrorPath,
75 } ERROR_STATUS;
76 
77 CHAR *ErrorStatusDesc[] = {
78   "Success",
79   "Failed to create files",
80   "Failed to read/write files",
81   "No MBR exists",
82   "Failed to detect Fat type",
83   "Inavlid path"
84 };
85 
86 typedef struct _DRIVE_TYPE_DESC {
87   UINT  Type;
88   CHAR  *Description;
89 } DRIVE_TYPE_DESC;
90 
91 #define DRIVE_TYPE_ITEM(x) {x, #x}
92 DRIVE_TYPE_DESC DriveTypeDesc[] = {
93   DRIVE_TYPE_ITEM (DRIVE_UNKNOWN),
94   DRIVE_TYPE_ITEM (DRIVE_NO_ROOT_DIR),
95   DRIVE_TYPE_ITEM (DRIVE_REMOVABLE),
96   DRIVE_TYPE_ITEM (DRIVE_FIXED),
97   DRIVE_TYPE_ITEM (DRIVE_REMOTE),
98   DRIVE_TYPE_ITEM (DRIVE_CDROM),
99   DRIVE_TYPE_ITEM (DRIVE_RAMDISK),
100   (UINT) -1, NULL
101 };
102 
103 typedef struct _DRIVE_INFO {
104   CHAR              VolumeLetter;
105   DRIVE_TYPE_DESC   *DriveType;
106   UINT              DiskNumber;
107 } DRIVE_INFO;
108 
109 typedef struct _PATH_INFO {
110   CHAR             *Path;
111   CHAR             PhysicalPath[260];
112   PATH_TYPE        Type;
113   BOOL             Input;
114 } PATH_INFO;
115 
116 #define BOOT_SECTOR_LBA_OFFSET 0x1FA
117 
118 #define IsLetter(x) (((x) >= 'a' && (x) <= 'z') || ((x) >= 'A' && (x) <= 'Z'))
119 
120 BOOL
GetDriveInfo(CHAR VolumeLetter,DRIVE_INFO * DriveInfo)121 GetDriveInfo (
122   CHAR       VolumeLetter,
123   DRIVE_INFO *DriveInfo
124   )
125 /*++
126 Routine Description:
127   Get drive information including disk number and drive type,
128   where disknumber is useful for reading/writing disk raw data.
129   NOTE: Floppy disk doesn't have disk number but it doesn't matter because
130         we can reading/writing floppy disk without disk number.
131 
132 Arguments:
133   VolumeLetter : volume letter, e.g.: C for C:, A for A:
134   DriveInfo    : pointer to DRIVE_INFO structure receiving drive information.
135 
136 Return:
137   TRUE  : successful
138   FALSE : failed
139 --*/
140 {
141   HANDLE                  VolumeHandle;
142   STORAGE_DEVICE_NUMBER   StorageDeviceNumber;
143   DWORD                   BytesReturned;
144   BOOL                    Success;
145   UINT                    DriveType;
146   UINT                    Index;
147 
148   CHAR RootPath[]         = "X:\\";       // "X:\"  -> for GetDriveType
149   CHAR VolumeAccessPath[] = "\\\\.\\X:";  // "\\.\X:"  -> to open the volume
150 
151   RootPath[0] = VolumeAccessPath[4] = VolumeLetter;
152   DriveType = GetDriveType(RootPath);
153   if (DriveType != DRIVE_REMOVABLE && DriveType != DRIVE_FIXED) {
154     return FALSE;
155   }
156 
157   DriveInfo->VolumeLetter = VolumeLetter;
158   VolumeHandle = CreateFile (
159                    VolumeAccessPath,
160                    0,
161                    FILE_SHARE_READ | FILE_SHARE_WRITE,
162                    NULL,
163                    OPEN_EXISTING,
164                    0,
165                    NULL
166                    );
167   if (VolumeHandle == INVALID_HANDLE_VALUE) {
168     fprintf (
169       stderr,
170       "error E0005: CreateFile failed: Volume = %s, LastError = 0x%x\n",
171       VolumeAccessPath,
172       GetLastError ()
173       );
174     return FALSE;
175   }
176 
177   //
178   // Get Disk Number. It should fail when operating on floppy. That's ok
179   //  because Disk Number is only needed when operating on Hard or USB disk.
180   //
181   // To direct write to disk:
182   //   for USB and HD: use path = \\.\PHYSICALDRIVEx, where x is Disk Number
183   //   for floppy:     use path = \\.\X:, where X can be A or B
184   //
185   Success = DeviceIoControl(
186               VolumeHandle,
187               IOCTL_STORAGE_GET_DEVICE_NUMBER,
188               NULL,
189               0,
190               &StorageDeviceNumber,
191               sizeof(StorageDeviceNumber),
192               &BytesReturned,
193               NULL
194               );
195   //
196   // DeviceIoControl should fail if Volume is floppy or network drive.
197   //
198   if (!Success) {
199     DriveInfo->DiskNumber = (UINT) -1;
200   } else if (StorageDeviceNumber.DeviceType != FILE_DEVICE_DISK) {
201     //
202     // Only care about the disk.
203     //
204     return FALSE;
205   } else{
206     DriveInfo->DiskNumber = StorageDeviceNumber.DeviceNumber;
207   }
208   CloseHandle(VolumeHandle);
209 
210   //
211   // Fill in the type string
212   //
213   DriveInfo->DriveType = NULL;
214   for (Index = 0; DriveTypeDesc[Index].Description != NULL; Index ++) {
215     if (DriveType == DriveTypeDesc[Index].Type) {
216       DriveInfo->DriveType = &DriveTypeDesc[Index];
217       break;
218     }
219   }
220 
221   if (DriveInfo->DriveType == NULL) {
222     //
223     // Should have a type.
224     //
225     fprintf (stderr, "error E3005: Fatal Error!!!\n");
226     return FALSE;
227   }
228   return TRUE;
229 }
230 
231 VOID
ListDrive(VOID)232 ListDrive (
233   VOID
234   )
235 /*++
236 Routine Description:
237   List every drive in current system and their information.
238 
239 --*/
240 {
241   UINT       Index;
242   DRIVE_INFO DriveInfo;
243 
244   UINT Mask =  GetLogicalDrives();
245 
246   for (Index = 0; Index < MAX_DRIVE; Index++) {
247     if (((Mask >> Index) & 0x1) == 1) {
248       if (GetDriveInfo ('A' + (CHAR) Index, &DriveInfo)) {
249         if (Index < 2) {
250           // Floppy will occupy 'A' and 'B'
251           fprintf (
252             stdout,
253             "%c: - Type: %s\n",
254             DriveInfo.VolumeLetter,
255             DriveInfo.DriveType->Description
256             );
257         } else {
258           fprintf (
259             stdout,
260             "%c: - DiskNum: %u, Type: %s\n",
261             DriveInfo.VolumeLetter,
262             (unsigned) DriveInfo.DiskNumber,
263             DriveInfo.DriveType->Description
264             );
265         }
266       }
267     }
268   }
269 
270 }
271 
272 INT
GetBootSectorOffset(HANDLE DiskHandle,PATH_INFO * PathInfo)273 GetBootSectorOffset (
274   HANDLE     DiskHandle,
275   PATH_INFO  *PathInfo
276   )
277 /*++
278 Description:
279   Get the offset of boot sector.
280   For non-MBR disk, offset is just 0
281   for disk with MBR, offset needs to be calculated by parsing MBR
282 
283   NOTE: if no one is active, we will patch MBR to select first partition as active.
284 
285 Arguments:
286   DiskHandle  : HANDLE of disk
287   PathInfo    : PATH_INFO structure.
288   WriteToDisk : TRUE indicates writing
289 
290 Return:
291   -1   : failed
292   o.w. : Offset to boot sector
293 --*/
294 {
295   BYTE    DiskPartition[0x200];
296   DWORD   BytesReturn;
297   DWORD   DbrOffset;
298   DWORD   Index;
299   BOOL    HasMbr;
300 
301   DbrOffset = 0;
302   HasMbr    = FALSE;
303 
304   SetFilePointer(DiskHandle, 0, NULL, FILE_BEGIN);
305   if (!ReadFile (DiskHandle, DiskPartition, 0x200, &BytesReturn, NULL)) {
306     return -1;
307   }
308 
309   //
310   // Check Signature, Jmp, and Boot Indicator.
311   // if all pass, we assume MBR found.
312   //
313 
314   // Check Signature: 55AA
315   if ((DiskPartition[0x1FE] == 0x55) && (DiskPartition[0x1FF] == 0xAA)) {
316     // Check Jmp: (EB ?? 90) or (E9 ?? ??)
317     if (((DiskPartition[0] != 0xEB) || (DiskPartition[2] != 0x90)) &&
318         (DiskPartition[0] != 0xE9)) {
319       // Check Boot Indicator: 0x00 or 0x80
320       // Boot Indicator is the first byte of Partition Entry
321       HasMbr = TRUE;
322       for (Index = 0; Index < PARTITION_ENTRY_NUM; ++Index) {
323         if ((DiskPartition[PARTITION_TABLE_OFFSET + Index * SIZE_OF_PARTITION_ENTRY] & 0x7F) != 0) {
324           HasMbr = FALSE;
325           break;
326         }
327       }
328     }
329   }
330 
331   if (HasMbr) {
332     //
333     // Skip MBR
334     //
335     for (Index = 0; Index < PARTITION_ENTRY_NUM; Index++) {
336       //
337       // Found Boot Indicator.
338       //
339       if (DiskPartition[PARTITION_TABLE_OFFSET + (Index * SIZE_OF_PARTITION_ENTRY)] == 0x80) {
340         DbrOffset = *(DWORD *)&DiskPartition[PARTITION_TABLE_OFFSET + (Index * SIZE_OF_PARTITION_ENTRY) + PARTITION_ENTRY_STARTLBA_OFFSET];
341         break;
342       }
343     }
344     //
345     // If no boot indicator, we manually select 1st partition, and patch MBR.
346     //
347     if (Index == PARTITION_ENTRY_NUM) {
348       DbrOffset = *(DWORD *)&DiskPartition[PARTITION_TABLE_OFFSET + PARTITION_ENTRY_STARTLBA_OFFSET];
349       if (!PathInfo->Input && (PathInfo->Type == PathUsb)) {
350         SetFilePointer(DiskHandle, 0, NULL, FILE_BEGIN);
351         DiskPartition[PARTITION_TABLE_OFFSET] = 0x80;
352         WriteFile (DiskHandle, DiskPartition, 0x200, &BytesReturn, NULL);
353       }
354     }
355   }
356 
357   return DbrOffset;
358 }
359 
360 /**
361  * Get window file handle for input/ouput disk/file.
362  *
363  * @param PathInfo
364  * @param ProcessMbr
365  * @param FileHandle
366  *
367  * @return ERROR_STATUS
368  */
369 ERROR_STATUS
GetFileHandle(PATH_INFO * PathInfo,BOOL ProcessMbr,HANDLE * FileHandle,DWORD * DbrOffset)370 GetFileHandle (
371   PATH_INFO  *PathInfo,
372   BOOL       ProcessMbr,
373   HANDLE     *FileHandle,
374   DWORD      *DbrOffset
375   )
376 {
377   DWORD  OpenFlag;
378 
379   OpenFlag = OPEN_ALWAYS;
380   if (PathInfo->Input || PathInfo->Type != PathFile) {
381     OpenFlag = OPEN_EXISTING;
382   }
383 
384   *FileHandle = CreateFile(
385                    PathInfo->PhysicalPath,
386                    GENERIC_READ | GENERIC_WRITE,
387                    FILE_SHARE_READ,
388                    NULL,
389                    OpenFlag,
390                    FILE_ATTRIBUTE_NORMAL,
391                    NULL
392                    );
393   if (*FileHandle == INVALID_HANDLE_VALUE) {
394     return ErrorFileCreate;
395   }
396 
397   if ((PathInfo->Type == PathIde) || (PathInfo->Type == PathUsb)){
398     *DbrOffset = GetBootSectorOffset (*FileHandle, PathInfo);
399     if (!ProcessMbr) {
400       //
401       // 1. Process boot sector, set file pointer to the beginning of boot sector
402       //
403       SetFilePointer (*FileHandle, *DbrOffset * 0x200, NULL, FILE_BEGIN);
404     } else if(*DbrOffset == 0) {
405       //
406       // If user want to process Mbr, but no Mbr exists, simply return FALSE
407       //
408       return ErrorNoMbr;
409     } else {
410       //
411       // 2. Process MBR, set file pointer to 0
412       //
413       SetFilePointer (*FileHandle, 0, NULL, FILE_BEGIN);
414     }
415   }
416 
417   return ErrorSuccess;
418 }
419 
420 /**
421   Writing or reading boot sector or MBR according to the argument.
422 
423   @param InputInfo PATH_INFO instance for input path
424   @param OutputInfo PATH_INFO instance for output path
425   @param ProcessMbr TRUE is to process MBR, otherwise, processing boot sector
426 
427   @return ERROR_STATUS
428  **/
429 ERROR_STATUS
ProcessBsOrMbr(PATH_INFO * InputInfo,PATH_INFO * OutputInfo,BOOL ProcessMbr)430 ProcessBsOrMbr (
431   PATH_INFO     *InputInfo,
432   PATH_INFO     *OutputInfo,
433   BOOL        	ProcessMbr
434   )
435 {
436   BYTE              DiskPartition[0x200] = {0};
437   BYTE              DiskPartitionBackup[0x200] = {0};
438   DWORD             BytesReturn;
439   INT               DrvNumOffset;
440   HANDLE            InputHandle;
441   HANDLE            OutputHandle;
442   ERROR_STATUS      Status;
443   DWORD             InputDbrOffset;
444   DWORD             OutputDbrOffset;
445 
446   //
447   // Create file Handle and move file Pointer is pointed to beginning of Mbr or Dbr
448   //
449   Status =  GetFileHandle(InputInfo, ProcessMbr, &InputHandle, &InputDbrOffset);
450   if (Status != ErrorSuccess) {
451     return Status;
452   }
453 
454   //
455   // Create file Handle and move file Pointer is pointed to beginning of Mbr or Dbr
456   //
457   Status = GetFileHandle(OutputInfo, ProcessMbr, &OutputHandle, &OutputDbrOffset);
458   if (Status != ErrorSuccess) {
459     return Status;
460   }
461 
462   //
463   // Read boot sector from source disk/file
464   //
465   if (!ReadFile (InputHandle, DiskPartition, 0x200, &BytesReturn, NULL)) {
466     return ErrorFileReadWrite;
467   }
468 
469   if (InputInfo->Type == PathUsb) {
470       // Manually set BS_DrvNum to 0x80 as window's format.exe has a bug which will clear this field discarding USB disk's MBR.
471       // offset of BS_DrvNum is 0x24 for FAT12/16
472       //                        0x40 for FAT32
473       //
474       DrvNumOffset = GetDrvNumOffset (DiskPartition);
475       if (DrvNumOffset == -1) {
476         return ErrorFatType;
477       }
478       //
479       // Some legacy BIOS require 0x80 discarding MBR.
480       // Question left here: is it needed to check Mbr before set 0x80?
481       //
482       DiskPartition[DrvNumOffset] = ((InputDbrOffset > 0) ? 0x80 : 0);
483   }
484 
485   if (InputInfo->Type == PathIde) {
486       //
487       // Patch LBAOffsetForBootSector
488       //
489       *(DWORD *)&DiskPartition [BOOT_SECTOR_LBA_OFFSET] = InputDbrOffset;
490   }
491 
492   if (OutputInfo->Type != PathFile) {
493     if (ProcessMbr) {
494       //
495       // Use original partition table
496       //
497       if (!ReadFile (OutputHandle, DiskPartitionBackup, 0x200, &BytesReturn, NULL)) {
498         return ErrorFileReadWrite;
499       }
500       memcpy (DiskPartition + 0x1BE, DiskPartitionBackup + 0x1BE, 0x40);
501       SetFilePointer (OutputHandle, 0, NULL, FILE_BEGIN);
502 
503     }
504   }
505 
506   //
507   // Write boot sector to taget disk/file
508   //
509   if (!WriteFile (OutputHandle, DiskPartition, 0x200, &BytesReturn, NULL)) {
510     return ErrorFileReadWrite;
511   }
512 
513   CloseHandle (InputHandle);
514   CloseHandle (OutputHandle);
515 
516   return ErrorSuccess;
517 }
518 
519 void
Version(void)520 Version (
521   void
522   )
523 /*++
524 
525 Routine Description:
526 
527   Displays the standard utility information to SDTOUT
528 
529 Arguments:
530 
531   None
532 
533 Returns:
534 
535   None
536 
537 --*/
538 {
539   printf ("%s Version %d.%d %s\n", UTILITY_NAME, UTILITY_MAJOR_VERSION, UTILITY_MINOR_VERSION, __BUILD_VERSION);
540 }
541 
542 VOID
PrintUsage(void)543 PrintUsage (
544   void
545   )
546 {
547   printf ("Usage: GenBootSector [options] --cfg-file CFG_FILE\n\n\
548 Copyright (c) 2009 - 2014, Intel Corporation.  All rights reserved.\n\n\
549   Utility to retrieve and update the boot sector or MBR.\n\n\
550 optional arguments:\n\
551   -h, --help            Show this help message and exit\n\
552   --version             Show program's version number and exit\n\
553   -d [DEBUG], --debug [DEBUG]\n\
554                         Output DEBUG statements, where DEBUG_LEVEL is 0 (min)\n\
555                         - 9 (max)\n\
556   -v, --verbose         Print informational statements\n\
557   -q, --quiet           Returns the exit code, error messages will be\n\
558                         displayed\n\
559   -s, --silent          Returns only the exit code; informational and error\n\
560                         messages are not displayed\n\
561   -l, --list            List disk drives\n\
562   -i INPUT_FILENAME, --input INPUT_FILENAME\n\
563                         Input file name\n\
564   -o OUTPUT_FILENAME, --output OUTPUT_FILENAME\n\
565                         Output file name\n\
566   -m, --mbr             Also process the MBR\n\
567   --sfo                 Reserved for future use\n");
568 
569 }
570 
571 /**
572   Get path information, including physical path for windows platform.
573 
574   @param PathInfo   Point to PATH_INFO structure.
575 
576   @return whether path is valid.
577 **/
578 ERROR_STATUS
GetPathInfo(PATH_INFO * PathInfo)579 GetPathInfo (
580   PATH_INFO   *PathInfo
581   )
582 {
583   DRIVE_INFO  DriveInfo;
584   CHAR        VolumeLetter;
585   CHAR        DiskPathTemplate[]   = "\\\\.\\PHYSICALDRIVE%u";
586   CHAR        FloppyPathTemplate[] = "\\\\.\\%c:";
587   FILE        *f;
588 
589   //
590   // If path is disk path
591   //
592   if (IsLetter(PathInfo->Path[0]) && (PathInfo->Path[1] == ':') && (PathInfo->Path[2] == '\0')) {
593     VolumeLetter = PathInfo->Path[0];
594     if ((VolumeLetter == 'A') || (VolumeLetter == 'a') ||
595         (VolumeLetter == 'B') || (VolumeLetter == 'b')) {
596       PathInfo->Type = PathFloppy;
597       sprintf (PathInfo->PhysicalPath, FloppyPathTemplate, VolumeLetter);
598       return ErrorSuccess;
599     }
600 
601     if (!GetDriveInfo(VolumeLetter, &DriveInfo)) {
602       fprintf (stderr, "ERROR: GetDriveInfo - 0x%x\n", GetLastError ());
603       return ErrorPath;
604     }
605 
606     if (!PathInfo->Input && (DriveInfo.DriveType->Type == DRIVE_FIXED)) {
607       fprintf (stderr, "ERROR: Could patch own IDE disk!\n");
608       return ErrorPath;
609     }
610 
611     sprintf(PathInfo->PhysicalPath, DiskPathTemplate, DriveInfo.DiskNumber);
612     if (DriveInfo.DriveType->Type == DRIVE_REMOVABLE) {
613       PathInfo->Type = PathUsb;
614     } else if (DriveInfo.DriveType->Type == DRIVE_FIXED) {
615       PathInfo->Type = PathIde;
616     } else {
617       fprintf (stderr, "ERROR, Invalid disk path - %s", PathInfo->Path);
618       return ErrorPath;
619     }
620 
621 	return ErrorSuccess;
622   }
623 
624   PathInfo->Type = PathFile;
625   if (PathInfo->Input) {
626     //
627     // If path is file path, check whether file is valid.
628     //
629     f = fopen (LongFilePath (PathInfo->Path), "r");
630     if (f == NULL) {
631       fprintf (stderr, "error E2003: File was not provided!\n");
632       return ErrorPath;
633     }
634   }
635   PathInfo->Type = PathFile;
636   strcpy(PathInfo->PhysicalPath, PathInfo->Path);
637 
638   return ErrorSuccess;
639 }
640 
641 INT
main(INT argc,CHAR * argv[])642 main (
643   INT  argc,
644   CHAR *argv[]
645   )
646 {
647   CHAR8         *AppName;
648   INTN          Index;
649   BOOLEAN       ProcessMbr;
650   ERROR_STATUS  Status;
651   EFI_STATUS    EfiStatus;
652   PATH_INFO     InputPathInfo = {0};
653   PATH_INFO     OutputPathInfo = {0};
654   UINT64        LogLevel;
655 
656   SetUtilityName (UTILITY_NAME);
657 
658   AppName = *argv;
659   argv ++;
660   argc --;
661 
662   ProcessMbr    = FALSE;
663 
664   if (argc == 0) {
665     PrintUsage();
666     return 0;
667   }
668 
669   //
670   // Parse command line
671   //
672   for (Index = 0; Index < argc; Index ++) {
673     if ((stricmp (argv[Index], "-l") == 0) || (stricmp (argv[Index], "--list") == 0)) {
674       ListDrive ();
675       return 0;
676     }
677 
678     if ((stricmp (argv[Index], "-m") == 0) || (stricmp (argv[Index], "--mbr") == 0)) {
679       ProcessMbr = TRUE;
680       continue;
681     }
682 
683     if ((stricmp (argv[Index], "-i") == 0) || (stricmp (argv[Index], "--input") == 0)) {
684       InputPathInfo.Path  = argv[Index + 1];
685       InputPathInfo.Input = TRUE;
686       if (InputPathInfo.Path == NULL) {
687         Error (NULL, 0, 1003, "Invalid option value", "Input file name can't be NULL");
688         return 1;
689       }
690       if (InputPathInfo.Path[0] == '-') {
691         Error (NULL, 0, 1003, "Invalid option value", "Input file is missing");
692         return 1;
693       }
694       ++Index;
695       continue;
696     }
697 
698     if ((stricmp (argv[Index], "-o") == 0) || (stricmp (argv[Index], "--output") == 0)) {
699       OutputPathInfo.Path  = argv[Index + 1];
700       OutputPathInfo.Input = FALSE;
701       if (OutputPathInfo.Path == NULL) {
702         Error (NULL, 0, 1003, "Invalid option value", "Output file name can't be NULL");
703         return 1;
704       }
705       if (OutputPathInfo.Path[0] == '-') {
706         Error (NULL, 0, 1003, "Invalid option value", "Output file is missing");
707         return 1;
708       }
709       ++Index;
710       continue;
711     }
712 
713     if ((stricmp (argv[Index], "-h") == 0) || (stricmp (argv[Index], "--help") == 0)) {
714       PrintUsage ();
715       return 0;
716     }
717 
718     if (stricmp (argv[Index], "--version") == 0) {
719       Version ();
720       return 0;
721     }
722 
723     if ((stricmp (argv[Index], "-v") == 0) || (stricmp (argv[Index], "--verbose") == 0)) {
724       continue;
725     }
726 
727     if ((stricmp (argv[Index], "-q") == 0) || (stricmp (argv[Index], "--quiet") == 0)) {
728       continue;
729     }
730 
731     if ((stricmp (argv[Index], "-d") == 0) || (stricmp (argv[Index], "--debug") == 0)) {
732       EfiStatus = AsciiStringToUint64 (argv[Index + 1], FALSE, &LogLevel);
733       if (EFI_ERROR (EfiStatus)) {
734         Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[Index], argv[Index + 1]);
735         return 1;
736       }
737       if (LogLevel > 9) {
738         Error (NULL, 0, 1003, "Invalid option value", "Debug Level range is 0-9, currnt input level is %d", (int) LogLevel);
739         return 1;
740       }
741       SetPrintLevel (LogLevel);
742       DebugMsg (NULL, 0, 9, "Debug Mode Set", "Debug Output Mode Level %s is set!", argv[Index + 1]);
743       ++Index;
744       continue;
745     }
746 
747     //
748     // Don't recognize the parameter.
749     //
750     Error (NULL, 0, 1000, "Unknown option", "%s", argv[Index]);
751     return 1;
752   }
753 
754   if (InputPathInfo.Path == NULL) {
755     Error (NULL, 0, 1001, "Missing options", "Input file is missing");
756     return 1;
757   }
758 
759   if (OutputPathInfo.Path == NULL) {
760     Error (NULL, 0, 1001, "Missing options", "Output file is missing");
761     return 1;
762   }
763 
764   if (GetPathInfo(&InputPathInfo) != ErrorSuccess) {
765     Error (NULL, 0, 1003, "Invalid option value", "Input file can't be found.");
766     return 1;
767   }
768 
769   if (GetPathInfo(&OutputPathInfo) != ErrorSuccess) {
770     Error (NULL, 0, 1003, "Invalid option value", "Output file can't be found.");
771     return 1;
772   }
773 
774   //
775   // Process DBR (Patch or Read)
776   //
777   Status = ProcessBsOrMbr (&InputPathInfo, &OutputPathInfo, ProcessMbr);
778 
779   if (Status == ErrorSuccess) {
780     fprintf (
781       stdout,
782       "%s %s: successful!\n",
783       (OutputPathInfo.Type != PathFile) ? "Write" : "Read",
784       ProcessMbr ? "MBR" : "DBR"
785       );
786     return 0;
787   } else {
788     fprintf (
789       stderr,
790       "%s: %s %s: failed - %s (LastError: 0x%x)!\n",
791       (Status == ErrorNoMbr) ? "WARNING" : "ERROR",
792       (OutputPathInfo.Type != PathFile) ? "Write" : "Read",
793       ProcessMbr ? "MBR" : "DBR",
794       ErrorStatusDesc[Status],
795       GetLastError ()
796       );
797     return 1;
798   }
799 }
800