2 Reading/writing MBR/DBR.
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.
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
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.
21 #include <Common/UefiBaseTypes.h>
24 #include "EfiUtilityMsgs.h"
25 #include "CommonLib.h"
30 #define UTILITY_NAME "GenBootSector"
33 // Utility version information
35 #define UTILITY_MAJOR_VERSION 0
36 #define UTILITY_MINOR_VERSION 2
39 #define PARTITION_TABLE_OFFSET 0x1BE
41 #define SIZE_OF_PARTITION_ENTRY 0x10
43 #define PARTITION_ENTRY_STARTLBA_OFFSET 8
45 #define PARTITION_ENTRY_NUM 4
57 PatchTypeFileImage
// input and output are all file image, patching action is same as PatchTypeFloppy
77 CHAR
*ErrorStatusDesc
[] = {
79 "Failed to create files",
80 "Failed to read/write files",
82 "Failed to detect Fat type",
86 typedef struct _DRIVE_TYPE_DESC
{
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
),
103 typedef struct _DRIVE_INFO
{
105 DRIVE_TYPE_DESC
*DriveType
;
109 typedef struct _PATH_INFO
{
111 CHAR PhysicalPath
[260];
116 #define BOOT_SECTOR_LBA_OFFSET 0x1FA
118 #define IsLetter(x) (((x) >= 'a' && (x) <= 'z') || ((x) >= 'A' && (x) <= 'Z'))
123 DRIVE_INFO
*DriveInfo
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.
133 VolumeLetter : volume letter, e.g.: C for C:, A for A:
134 DriveInfo : pointer to DRIVE_INFO structure receiving drive information.
142 STORAGE_DEVICE_NUMBER StorageDeviceNumber
;
148 CHAR RootPath
[] = "X:\\"; // "X:\" -> for GetDriveType
149 CHAR VolumeAccessPath
[] = "\\\\.\\X:"; // "\\.\X:" -> to open the volume
151 RootPath
[0] = VolumeAccessPath
[4] = VolumeLetter
;
152 DriveType
= GetDriveType(RootPath
);
153 if (DriveType
!= DRIVE_REMOVABLE
&& DriveType
!= DRIVE_FIXED
) {
157 DriveInfo
->VolumeLetter
= VolumeLetter
;
158 VolumeHandle
= CreateFile (
161 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
167 if (VolumeHandle
== INVALID_HANDLE_VALUE
) {
170 "error E0005: CreateFile failed: Volume = %s, LastError = 0x%x\n",
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.
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
185 Success
= DeviceIoControl(
187 IOCTL_STORAGE_GET_DEVICE_NUMBER
,
190 &StorageDeviceNumber
,
191 sizeof(StorageDeviceNumber
),
196 // DeviceIoControl should fail if Volume is floppy or network drive.
199 DriveInfo
->DiskNumber
= (UINT
) -1;
200 } else if (StorageDeviceNumber
.DeviceType
!= FILE_DEVICE_DISK
) {
202 // Only care about the disk.
206 DriveInfo
->DiskNumber
= StorageDeviceNumber
.DeviceNumber
;
208 CloseHandle(VolumeHandle
);
211 // Fill in the type string
213 DriveInfo
->DriveType
= NULL
;
214 for (Index
= 0; DriveTypeDesc
[Index
].Description
!= NULL
; Index
++) {
215 if (DriveType
== DriveTypeDesc
[Index
].Type
) {
216 DriveInfo
->DriveType
= &DriveTypeDesc
[Index
];
221 if (DriveInfo
->DriveType
== NULL
) {
223 // Should have a type.
225 fprintf (stderr
, "error E3005: Fatal Error!!!\n");
237 List every drive in current system and their information.
242 DRIVE_INFO DriveInfo
;
244 UINT Mask
= GetLogicalDrives();
246 for (Index
= 0; Index
< MAX_DRIVE
; Index
++) {
247 if (((Mask
>> Index
) & 0x1) == 1) {
248 if (GetDriveInfo ('A' + (CHAR
) Index
, &DriveInfo
)) {
250 // Floppy will occupy 'A' and 'B'
254 DriveInfo
.VolumeLetter
,
255 DriveInfo
.DriveType
->Description
260 "%c: - DiskNum: %u, Type: %s\n",
261 DriveInfo
.VolumeLetter
,
262 (unsigned) DriveInfo
.DiskNumber
,
263 DriveInfo
.DriveType
->Description
273 GetBootSectorOffset (
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 caculated by parsing MBR
283 NOTE: if no one is active, we will patch MBR to select first partition as active.
286 DiskHandle : HANDLE of disk
287 PathInfo : PATH_INFO structure.
288 WriteToDisk : TRUE indicates writing
292 o.w. : Offset to boot sector
295 BYTE DiskPartition
[0x200];
304 SetFilePointer(DiskHandle
, 0, NULL
, FILE_BEGIN
);
305 if (!ReadFile (DiskHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
)) {
310 // Check Signature, Jmp, and Boot Indicator.
311 // if all pass, we assume MBR found.
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
322 for (Index
= 0; Index
< PARTITION_ENTRY_NUM
; ++Index
) {
323 if ((DiskPartition
[PARTITION_TABLE_OFFSET
+ Index
* SIZE_OF_PARTITION_ENTRY
] & 0x7F) != 0) {
335 for (Index
= 0; Index
< PARTITION_ENTRY_NUM
; Index
++) {
337 // Found Boot Indicator.
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
];
345 // If no boot indicator, we manually select 1st partition, and patch MBR.
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
);
361 * Get window file handle for input/ouput disk/file.
367 * @return ERROR_STATUS
379 OpenFlag
= OPEN_ALWAYS
;
380 if (PathInfo
->Input
|| PathInfo
->Type
!= PathFile
) {
381 OpenFlag
= OPEN_EXISTING
;
384 *FileHandle
= CreateFile(
385 PathInfo
->PhysicalPath
,
386 GENERIC_READ
| GENERIC_WRITE
,
390 FILE_ATTRIBUTE_NORMAL
,
393 if (*FileHandle
== INVALID_HANDLE_VALUE
) {
394 return ErrorFileCreate
;
397 if ((PathInfo
->Type
== PathIde
) || (PathInfo
->Type
== PathUsb
)){
398 *DbrOffset
= GetBootSectorOffset (*FileHandle
, PathInfo
);
401 // 1. Process boot sector, set file pointer to the beginning of boot sector
403 SetFilePointer (*FileHandle
, *DbrOffset
* 0x200, NULL
, FILE_BEGIN
);
404 } else if(*DbrOffset
== 0) {
406 // If user want to process Mbr, but no Mbr exists, simply return FALSE
411 // 2. Process MBR, set file pointer to 0
413 SetFilePointer (*FileHandle
, 0, NULL
, FILE_BEGIN
);
421 Writing or reading boot sector or MBR according to the argument.
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
431 PATH_INFO
*InputInfo
,
432 PATH_INFO
*OutputInfo
,
436 BYTE DiskPartition
[0x200] = {0};
437 BYTE DiskPartitionBackup
[0x200] = {0};
443 DWORD InputDbrOffset
;
444 DWORD OutputDbrOffset
;
447 // Create file Handle and move file Pointer is pointed to beginning of Mbr or Dbr
449 Status
= GetFileHandle(InputInfo
, ProcessMbr
, &InputHandle
, &InputDbrOffset
);
450 if (Status
!= ErrorSuccess
) {
455 // Create file Handle and move file Pointer is pointed to beginning of Mbr or Dbr
457 Status
= GetFileHandle(OutputInfo
, ProcessMbr
, &OutputHandle
, &OutputDbrOffset
);
458 if (Status
!= ErrorSuccess
) {
463 // Read boot sector from source disk/file
465 if (!ReadFile (InputHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
)) {
466 return ErrorFileReadWrite
;
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
474 DrvNumOffset
= GetDrvNumOffset (DiskPartition
);
475 if (DrvNumOffset
== -1) {
479 // Some legacy BIOS require 0x80 discarding MBR.
480 // Question left here: is it needed to check Mbr before set 0x80?
482 DiskPartition
[DrvNumOffset
] = ((InputDbrOffset
> 0) ? 0x80 : 0);
485 if (InputInfo
->Type
== PathIde
) {
487 // Patch LBAOffsetForBootSector
489 *(DWORD
*)&DiskPartition
[BOOT_SECTOR_LBA_OFFSET
] = InputDbrOffset
;
492 if (OutputInfo
->Type
!= PathFile
) {
495 // Use original partition table
497 if (!ReadFile (OutputHandle
, DiskPartitionBackup
, 0x200, &BytesReturn
, NULL
)) {
498 return ErrorFileReadWrite
;
500 memcpy (DiskPartition
+ 0x1BE, DiskPartitionBackup
+ 0x1BE, 0x40);
501 SetFilePointer (OutputHandle
, 0, NULL
, FILE_BEGIN
);
507 // Write boot sector to taget disk/file
509 if (!WriteFile (OutputHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
)) {
510 return ErrorFileReadWrite
;
513 CloseHandle (InputHandle
);
514 CloseHandle (OutputHandle
);
527 Displays the standard utility information to SDTOUT
539 printf ("%s Version %d.%d %s\n", UTILITY_NAME
, UTILITY_MAJOR_VERSION
, UTILITY_MINOR_VERSION
, __BUILD_VERSION
);
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\
556 -v, --verbose Print informational statements\n\
557 -q, --quiet Returns the exit code, error messages will be\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\
564 -o OUTPUT_FILENAME, --output OUTPUT_FILENAME\n\
566 -m, --mbr Also process the MBR\n\
567 --sfo Reserved for future use\n");
572 Get path information, including physical path for windows platform.
574 @param PathInfo Point to PATH_INFO structure.
576 @return whether path is valid.
583 DRIVE_INFO DriveInfo
;
585 CHAR DiskPathTemplate
[] = "\\\\.\\PHYSICALDRIVE%u";
586 CHAR FloppyPathTemplate
[] = "\\\\.\\%c:";
590 // If path is disk path
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
);
601 if (!GetDriveInfo(VolumeLetter
, &DriveInfo
)) {
602 fprintf (stderr
, "ERROR: GetDriveInfo - 0x%x\n", GetLastError ());
606 if (!PathInfo
->Input
&& (DriveInfo
.DriveType
->Type
== DRIVE_FIXED
)) {
607 fprintf (stderr
, "ERROR: Could patch own IDE disk!\n");
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
;
617 fprintf (stderr
, "ERROR, Invalid disk path - %s", PathInfo
->Path
);
624 PathInfo
->Type
= PathFile
;
625 if (PathInfo
->Input
) {
627 // If path is file path, check whether file is valid.
629 f
= fopen (LongFilePath (PathInfo
->Path
), "r");
631 fprintf (stderr
, "error E2003: File was not provided!\n");
635 PathInfo
->Type
= PathFile
;
636 strcpy(PathInfo
->PhysicalPath
, PathInfo
->Path
);
651 EFI_STATUS EfiStatus
;
652 PATH_INFO InputPathInfo
= {0};
653 PATH_INFO OutputPathInfo
= {0};
656 SetUtilityName (UTILITY_NAME
);
670 // Parse command line
672 for (Index
= 0; Index
< argc
; Index
++) {
673 if ((stricmp (argv
[Index
], "-l") == 0) || (stricmp (argv
[Index
], "--list") == 0)) {
678 if ((stricmp (argv
[Index
], "-m") == 0) || (stricmp (argv
[Index
], "--mbr") == 0)) {
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");
690 if (InputPathInfo
.Path
[0] == '-') {
691 Error (NULL
, 0, 1003, "Invalid option value", "Input file is missing");
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");
705 if (OutputPathInfo
.Path
[0] == '-') {
706 Error (NULL
, 0, 1003, "Invalid option value", "Output file is missing");
713 if ((stricmp (argv
[Index
], "-h") == 0) || (stricmp (argv
[Index
], "--help") == 0)) {
718 if (stricmp (argv
[Index
], "--version") == 0) {
723 if ((stricmp (argv
[Index
], "-v") == 0) || (stricmp (argv
[Index
], "--verbose") == 0)) {
727 if ((stricmp (argv
[Index
], "-q") == 0) || (stricmp (argv
[Index
], "--quiet") == 0)) {
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]);
738 Error (NULL
, 0, 1003, "Invalid option value", "Debug Level range is 0-9, currnt input level is %d", (int) LogLevel
);
741 SetPrintLevel (LogLevel
);
742 DebugMsg (NULL
, 0, 9, "Debug Mode Set", "Debug Output Mode Level %s is set!", argv
[Index
+ 1]);
748 // Don't recognize the parameter.
750 Error (NULL
, 0, 1000, "Unknown option", "%s", argv
[Index
]);
754 if (InputPathInfo
.Path
== NULL
) {
755 Error (NULL
, 0, 1001, "Missing options", "Input file is missing");
759 if (OutputPathInfo
.Path
== NULL
) {
760 Error (NULL
, 0, 1001, "Missing options", "Output file is missing");
764 if (GetPathInfo(&InputPathInfo
) != ErrorSuccess
) {
765 Error (NULL
, 0, 1003, "Invalid option value", "Input file can't be found.");
769 if (GetPathInfo(&OutputPathInfo
) != ErrorSuccess
) {
770 Error (NULL
, 0, 1003, "Invalid option value", "Output file can't be found.");
775 // Process DBR (Patch or Read)
777 Status
= ProcessBsOrMbr (&InputPathInfo
, &OutputPathInfo
, ProcessMbr
);
779 if (Status
== ErrorSuccess
) {
782 "%s %s: successful!\n",
783 (OutputPathInfo
.Type
!= PathFile
) ? "Write" : "Read",
784 ProcessMbr
? "MBR" : "DBR"
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
],