3 Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR>
4 This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 Reading/writing MBR/DBR.
19 If we write MBR to disk, we just update the MBR code and the partition table wouldn't be over written.
20 If we process DBR, we will patch MBR to set first partition active if no active partition exists.
27 #include <Common/UefiBaseTypes.h>
30 #include "EfiUtilityMsgs.h"
31 #include "CommonLib.h"
36 #define UTILITY_NAME "GenBootSector"
39 // Utility version information
41 #define UTILITY_MAJOR_VERSION 0
42 #define UTILITY_MINOR_VERSION 2
45 #define PARTITION_TABLE_OFFSET 0x1BE
47 #define SIZE_OF_PARTITION_ENTRY 0x10
49 #define PARTITION_ENTRY_STARTLBA_OFFSET 8
51 #define PARTITION_ENTRY_NUM 4
63 PatchTypeFileImage
// input and output are all file image, patching action is same as PatchTypeFloppy
83 CHAR
*ErrorStatusDesc
[] = {
85 "Failed to create files",
86 "Failed to read/write files",
88 "Failed to detect Fat type",
92 typedef struct _DRIVE_TYPE_DESC
{
97 #define DRIVE_TYPE_ITEM(x) {x, #x}
98 DRIVE_TYPE_DESC DriveTypeDesc
[] = {
99 DRIVE_TYPE_ITEM (DRIVE_UNKNOWN
),
100 DRIVE_TYPE_ITEM (DRIVE_NO_ROOT_DIR
),
101 DRIVE_TYPE_ITEM (DRIVE_REMOVABLE
),
102 DRIVE_TYPE_ITEM (DRIVE_FIXED
),
103 DRIVE_TYPE_ITEM (DRIVE_REMOTE
),
104 DRIVE_TYPE_ITEM (DRIVE_CDROM
),
105 DRIVE_TYPE_ITEM (DRIVE_RAMDISK
),
109 typedef struct _DRIVE_INFO
{
111 DRIVE_TYPE_DESC
*DriveType
;
115 typedef struct _PATH_INFO
{
117 CHAR PhysicalPath
[260];
122 #define BOOT_SECTOR_LBA_OFFSET 0x1FA
124 #define IsLetter(x) (((x) >= 'a' && (x) <= 'z') || ((x) >= 'A' && (x) <= 'Z'))
129 DRIVE_INFO
*DriveInfo
133 Get drive information including disk number and drive type,
134 where disknumber is useful for reading/writing disk raw data.
135 NOTE: Floppy disk doesn't have disk number but it doesn't matter because
136 we can reading/writing floppy disk without disk number.
139 VolumeLetter : volume letter, e.g.: C for C:, A for A:
140 DriveInfo : pointer to DRIVE_INFO structure receiving drive information.
148 STORAGE_DEVICE_NUMBER StorageDeviceNumber
;
154 CHAR RootPath
[] = "X:\\"; // "X:\" -> for GetDriveType
155 CHAR VolumeAccessPath
[] = "\\\\.\\X:"; // "\\.\X:" -> to open the volume
157 RootPath
[0] = VolumeAccessPath
[4] = VolumeLetter
;
158 DriveType
= GetDriveType(RootPath
);
159 if (DriveType
!= DRIVE_REMOVABLE
&& DriveType
!= DRIVE_FIXED
) {
163 DriveInfo
->VolumeLetter
= VolumeLetter
;
164 VolumeHandle
= CreateFile (
167 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
173 if (VolumeHandle
== INVALID_HANDLE_VALUE
) {
176 "error E0005: CreateFile failed: Volume = %s, LastError = 0x%x\n",
184 // Get Disk Number. It should fail when operating on floppy. That's ok
185 // because Disk Number is only needed when operating on Hard or USB disk.
187 // To direct write to disk:
188 // for USB and HD: use path = \\.\PHYSICALDRIVEx, where x is Disk Number
189 // for floppy: use path = \\.\X:, where X can be A or B
191 Success
= DeviceIoControl(
193 IOCTL_STORAGE_GET_DEVICE_NUMBER
,
196 &StorageDeviceNumber
,
197 sizeof(StorageDeviceNumber
),
202 // DeviceIoControl should fail if Volume is floppy or network drive.
205 DriveInfo
->DiskNumber
= (UINT
) -1;
206 } else if (StorageDeviceNumber
.DeviceType
!= FILE_DEVICE_DISK
) {
208 // Only care about the disk.
212 DriveInfo
->DiskNumber
= StorageDeviceNumber
.DeviceNumber
;
214 CloseHandle(VolumeHandle
);
217 // Fill in the type string
219 DriveInfo
->DriveType
= NULL
;
220 for (Index
= 0; DriveTypeDesc
[Index
].Description
!= NULL
; Index
++) {
221 if (DriveType
== DriveTypeDesc
[Index
].Type
) {
222 DriveInfo
->DriveType
= &DriveTypeDesc
[Index
];
227 if (DriveInfo
->DriveType
== NULL
) {
229 // Should have a type.
231 fprintf (stderr
, "error E3005: Fatal Error!!!\n");
243 List every drive in current system and their information.
248 DRIVE_INFO DriveInfo
;
250 UINT Mask
= GetLogicalDrives();
252 for (Index
= 0; Index
< MAX_DRIVE
; Index
++) {
253 if (((Mask
>> Index
) & 0x1) == 1) {
254 if (GetDriveInfo ('A' + (CHAR
) Index
, &DriveInfo
)) {
256 // Floppy will occupy 'A' and 'B'
260 DriveInfo
.VolumeLetter
,
261 DriveInfo
.DriveType
->Description
266 "%c: - DiskNum: %u, Type: %s\n",
267 DriveInfo
.VolumeLetter
,
268 (unsigned) DriveInfo
.DiskNumber
,
269 DriveInfo
.DriveType
->Description
279 GetBootSectorOffset (
285 Get the offset of boot sector.
286 For non-MBR disk, offset is just 0
287 for disk with MBR, offset needs to be caculated by parsing MBR
289 NOTE: if no one is active, we will patch MBR to select first partition as active.
292 DiskHandle : HANDLE of disk
293 PathInfo : PATH_INFO structure.
294 WriteToDisk : TRUE indicates writing
298 o.w. : Offset to boot sector
301 BYTE DiskPartition
[0x200];
310 SetFilePointer(DiskHandle
, 0, NULL
, FILE_BEGIN
);
311 if (!ReadFile (DiskHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
)) {
316 // Check Signature, Jmp, and Boot Indicator.
317 // if all pass, we assume MBR found.
320 // Check Signature: 55AA
321 if ((DiskPartition
[0x1FE] == 0x55) && (DiskPartition
[0x1FF] == 0xAA)) {
322 // Check Jmp: (EB ?? 90) or (E9 ?? ??)
323 if (((DiskPartition
[0] != 0xEB) || (DiskPartition
[2] != 0x90)) &&
324 (DiskPartition
[0] != 0xE9)) {
325 // Check Boot Indicator: 0x00 or 0x80
326 // Boot Indicator is the first byte of Partition Entry
328 for (Index
= 0; Index
< PARTITION_ENTRY_NUM
; ++Index
) {
329 if ((DiskPartition
[PARTITION_TABLE_OFFSET
+ Index
* SIZE_OF_PARTITION_ENTRY
] & 0x7F) != 0) {
341 for (Index
= 0; Index
< PARTITION_ENTRY_NUM
; Index
++) {
343 // Found Boot Indicator.
345 if (DiskPartition
[PARTITION_TABLE_OFFSET
+ (Index
* SIZE_OF_PARTITION_ENTRY
)] == 0x80) {
346 DbrOffset
= *(DWORD
*)&DiskPartition
[PARTITION_TABLE_OFFSET
+ (Index
* SIZE_OF_PARTITION_ENTRY
) + PARTITION_ENTRY_STARTLBA_OFFSET
];
351 // If no boot indicator, we manually select 1st partition, and patch MBR.
353 if (Index
== PARTITION_ENTRY_NUM
) {
354 DbrOffset
= *(DWORD
*)&DiskPartition
[PARTITION_TABLE_OFFSET
+ PARTITION_ENTRY_STARTLBA_OFFSET
];
355 if (!PathInfo
->Input
&& (PathInfo
->Type
== PathUsb
)) {
356 SetFilePointer(DiskHandle
, 0, NULL
, FILE_BEGIN
);
357 DiskPartition
[PARTITION_TABLE_OFFSET
] = 0x80;
358 WriteFile (DiskHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
);
367 * Get window file handle for input/ouput disk/file.
373 * @return ERROR_STATUS
385 OpenFlag
= OPEN_ALWAYS
;
386 if (PathInfo
->Input
|| PathInfo
->Type
!= PathFile
) {
387 OpenFlag
= OPEN_EXISTING
;
390 *FileHandle
= CreateFile(
391 PathInfo
->PhysicalPath
,
392 GENERIC_READ
| GENERIC_WRITE
,
396 FILE_ATTRIBUTE_NORMAL
,
399 if (*FileHandle
== INVALID_HANDLE_VALUE
) {
400 return ErrorFileCreate
;
403 if ((PathInfo
->Type
== PathIde
) || (PathInfo
->Type
== PathUsb
)){
404 *DbrOffset
= GetBootSectorOffset (*FileHandle
, PathInfo
);
407 // 1. Process boot sector, set file pointer to the beginning of boot sector
409 SetFilePointer (*FileHandle
, *DbrOffset
* 0x200, NULL
, FILE_BEGIN
);
410 } else if(*DbrOffset
== 0) {
412 // If user want to process Mbr, but no Mbr exists, simply return FALSE
417 // 2. Process MBR, set file pointer to 0
419 SetFilePointer (*FileHandle
, 0, NULL
, FILE_BEGIN
);
427 Writing or reading boot sector or MBR according to the argument.
429 @param InputInfo PATH_INFO instance for input path
430 @param OutputInfo PATH_INFO instance for output path
431 @param ProcessMbr TRUE is to process MBR, otherwise, processing boot sector
437 PATH_INFO
*InputInfo
,
438 PATH_INFO
*OutputInfo
,
442 BYTE DiskPartition
[0x200] = {0};
443 BYTE DiskPartitionBackup
[0x200] = {0};
449 DWORD InputDbrOffset
;
450 DWORD OutputDbrOffset
;
453 // Create file Handle and move file Pointer is pointed to beginning of Mbr or Dbr
455 Status
= GetFileHandle(InputInfo
, ProcessMbr
, &InputHandle
, &InputDbrOffset
);
456 if (Status
!= ErrorSuccess
) {
461 // Create file Handle and move file Pointer is pointed to beginning of Mbr or Dbr
463 Status
= GetFileHandle(OutputInfo
, ProcessMbr
, &OutputHandle
, &OutputDbrOffset
);
464 if (Status
!= ErrorSuccess
) {
469 // Read boot sector from source disk/file
471 if (!ReadFile (InputHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
)) {
472 return ErrorFileReadWrite
;
475 if (InputInfo
->Type
== PathUsb
) {
476 // Manually set BS_DrvNum to 0x80 as window's format.exe has a bug which will clear this field discarding USB disk's MBR.
477 // offset of BS_DrvNum is 0x24 for FAT12/16
480 DrvNumOffset
= GetDrvNumOffset (DiskPartition
);
481 if (DrvNumOffset
== -1) {
485 // Some legacy BIOS require 0x80 discarding MBR.
486 // Question left here: is it needed to check Mbr before set 0x80?
488 DiskPartition
[DrvNumOffset
] = ((InputDbrOffset
> 0) ? 0x80 : 0);
491 if (InputInfo
->Type
== PathIde
) {
493 // Patch LBAOffsetForBootSector
495 *(DWORD
*)&DiskPartition
[BOOT_SECTOR_LBA_OFFSET
] = InputDbrOffset
;
498 if (OutputInfo
->Type
!= PathFile
) {
501 // Use original partition table
503 if (!ReadFile (OutputHandle
, DiskPartitionBackup
, 0x200, &BytesReturn
, NULL
)) {
504 return ErrorFileReadWrite
;
506 memcpy (DiskPartition
+ 0x1BE, DiskPartitionBackup
+ 0x1BE, 0x40);
507 SetFilePointer (OutputHandle
, 0, NULL
, FILE_BEGIN
);
513 // Write boot sector to taget disk/file
515 if (!WriteFile (OutputHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
)) {
516 return ErrorFileReadWrite
;
519 CloseHandle (InputHandle
);
520 CloseHandle (OutputHandle
);
533 Displays the standard utility information to SDTOUT
545 printf ("%s Version %d.%d %s\n", UTILITY_NAME
, UTILITY_MAJOR_VERSION
, UTILITY_MINOR_VERSION
, __BUILD_VERSION
);
553 printf ("Usage: GenBootSector [options] --cfg-file CFG_FILE\n\n\
554 Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.\n\n\
555 Utility to retrieve and update the boot sector or MBR.\n\n\
556 optional arguments:\n\
557 -h, --help Show this help message and exit\n\
558 --version Show program's version number and exit\n\
559 -d [DEBUG], --debug [DEBUG]\n\
560 Output DEBUG statements, where DEBUG_LEVEL is 0 (min)\n\
562 -v, --verbose Print informational statements\n\
563 -q, --quiet Returns the exit code, error messages will be\n\
565 -s, --silent Returns only the exit code; informational and error\n\
566 messages are not displayed\n\
567 -l, --list List disk drives\n\
568 -i INPUT_FILENAME, --input INPUT_FILENAME\n\
570 -o OUTPUT_FILENAME, --output OUTPUT_FILENAME\n\
572 -m, --mbr Also process the MBR\n\
573 --sfo Reserved for future use\n");
578 Get path information, including physical path for windows platform.
580 @param PathInfo Point to PATH_INFO structure.
582 @return whether path is valid.
589 DRIVE_INFO DriveInfo
;
591 CHAR DiskPathTemplate
[] = "\\\\.\\PHYSICALDRIVE%u";
592 CHAR FloppyPathTemplate
[] = "\\\\.\\%c:";
596 // If path is disk path
598 if (IsLetter(PathInfo
->Path
[0]) && (PathInfo
->Path
[1] == ':') && (PathInfo
->Path
[2] == '\0')) {
599 VolumeLetter
= PathInfo
->Path
[0];
600 if ((VolumeLetter
== 'A') || (VolumeLetter
== 'a') ||
601 (VolumeLetter
== 'B') || (VolumeLetter
== 'b')) {
602 PathInfo
->Type
= PathFloppy
;
603 sprintf (PathInfo
->PhysicalPath
, FloppyPathTemplate
, VolumeLetter
);
607 if (!GetDriveInfo(VolumeLetter
, &DriveInfo
)) {
608 fprintf (stderr
, "ERROR: GetDriveInfo - 0x%x\n", GetLastError ());
612 if (!PathInfo
->Input
&& (DriveInfo
.DriveType
->Type
== DRIVE_FIXED
)) {
613 fprintf (stderr
, "ERROR: Could patch own IDE disk!\n");
617 sprintf(PathInfo
->PhysicalPath
, DiskPathTemplate
, DriveInfo
.DiskNumber
);
618 if (DriveInfo
.DriveType
->Type
== DRIVE_REMOVABLE
) {
619 PathInfo
->Type
= PathUsb
;
620 } else if (DriveInfo
.DriveType
->Type
== DRIVE_FIXED
) {
621 PathInfo
->Type
= PathIde
;
623 fprintf (stderr
, "ERROR, Invalid disk path - %s", PathInfo
->Path
);
630 PathInfo
->Type
= PathFile
;
631 if (PathInfo
->Input
) {
633 // If path is file path, check whether file is valid.
635 f
= fopen (LongFilePath (PathInfo
->Path
), "r");
637 fprintf (stderr
, "error E2003: File was not provided!\n");
641 PathInfo
->Type
= PathFile
;
642 strcpy(PathInfo
->PhysicalPath
, PathInfo
->Path
);
657 EFI_STATUS EfiStatus
;
658 PATH_INFO InputPathInfo
= {0};
659 PATH_INFO OutputPathInfo
= {0};
662 SetUtilityName (UTILITY_NAME
);
676 // Parse command line
678 for (Index
= 0; Index
< argc
; Index
++) {
679 if ((stricmp (argv
[Index
], "-l") == 0) || (stricmp (argv
[Index
], "--list") == 0)) {
684 if ((stricmp (argv
[Index
], "-m") == 0) || (stricmp (argv
[Index
], "--mbr") == 0)) {
689 if ((stricmp (argv
[Index
], "-i") == 0) || (stricmp (argv
[Index
], "--input") == 0)) {
690 InputPathInfo
.Path
= argv
[Index
+ 1];
691 InputPathInfo
.Input
= TRUE
;
692 if (InputPathInfo
.Path
== NULL
) {
693 Error (NULL
, 0, 1003, "Invalid option value", "Input file name can't be NULL");
696 if (InputPathInfo
.Path
[0] == '-') {
697 Error (NULL
, 0, 1003, "Invalid option value", "Input file is missing");
704 if ((stricmp (argv
[Index
], "-o") == 0) || (stricmp (argv
[Index
], "--output") == 0)) {
705 OutputPathInfo
.Path
= argv
[Index
+ 1];
706 OutputPathInfo
.Input
= FALSE
;
707 if (OutputPathInfo
.Path
== NULL
) {
708 Error (NULL
, 0, 1003, "Invalid option value", "Output file name can't be NULL");
711 if (OutputPathInfo
.Path
[0] == '-') {
712 Error (NULL
, 0, 1003, "Invalid option value", "Output file is missing");
719 if ((stricmp (argv
[Index
], "-h") == 0) || (stricmp (argv
[Index
], "--help") == 0)) {
724 if (stricmp (argv
[Index
], "--version") == 0) {
729 if ((stricmp (argv
[Index
], "-v") == 0) || (stricmp (argv
[Index
], "--verbose") == 0)) {
733 if ((stricmp (argv
[Index
], "-q") == 0) || (stricmp (argv
[Index
], "--quiet") == 0)) {
737 if ((stricmp (argv
[Index
], "-d") == 0) || (stricmp (argv
[Index
], "--debug") == 0)) {
738 EfiStatus
= AsciiStringToUint64 (argv
[Index
+ 1], FALSE
, &LogLevel
);
739 if (EFI_ERROR (EfiStatus
)) {
740 Error (NULL
, 0, 1003, "Invalid option value", "%s = %s", argv
[Index
], argv
[Index
+ 1]);
744 Error (NULL
, 0, 1003, "Invalid option value", "Debug Level range is 0-9, currnt input level is %d", (int) LogLevel
);
747 SetPrintLevel (LogLevel
);
748 DebugMsg (NULL
, 0, 9, "Debug Mode Set", "Debug Output Mode Level %s is set!", argv
[Index
+ 1]);
754 // Don't recognize the parameter.
756 Error (NULL
, 0, 1000, "Unknown option", "%s", argv
[Index
]);
760 if (InputPathInfo
.Path
== NULL
) {
761 Error (NULL
, 0, 1001, "Missing options", "Input file is missing");
765 if (OutputPathInfo
.Path
== NULL
) {
766 Error (NULL
, 0, 1001, "Missing options", "Output file is missing");
770 if (GetPathInfo(&InputPathInfo
) != ErrorSuccess
) {
771 Error (NULL
, 0, 1003, "Invalid option value", "Input file can't be found.");
775 if (GetPathInfo(&OutputPathInfo
) != ErrorSuccess
) {
776 Error (NULL
, 0, 1003, "Invalid option value", "Output file can't be found.");
781 // Process DBR (Patch or Read)
783 Status
= ProcessBsOrMbr (&InputPathInfo
, &OutputPathInfo
, ProcessMbr
);
785 if (Status
== ErrorSuccess
) {
788 "%s %s: successful!\n",
789 (OutputPathInfo
.Type
!= PathFile
) ? "Write" : "Read",
790 ProcessMbr
? "MBR" : "DBR"
796 "%s: %s %s: failed - %s (LastError: 0x%x)!\n",
797 (Status
== ErrorNoMbr
) ? "WARNING" : "ERROR",
798 (OutputPathInfo
.Type
!= PathFile
) ? "Write" : "Read",
799 ProcessMbr
? "MBR" : "DBR",
800 ErrorStatusDesc
[Status
],