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 - 2016, 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
);
634 PathInfo
->Type
= PathFile
;
635 if (PathInfo
->Input
) {
637 // If path is file path, check whether file is valid.
639 f
= fopen (LongFilePath (PathInfo
->Path
), "r");
641 fprintf (stderr
, "error E2003: File was not provided!\n");
646 PathInfo
->Type
= PathFile
;
647 strcpy(PathInfo
->PhysicalPath
, PathInfo
->Path
);
662 EFI_STATUS EfiStatus
;
663 PATH_INFO InputPathInfo
= {0};
664 PATH_INFO OutputPathInfo
= {0};
667 SetUtilityName (UTILITY_NAME
);
681 // Parse command line
683 for (Index
= 0; Index
< argc
; Index
++) {
684 if ((stricmp (argv
[Index
], "-l") == 0) || (stricmp (argv
[Index
], "--list") == 0)) {
689 if ((stricmp (argv
[Index
], "-m") == 0) || (stricmp (argv
[Index
], "--mbr") == 0)) {
694 if ((stricmp (argv
[Index
], "-i") == 0) || (stricmp (argv
[Index
], "--input") == 0)) {
695 InputPathInfo
.Path
= argv
[Index
+ 1];
696 InputPathInfo
.Input
= TRUE
;
697 if (InputPathInfo
.Path
== NULL
) {
698 Error (NULL
, 0, 1003, "Invalid option value", "Input file name can't be NULL");
701 if (InputPathInfo
.Path
[0] == '-') {
702 Error (NULL
, 0, 1003, "Invalid option value", "Input file is missing");
709 if ((stricmp (argv
[Index
], "-o") == 0) || (stricmp (argv
[Index
], "--output") == 0)) {
710 OutputPathInfo
.Path
= argv
[Index
+ 1];
711 OutputPathInfo
.Input
= FALSE
;
712 if (OutputPathInfo
.Path
== NULL
) {
713 Error (NULL
, 0, 1003, "Invalid option value", "Output file name can't be NULL");
716 if (OutputPathInfo
.Path
[0] == '-') {
717 Error (NULL
, 0, 1003, "Invalid option value", "Output file is missing");
724 if ((stricmp (argv
[Index
], "-h") == 0) || (stricmp (argv
[Index
], "--help") == 0)) {
729 if (stricmp (argv
[Index
], "--version") == 0) {
734 if ((stricmp (argv
[Index
], "-v") == 0) || (stricmp (argv
[Index
], "--verbose") == 0)) {
738 if ((stricmp (argv
[Index
], "-q") == 0) || (stricmp (argv
[Index
], "--quiet") == 0)) {
742 if ((stricmp (argv
[Index
], "-d") == 0) || (stricmp (argv
[Index
], "--debug") == 0)) {
743 EfiStatus
= AsciiStringToUint64 (argv
[Index
+ 1], FALSE
, &LogLevel
);
744 if (EFI_ERROR (EfiStatus
)) {
745 Error (NULL
, 0, 1003, "Invalid option value", "%s = %s", argv
[Index
], argv
[Index
+ 1]);
749 Error (NULL
, 0, 1003, "Invalid option value", "Debug Level range is 0-9, currnt input level is %d", (int) LogLevel
);
752 SetPrintLevel (LogLevel
);
753 DebugMsg (NULL
, 0, 9, "Debug Mode Set", "Debug Output Mode Level %s is set!", argv
[Index
+ 1]);
759 // Don't recognize the parameter.
761 Error (NULL
, 0, 1000, "Unknown option", "%s", argv
[Index
]);
765 if (InputPathInfo
.Path
== NULL
) {
766 Error (NULL
, 0, 1001, "Missing options", "Input file is missing");
770 if (OutputPathInfo
.Path
== NULL
) {
771 Error (NULL
, 0, 1001, "Missing options", "Output file is missing");
775 if (GetPathInfo(&InputPathInfo
) != ErrorSuccess
) {
776 Error (NULL
, 0, 1003, "Invalid option value", "Input file can't be found.");
780 if (GetPathInfo(&OutputPathInfo
) != ErrorSuccess
) {
781 Error (NULL
, 0, 1003, "Invalid option value", "Output file can't be found.");
786 // Process DBR (Patch or Read)
788 Status
= ProcessBsOrMbr (&InputPathInfo
, &OutputPathInfo
, ProcessMbr
);
790 if (Status
== ErrorSuccess
) {
793 "%s %s: successful!\n",
794 (OutputPathInfo
.Type
!= PathFile
) ? "Write" : "Read",
795 ProcessMbr
? "MBR" : "DBR"
801 "%s: %s %s: failed - %s (LastError: 0x%lx)!\n",
802 (Status
== ErrorNoMbr
) ? "WARNING" : "ERROR",
803 (OutputPathInfo
.Type
!= PathFile
) ? "Write" : "Read",
804 ProcessMbr
? "MBR" : "DBR",
805 ErrorStatusDesc
[Status
],