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 - 2017, 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%lx\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.
204 CloseHandle(VolumeHandle
);
207 DriveInfo
->DiskNumber
= StorageDeviceNumber
.DeviceNumber
;
209 CloseHandle(VolumeHandle
);
212 // Fill in the type string
214 DriveInfo
->DriveType
= NULL
;
215 for (Index
= 0; DriveTypeDesc
[Index
].Description
!= NULL
; Index
++) {
216 if (DriveType
== DriveTypeDesc
[Index
].Type
) {
217 DriveInfo
->DriveType
= &DriveTypeDesc
[Index
];
222 if (DriveInfo
->DriveType
== NULL
) {
224 // Should have a type.
226 fprintf (stderr
, "error E3005: Fatal Error!!!\n");
238 List every drive in current system and their information.
243 DRIVE_INFO DriveInfo
;
245 UINT Mask
= GetLogicalDrives();
247 for (Index
= 0; Index
< MAX_DRIVE
; Index
++) {
248 if (((Mask
>> Index
) & 0x1) == 1) {
249 if (GetDriveInfo ('A' + (CHAR
) Index
, &DriveInfo
)) {
251 // Floppy will occupy 'A' and 'B'
255 DriveInfo
.VolumeLetter
,
256 DriveInfo
.DriveType
->Description
261 "%c: - DiskNum: %u, Type: %s\n",
262 DriveInfo
.VolumeLetter
,
263 (unsigned) DriveInfo
.DiskNumber
,
264 DriveInfo
.DriveType
->Description
274 GetBootSectorOffset (
280 Get the offset of boot sector.
281 For non-MBR disk, offset is just 0
282 for disk with MBR, offset needs to be calculated by parsing MBR
284 NOTE: if no one is active, we will patch MBR to select first partition as active.
287 DiskHandle : HANDLE of disk
288 PathInfo : PATH_INFO structure.
289 WriteToDisk : TRUE indicates writing
293 o.w. : Offset to boot sector
296 BYTE DiskPartition
[0x200];
305 SetFilePointer(DiskHandle
, 0, NULL
, FILE_BEGIN
);
306 if (!ReadFile (DiskHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
)) {
311 // Check Signature, Jmp, and Boot Indicator.
312 // if all pass, we assume MBR found.
315 // Check Signature: 55AA
316 if ((DiskPartition
[0x1FE] == 0x55) && (DiskPartition
[0x1FF] == 0xAA)) {
317 // Check Jmp: (EB ?? 90) or (E9 ?? ??)
318 if (((DiskPartition
[0] != 0xEB) || (DiskPartition
[2] != 0x90)) &&
319 (DiskPartition
[0] != 0xE9)) {
320 // Check Boot Indicator: 0x00 or 0x80
321 // Boot Indicator is the first byte of Partition Entry
323 for (Index
= 0; Index
< PARTITION_ENTRY_NUM
; ++Index
) {
324 if ((DiskPartition
[PARTITION_TABLE_OFFSET
+ Index
* SIZE_OF_PARTITION_ENTRY
] & 0x7F) != 0) {
336 for (Index
= 0; Index
< PARTITION_ENTRY_NUM
; Index
++) {
338 // Found Boot Indicator.
340 if (DiskPartition
[PARTITION_TABLE_OFFSET
+ (Index
* SIZE_OF_PARTITION_ENTRY
)] == 0x80) {
341 DbrOffset
= *(DWORD
*)&DiskPartition
[PARTITION_TABLE_OFFSET
+ (Index
* SIZE_OF_PARTITION_ENTRY
) + PARTITION_ENTRY_STARTLBA_OFFSET
];
346 // If no boot indicator, we manually select 1st partition, and patch MBR.
348 if (Index
== PARTITION_ENTRY_NUM
) {
349 DbrOffset
= *(DWORD
*)&DiskPartition
[PARTITION_TABLE_OFFSET
+ PARTITION_ENTRY_STARTLBA_OFFSET
];
350 if (!PathInfo
->Input
&& (PathInfo
->Type
== PathUsb
)) {
351 SetFilePointer(DiskHandle
, 0, NULL
, FILE_BEGIN
);
352 DiskPartition
[PARTITION_TABLE_OFFSET
] = 0x80;
353 WriteFile (DiskHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
);
362 * Get window file handle for input/ouput disk/file.
368 * @return ERROR_STATUS
380 OpenFlag
= OPEN_ALWAYS
;
381 if (PathInfo
->Input
|| PathInfo
->Type
!= PathFile
) {
382 OpenFlag
= OPEN_EXISTING
;
385 *FileHandle
= CreateFile(
386 PathInfo
->PhysicalPath
,
387 GENERIC_READ
| GENERIC_WRITE
,
391 FILE_ATTRIBUTE_NORMAL
,
394 if (*FileHandle
== INVALID_HANDLE_VALUE
) {
395 return ErrorFileCreate
;
398 if ((PathInfo
->Type
== PathIde
) || (PathInfo
->Type
== PathUsb
)){
399 *DbrOffset
= GetBootSectorOffset (*FileHandle
, PathInfo
);
402 // 1. Process boot sector, set file pointer to the beginning of boot sector
404 SetFilePointer (*FileHandle
, *DbrOffset
* 0x200, NULL
, FILE_BEGIN
);
405 } else if(*DbrOffset
== 0) {
407 // If user want to process Mbr, but no Mbr exists, simply return FALSE
412 // 2. Process MBR, set file pointer to 0
414 SetFilePointer (*FileHandle
, 0, NULL
, FILE_BEGIN
);
422 Writing or reading boot sector or MBR according to the argument.
424 @param InputInfo PATH_INFO instance for input path
425 @param OutputInfo PATH_INFO instance for output path
426 @param ProcessMbr TRUE is to process MBR, otherwise, processing boot sector
432 PATH_INFO
*InputInfo
,
433 PATH_INFO
*OutputInfo
,
437 BYTE DiskPartition
[0x200] = {0};
438 BYTE DiskPartitionBackup
[0x200] = {0};
441 HANDLE InputHandle
= INVALID_HANDLE_VALUE
;
442 HANDLE OutputHandle
= INVALID_HANDLE_VALUE
;
444 DWORD InputDbrOffset
;
445 DWORD OutputDbrOffset
;
448 // Create file Handle and move file Pointer is pointed to beginning of Mbr or Dbr
450 Status
= GetFileHandle(InputInfo
, ProcessMbr
, &InputHandle
, &InputDbrOffset
);
451 if (Status
!= ErrorSuccess
) {
456 // Create file Handle and move file Pointer is pointed to beginning of Mbr or Dbr
458 Status
= GetFileHandle(OutputInfo
, ProcessMbr
, &OutputHandle
, &OutputDbrOffset
);
459 if (Status
!= ErrorSuccess
) {
464 // Read boot sector from source disk/file
466 if (!ReadFile (InputHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
)) {
467 Status
= ErrorFileReadWrite
;
471 if (InputInfo
->Type
== PathUsb
) {
472 // Manually set BS_DrvNum to 0x80 as window's format.exe has a bug which will clear this field discarding USB disk's MBR.
473 // offset of BS_DrvNum is 0x24 for FAT12/16
476 DrvNumOffset
= GetDrvNumOffset (DiskPartition
);
477 if (DrvNumOffset
== -1) {
478 Status
= ErrorFatType
;
482 // Some legacy BIOS require 0x80 discarding MBR.
483 // Question left here: is it needed to check Mbr before set 0x80?
485 DiskPartition
[DrvNumOffset
] = ((InputDbrOffset
> 0) ? 0x80 : 0);
488 if (InputInfo
->Type
== PathIde
) {
490 // Patch LBAOffsetForBootSector
492 *(DWORD
*)&DiskPartition
[BOOT_SECTOR_LBA_OFFSET
] = InputDbrOffset
;
495 if (OutputInfo
->Type
!= PathFile
) {
498 // Use original partition table
500 if (!ReadFile (OutputHandle
, DiskPartitionBackup
, 0x200, &BytesReturn
, NULL
)) {
501 Status
= ErrorFileReadWrite
;
504 memcpy (DiskPartition
+ 0x1BE, DiskPartitionBackup
+ 0x1BE, 0x40);
505 SetFilePointer (OutputHandle
, 0, NULL
, FILE_BEGIN
);
511 // Write boot sector to taget disk/file
513 if (!WriteFile (OutputHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
)) {
514 Status
= ErrorFileReadWrite
;
519 if (InputHandle
!= INVALID_HANDLE_VALUE
) {
520 CloseHandle (InputHandle
);
522 if (OutputHandle
!= INVALID_HANDLE_VALUE
) {
523 CloseHandle (OutputHandle
);
537 Displays the standard utility information to SDTOUT
549 printf ("%s Version %d.%d %s\n", UTILITY_NAME
, UTILITY_MAJOR_VERSION
, UTILITY_MINOR_VERSION
, __BUILD_VERSION
);
557 printf ("Usage: GenBootSector [options] --cfg-file CFG_FILE\n\n\
558 Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.\n\n\
559 Utility to retrieve and update the boot sector or MBR.\n\n\
560 optional arguments:\n\
561 -h, --help Show this help message and exit\n\
562 --version Show program's version number and exit\n\
563 -d [DEBUG], --debug [DEBUG]\n\
564 Output DEBUG statements, where DEBUG_LEVEL is 0 (min)\n\
566 -v, --verbose Print informational statements\n\
567 -q, --quiet Returns the exit code, error messages will be\n\
569 -s, --silent Returns only the exit code; informational and error\n\
570 messages are not displayed\n\
571 -l, --list List disk drives\n\
572 -i INPUT_FILENAME, --input INPUT_FILENAME\n\
574 -o OUTPUT_FILENAME, --output OUTPUT_FILENAME\n\
576 -m, --mbr Also process the MBR\n\
577 --sfo Reserved for future use\n");
582 Get path information, including physical path for windows platform.
584 @param PathInfo Point to PATH_INFO structure.
586 @return whether path is valid.
593 DRIVE_INFO DriveInfo
;
595 CHAR DiskPathTemplate
[] = "\\\\.\\PHYSICALDRIVE%u";
596 CHAR FloppyPathTemplate
[] = "\\\\.\\%c:";
600 // If path is disk path
602 if (IsLetter(PathInfo
->Path
[0]) && (PathInfo
->Path
[1] == ':') && (PathInfo
->Path
[2] == '\0')) {
603 VolumeLetter
= PathInfo
->Path
[0];
604 if ((VolumeLetter
== 'A') || (VolumeLetter
== 'a') ||
605 (VolumeLetter
== 'B') || (VolumeLetter
== 'b')) {
606 PathInfo
->Type
= PathFloppy
;
607 sprintf (PathInfo
->PhysicalPath
, FloppyPathTemplate
, VolumeLetter
);
611 if (!GetDriveInfo(VolumeLetter
, &DriveInfo
)) {
612 fprintf (stderr
, "ERROR: GetDriveInfo - 0x%lx\n", GetLastError ());
616 if (!PathInfo
->Input
&& (DriveInfo
.DriveType
->Type
== DRIVE_FIXED
)) {
617 fprintf (stderr
, "ERROR: Could patch own IDE disk!\n");
621 sprintf(PathInfo
->PhysicalPath
, DiskPathTemplate
, DriveInfo
.DiskNumber
);
622 if (DriveInfo
.DriveType
->Type
== DRIVE_REMOVABLE
) {
623 PathInfo
->Type
= PathUsb
;
624 } else if (DriveInfo
.DriveType
->Type
== DRIVE_FIXED
) {
625 PathInfo
->Type
= PathIde
;
627 fprintf (stderr
, "ERROR, Invalid disk path - %s", PathInfo
->Path
);
635 // Check the path length
637 if (strlen (PathInfo
->Path
) >= (sizeof (PathInfo
->PhysicalPath
) / sizeof (PathInfo
->PhysicalPath
[0]))) {
638 fprintf (stderr
, "ERROR, Path is too long for - %s", PathInfo
->Path
);
642 PathInfo
->Type
= PathFile
;
643 if (PathInfo
->Input
) {
645 // If path is file path, check whether file is valid.
647 f
= fopen (LongFilePath (PathInfo
->Path
), "r");
649 fprintf (stderr
, "error E2003: File was not provided!\n");
654 PathInfo
->Type
= PathFile
;
656 PathInfo
->PhysicalPath
,
658 sizeof (PathInfo
->PhysicalPath
) / sizeof (PathInfo
->PhysicalPath
[0]) - 1
660 PathInfo
->PhysicalPath
[sizeof (PathInfo
->PhysicalPath
) / sizeof (PathInfo
->PhysicalPath
[0]) - 1] = 0;
675 EFI_STATUS EfiStatus
;
676 PATH_INFO InputPathInfo
= {0};
677 PATH_INFO OutputPathInfo
= {0};
680 SetUtilityName (UTILITY_NAME
);
694 // Parse command line
696 for (Index
= 0; Index
< argc
; Index
++) {
697 if ((stricmp (argv
[Index
], "-l") == 0) || (stricmp (argv
[Index
], "--list") == 0)) {
702 if ((stricmp (argv
[Index
], "-m") == 0) || (stricmp (argv
[Index
], "--mbr") == 0)) {
707 if ((stricmp (argv
[Index
], "-i") == 0) || (stricmp (argv
[Index
], "--input") == 0)) {
708 InputPathInfo
.Path
= argv
[Index
+ 1];
709 InputPathInfo
.Input
= TRUE
;
710 if (InputPathInfo
.Path
== NULL
) {
711 Error (NULL
, 0, 1003, "Invalid option value", "Input file name can't be NULL");
714 if (InputPathInfo
.Path
[0] == '-') {
715 Error (NULL
, 0, 1003, "Invalid option value", "Input file is missing");
722 if ((stricmp (argv
[Index
], "-o") == 0) || (stricmp (argv
[Index
], "--output") == 0)) {
723 OutputPathInfo
.Path
= argv
[Index
+ 1];
724 OutputPathInfo
.Input
= FALSE
;
725 if (OutputPathInfo
.Path
== NULL
) {
726 Error (NULL
, 0, 1003, "Invalid option value", "Output file name can't be NULL");
729 if (OutputPathInfo
.Path
[0] == '-') {
730 Error (NULL
, 0, 1003, "Invalid option value", "Output file is missing");
737 if ((stricmp (argv
[Index
], "-h") == 0) || (stricmp (argv
[Index
], "--help") == 0)) {
742 if (stricmp (argv
[Index
], "--version") == 0) {
747 if ((stricmp (argv
[Index
], "-v") == 0) || (stricmp (argv
[Index
], "--verbose") == 0)) {
751 if ((stricmp (argv
[Index
], "-q") == 0) || (stricmp (argv
[Index
], "--quiet") == 0)) {
755 if ((stricmp (argv
[Index
], "-d") == 0) || (stricmp (argv
[Index
], "--debug") == 0)) {
756 EfiStatus
= AsciiStringToUint64 (argv
[Index
+ 1], FALSE
, &LogLevel
);
757 if (EFI_ERROR (EfiStatus
)) {
758 Error (NULL
, 0, 1003, "Invalid option value", "%s = %s", argv
[Index
], argv
[Index
+ 1]);
762 Error (NULL
, 0, 1003, "Invalid option value", "Debug Level range is 0-9, currnt input level is %d", (int) LogLevel
);
765 SetPrintLevel (LogLevel
);
766 DebugMsg (NULL
, 0, 9, "Debug Mode Set", "Debug Output Mode Level %s is set!", argv
[Index
+ 1]);
772 // Don't recognize the parameter.
774 Error (NULL
, 0, 1000, "Unknown option", "%s", argv
[Index
]);
778 if (InputPathInfo
.Path
== NULL
) {
779 Error (NULL
, 0, 1001, "Missing options", "Input file is missing");
783 if (OutputPathInfo
.Path
== NULL
) {
784 Error (NULL
, 0, 1001, "Missing options", "Output file is missing");
788 if (GetPathInfo(&InputPathInfo
) != ErrorSuccess
) {
789 Error (NULL
, 0, 1003, "Invalid option value", "Input file can't be found.");
793 if (GetPathInfo(&OutputPathInfo
) != ErrorSuccess
) {
794 Error (NULL
, 0, 1003, "Invalid option value", "Output file can't be found.");
799 // Process DBR (Patch or Read)
801 Status
= ProcessBsOrMbr (&InputPathInfo
, &OutputPathInfo
, ProcessMbr
);
803 if (Status
== ErrorSuccess
) {
806 "%s %s: successful!\n",
807 (OutputPathInfo
.Type
!= PathFile
) ? "Write" : "Read",
808 ProcessMbr
? "MBR" : "DBR"
814 "%s: %s %s: failed - %s (LastError: 0x%lx)!\n",
815 (Status
== ErrorNoMbr
) ? "WARNING" : "ERROR",
816 (OutputPathInfo
.Type
!= PathFile
) ? "Write" : "Read",
817 ProcessMbr
? "MBR" : "DBR",
818 ErrorStatusDesc
[Status
],