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