Home | History | Annotate | Download | only in GenBootSector
      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
    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
    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
    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
    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
    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
    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
    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
    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
    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