3 Copyright 2006 - 2008, Intel Corporation
4 All rights reserved. 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>
32 #define UTILITY_NAME "GenBootSector"
35 // Utility version information
37 #define UTILITY_MAJOR_VERSION 0
38 #define UTILITY_MINOR_VERSION 1
41 #define PARTITION_TABLE_OFFSET 0x1BE
43 #define SIZE_OF_PARTITION_ENTRY 0x10
45 #define PARTITION_ENTRY_STARTLBA_OFFSET 8
47 #define PARTITION_ENTRY_NUM 4
59 PatchTypeFileImage
// input and output are all file image, patching action is same as PatchTypeFloppy
79 CHAR
*ErrorStatusDesc
[] = {
81 "Failed to create files",
82 "Failed to read/write files",
84 "Failed to detect Fat type",
88 typedef struct _DRIVE_TYPE_DESC
{
93 #define DRIVE_TYPE_ITEM(x) {x, #x}
94 DRIVE_TYPE_DESC DriveTypeDesc
[] = {
95 DRIVE_TYPE_ITEM (DRIVE_UNKNOWN
),
96 DRIVE_TYPE_ITEM (DRIVE_NO_ROOT_DIR
),
97 DRIVE_TYPE_ITEM (DRIVE_REMOVABLE
),
98 DRIVE_TYPE_ITEM (DRIVE_FIXED
),
99 DRIVE_TYPE_ITEM (DRIVE_REMOTE
),
100 DRIVE_TYPE_ITEM (DRIVE_CDROM
),
101 DRIVE_TYPE_ITEM (DRIVE_RAMDISK
),
105 typedef struct _DRIVE_INFO
{
107 DRIVE_TYPE_DESC
*DriveType
;
111 typedef struct _PATH_INFO
{
113 CHAR PhysicalPath
[260];
118 #define BOOT_SECTOR_LBA_OFFSET 0x1FA
120 #define IsLetter(x) (((x) >= 'a' && (x) <= 'z') || ((x) >= 'A' && (x) <= 'Z'))
125 DRIVE_INFO
*DriveInfo
129 Get drive information including disk number and drive type,
130 where disknumber is useful for reading/writing disk raw data.
131 NOTE: Floppy disk doesn't have disk number but it doesn't matter because
132 we can reading/writing floppy disk without disk number.
135 VolumeLetter : volume letter, e.g.: C for C:, A for A:
136 DriveInfo : pointer to DRIVE_INFO structure receiving drive information.
144 STORAGE_DEVICE_NUMBER StorageDeviceNumber
;
150 CHAR RootPath
[] = "X:\\"; // "X:\" -> for GetDriveType
151 CHAR VolumeAccessPath
[] = "\\\\.\\X:"; // "\\.\X:" -> to open the volume
153 RootPath
[0] = VolumeAccessPath
[4] = VolumeLetter
;
154 DriveType
= GetDriveType(RootPath
);
155 if (DriveType
!= DRIVE_REMOVABLE
&& DriveType
!= DRIVE_FIXED
) {
159 DriveInfo
->VolumeLetter
= VolumeLetter
;
160 VolumeHandle
= CreateFile (
163 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
169 if (VolumeHandle
== INVALID_HANDLE_VALUE
) {
172 "error E0005: CreateFile failed: Volume = %s, LastError = 0x%x\n",
180 // Get Disk Number. It should fail when operating on floppy. That's ok
181 // because Disk Number is only needed when operating on Hard or USB disk.
183 // To direct write to disk:
184 // for USB and HD: use path = \\.\PHYSICALDRIVEx, where x is Disk Number
185 // for floppy: use path = \\.\X:, where X can be A or B
187 Success
= DeviceIoControl(
189 IOCTL_STORAGE_GET_DEVICE_NUMBER
,
192 &StorageDeviceNumber
,
193 sizeof(StorageDeviceNumber
),
198 // DeviceIoControl should fail if Volume is floppy or network drive.
201 DriveInfo
->DiskNumber
= (UINT
) -1;
202 } else if (StorageDeviceNumber
.DeviceType
!= FILE_DEVICE_DISK
) {
204 // Only care about the disk.
208 DriveInfo
->DiskNumber
= StorageDeviceNumber
.DeviceNumber
;
210 CloseHandle(VolumeHandle
);
213 // Fill in the type string
215 DriveInfo
->DriveType
= NULL
;
216 for (Index
= 0; DriveTypeDesc
[Index
].Description
!= NULL
; Index
++) {
217 if (DriveType
== DriveTypeDesc
[Index
].Type
) {
218 DriveInfo
->DriveType
= &DriveTypeDesc
[Index
];
223 if (DriveInfo
->DriveType
== NULL
) {
225 // Should have a type.
227 fprintf (stderr
, "error E3005: Fatal Error!!!\n");
239 List every drive in current system and their information.
244 DRIVE_INFO DriveInfo
;
246 UINT Mask
= GetLogicalDrives();
248 for (Index
= 0; Index
< MAX_DRIVE
; Index
++) {
249 if (((Mask
>> Index
) & 0x1) == 1) {
250 if (GetDriveInfo ('A' + (CHAR
) Index
, &DriveInfo
)) {
252 // Floppy will occupy 'A' and 'B'
256 DriveInfo
.VolumeLetter
,
257 DriveInfo
.DriveType
->Description
262 "%c: - DiskNum: %d, Type: %s\n",
263 DriveInfo
.VolumeLetter
,
264 DriveInfo
.DiskNumber
,
265 DriveInfo
.DriveType
->Description
275 GetBootSectorOffset (
281 Get the offset of boot sector.
282 For non-MBR disk, offset is just 0
283 for disk with MBR, offset needs to be caculated by parsing MBR
285 NOTE: if no one is active, we will patch MBR to select first partition as active.
288 DiskHandle : HANDLE of disk
289 PathInfo : PATH_INFO structure.
290 WriteToDisk : TRUE indicates writing
294 o.w. : Offset to boot sector
297 BYTE DiskPartition
[0x200];
306 SetFilePointer(DiskHandle
, 0, NULL
, FILE_BEGIN
);
307 if (!ReadFile (DiskHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
)) {
312 // Check Signature, Jmp, and Boot Indicator.
313 // if all pass, we assume MBR found.
316 // Check Signature: 55AA
317 if ((DiskPartition
[0x1FE] == 0x55) && (DiskPartition
[0x1FF] == 0xAA)) {
318 // Check Jmp: (EB ?? 90) or (E9 ?? ??)
319 if (((DiskPartition
[0] != 0xEB) || (DiskPartition
[2] != 0x90)) &&
320 (DiskPartition
[0] != 0xE9)) {
321 // Check Boot Indicator: 0x00 or 0x80
322 // Boot Indicator is the first byte of Partition Entry
324 for (Index
= 0; Index
< PARTITION_ENTRY_NUM
; ++Index
) {
325 if ((DiskPartition
[PARTITION_TABLE_OFFSET
+ Index
* SIZE_OF_PARTITION_ENTRY
] & 0x7F) != 0) {
337 for (Index
= 0; Index
< PARTITION_ENTRY_NUM
; Index
++) {
339 // Found Boot Indicator.
341 if (DiskPartition
[PARTITION_TABLE_OFFSET
+ (Index
* SIZE_OF_PARTITION_ENTRY
)] == 0x80) {
342 DbrOffset
= *(DWORD
*)&DiskPartition
[PARTITION_TABLE_OFFSET
+ (Index
* SIZE_OF_PARTITION_ENTRY
) + PARTITION_ENTRY_STARTLBA_OFFSET
];
347 // If no boot indicator, we manually select 1st partition, and patch MBR.
349 if (Index
== PARTITION_ENTRY_NUM
) {
350 DbrOffset
= *(DWORD
*)&DiskPartition
[PARTITION_TABLE_OFFSET
+ PARTITION_ENTRY_STARTLBA_OFFSET
];
351 if (!PathInfo
->Input
&& (PathInfo
->Type
== PathUsb
)) {
352 SetFilePointer(DiskHandle
, 0, NULL
, FILE_BEGIN
);
353 DiskPartition
[PARTITION_TABLE_OFFSET
] = 0x80;
354 WriteFile (DiskHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
);
363 * Get window file handle for input/ouput disk/file.
369 * @return ERROR_STATUS
381 OpenFlag
= OPEN_ALWAYS
;
382 if (PathInfo
->Input
|| PathInfo
->Type
!= PathFile
) {
383 OpenFlag
= OPEN_EXISTING
;
386 *FileHandle
= CreateFile(
387 PathInfo
->PhysicalPath
,
388 GENERIC_READ
| GENERIC_WRITE
,
392 FILE_ATTRIBUTE_NORMAL
,
395 if (*FileHandle
== INVALID_HANDLE_VALUE
) {
396 return ErrorFileCreate
;
399 if ((PathInfo
->Type
== PathIde
) || (PathInfo
->Type
== PathUsb
)){
400 *DbrOffset
= GetBootSectorOffset (*FileHandle
, PathInfo
);
403 // 1. Process boot sector, set file pointer to the beginning of boot sector
405 SetFilePointer (*FileHandle
, *DbrOffset
* 0x200, NULL
, FILE_BEGIN
);
406 } else if(*DbrOffset
== 0) {
408 // If user want to process Mbr, but no Mbr exists, simply return FALSE
413 // 2. Process MBR, set file pointer to 0
415 SetFilePointer (*FileHandle
, 0, NULL
, FILE_BEGIN
);
423 Writing or reading boot sector or MBR according to the argument.
425 @param InputInfo PATH_INFO instance for input path
426 @param OutputInfo PATH_INFO instance for output path
427 @param ProcessMbr TRUE is to process MBR, otherwise, processing boot sector
433 PATH_INFO
*InputInfo
,
434 PATH_INFO
*OutputInfo
,
438 BYTE DiskPartition
[0x200] = {0};
439 BYTE DiskPartitionBackup
[0x200] = {0};
447 DWORD InputDbrOffset
;
448 DWORD OutputDbrOffset
;
451 // Create file Handle and move file Pointer is pointed to beginning of Mbr or Dbr
453 Status
= GetFileHandle(InputInfo
, ProcessMbr
, &InputHandle
, &InputDbrOffset
);
454 if (Status
!= ErrorSuccess
) {
459 // Create file Handle and move file Pointer is pointed to beginning of Mbr or Dbr
461 Status
= GetFileHandle(OutputInfo
, ProcessMbr
, &OutputHandle
, &OutputDbrOffset
);
462 if (Status
!= ErrorSuccess
) {
467 // Read boot sector from source disk/file
469 if (!ReadFile (InputHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
)) {
470 return ErrorFileReadWrite
;
473 if (InputInfo
->Type
== PathUsb
) {
474 // Manually set BS_DrvNum to 0x80 as window's format.exe has a bug which will clear this field discarding USB disk's MBR.
475 // offset of BS_DrvNum is 0x24 for FAT12/16
478 DrvNumOffset
= GetDrvNumOffset (DiskPartition
);
479 if (DrvNumOffset
== -1) {
483 // Some legacy BIOS require 0x80 discarding MBR.
484 // Question left here: is it needed to check Mbr before set 0x80?
486 DiskPartition
[DrvNumOffset
] = ((InputDbrOffset
> 0) ? 0x80 : 0);
489 if (InputInfo
->Type
== PathIde
) {
491 // Patch LBAOffsetForBootSector
493 *(DWORD
*)&DiskPartition
[BOOT_SECTOR_LBA_OFFSET
] = InputDbrOffset
;
496 if (OutputInfo
->Type
!= PathFile
) {
499 // Use original partition table
501 if (!ReadFile (OutputHandle
, DiskPartitionBackup
, 0x200, &BytesReturn
, NULL
)) {
502 return 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 return ErrorFileReadWrite
;
517 CloseHandle (InputHandle
);
518 CloseHandle (OutputHandle
);
531 Displays the standard utility information to SDTOUT
543 printf ("%s v%d.%d -Utility to retrieve and update the boot sector or MBR.\n", UTILITY_NAME
, UTILITY_MAJOR_VERSION
, UTILITY_MINOR_VERSION
);
544 printf ("Copyright (c) 2009 Intel Corporation. All rights reserved.\n");
553 printf ("\nUsage: \n\
555 [-l, --list list disks]\n\
556 [-i, --input Filename]\n\
557 [-o, --output Filename]\n\
558 [-m, --mbr process the MBR also]\n\
561 [-q, --quiet disable all messages except fatal errors]\n\
568 Get path information, including physical path for windows platform.
570 @param PathInfo Point to PATH_INFO structure.
572 @return whether path is valid.
579 DRIVE_INFO DriveInfo
;
581 CHAR DiskPathTemplate
[] = "\\\\.\\PHYSICALDRIVE%u";
582 CHAR FloppyPathTemplate
[] = "\\\\.\\%c:";
586 // If path is disk path
588 if (IsLetter(PathInfo
->Path
[0]) && (PathInfo
->Path
[1] == ':') && (PathInfo
->Path
[2] == '\0')) {
589 VolumeLetter
= PathInfo
->Path
[0];
590 if ((VolumeLetter
== 'A') || (VolumeLetter
== 'a') ||
591 (VolumeLetter
== 'B') || (VolumeLetter
== 'b')) {
592 PathInfo
->Type
= PathFloppy
;
593 sprintf (PathInfo
->PhysicalPath
, FloppyPathTemplate
, VolumeLetter
);
597 if (!GetDriveInfo(VolumeLetter
, &DriveInfo
)) {
598 fprintf (stderr
, "ERROR: GetDriveInfo - 0x%x\n", GetLastError ());
602 if (!PathInfo
->Input
&& (DriveInfo
.DriveType
->Type
== DRIVE_FIXED
)) {
603 fprintf (stderr
, "ERROR: Could patch own IDE disk!\n");
607 sprintf(PathInfo
->PhysicalPath
, DiskPathTemplate
, DriveInfo
.DiskNumber
);
608 if (DriveInfo
.DriveType
->Type
== DRIVE_REMOVABLE
) {
609 PathInfo
->Type
= PathUsb
;
610 } else if (DriveInfo
.DriveType
->Type
== DRIVE_FIXED
) {
611 PathInfo
->Type
= PathIde
;
613 fprintf (stderr
, "ERROR, Invalid disk path - %s", PathInfo
->Path
);
620 PathInfo
->Type
= PathFile
;
621 if (PathInfo
->Input
) {
623 // If path is file path, check whether file is valid.
625 f
= fopen (PathInfo
->Path
, "r");
627 fprintf (stderr
, "error E2003: File was not provided!\n");
631 PathInfo
->Type
= PathFile
;
632 strcpy(PathInfo
->PhysicalPath
, PathInfo
->Path
);
647 EFI_STATUS EfiStatus
;
648 PATH_INFO InputPathInfo
= {0};
649 PATH_INFO OutputPathInfo
= {0};
652 SetUtilityName (UTILITY_NAME
);
666 // Parse command line
668 for (Index
= 0; Index
< argc
; Index
++) {
669 if ((stricmp (argv
[Index
], "-l") == 0) || (stricmp (argv
[Index
], "--list") == 0)) {
674 if ((stricmp (argv
[Index
], "-m") == 0) || (stricmp (argv
[Index
], "--mbr") == 0)) {
679 if ((stricmp (argv
[Index
], "-i") == 0) || (stricmp (argv
[Index
], "--input") == 0)) {
680 InputPathInfo
.Path
= argv
[Index
+ 1];
681 InputPathInfo
.Input
= TRUE
;
682 if (InputPathInfo
.Path
== NULL
) {
683 Error (NULL
, 0, 1003, "Invalid option value", "Input file name can't be NULL");
686 if (InputPathInfo
.Path
[0] == '-') {
687 Error (NULL
, 0, 1003, "Invalid option value", "Input file is missing");
694 if ((stricmp (argv
[Index
], "-o") == 0) || (stricmp (argv
[Index
], "--output") == 0)) {
695 OutputPathInfo
.Path
= argv
[Index
+ 1];
696 OutputPathInfo
.Input
= FALSE
;
697 if (OutputPathInfo
.Path
== NULL
) {
698 Error (NULL
, 0, 1003, "Invalid option value", "Output file name can't be NULL");
701 if (OutputPathInfo
.Path
[0] == '-') {
702 Error (NULL
, 0, 1003, "Invalid option value", "Output file is missing");
709 if ((stricmp (argv
[Index
], "-h") == 0) || (stricmp (argv
[Index
], "--help") == 0)) {
714 if (stricmp (argv
[Index
], "--version") == 0) {
719 if ((stricmp (argv
[Index
], "-v") == 0) || (stricmp (argv
[Index
], "--verbose") == 0)) {
723 if ((stricmp (argv
[Index
], "-q") == 0) || (stricmp (argv
[Index
], "--quiet") == 0)) {
727 if ((stricmp (argv
[Index
], "-d") == 0) || (stricmp (argv
[Index
], "--debug") == 0)) {
728 EfiStatus
= AsciiStringToUint64 (argv
[Index
+ 1], FALSE
, &LogLevel
);
729 if (EFI_ERROR (EfiStatus
)) {
730 Error (NULL
, 0, 1003, "Invalid option value", "%s = %s", argv
[Index
], argv
[Index
+ 1]);
734 Error (NULL
, 0, 1003, "Invalid option value", "Debug Level range is 0-9, currnt input level is %d", LogLevel
);
737 SetPrintLevel (LogLevel
);
738 DebugMsg (NULL
, 0, 9, "Debug Mode Set", "Debug Output Mode Level %s is set!", argv
[Index
+ 1]);
744 // Don't recognize the parameter.
746 Error (NULL
, 0, 1000, "Unknown option", "%s", argv
[Index
]);
750 if (InputPathInfo
.Path
== NULL
) {
751 Error (NULL
, 0, 1001, "Missing options", "Input file is missing");
755 if (OutputPathInfo
.Path
== NULL
) {
756 Error (NULL
, 0, 1001, "Missing options", "Output file is missing");
760 if (GetPathInfo(&InputPathInfo
) != ErrorSuccess
) {
761 Error (NULL
, 0, 1003, "Invalid option value", "Input file can't be found.");
765 if (GetPathInfo(&OutputPathInfo
) != ErrorSuccess
) {
766 Error (NULL
, 0, 1003, "Invalid option value", "Output file can't be found.");
771 // Process DBR (Patch or Read)
773 Status
= ProcessBsOrMbr (&InputPathInfo
, &OutputPathInfo
, ProcessMbr
);
775 if (Status
== ErrorSuccess
) {
778 "%s %s: successful!\n",
779 (OutputPathInfo
.Type
!= PathFile
) ? "Write" : "Read",
780 ProcessMbr
? "MBR" : "DBR"
786 "%s: %s %s: failed - %s (LastError: 0x%x)!\n",
787 (Status
== ErrorNoMbr
) ? "WARNING" : "ERROR",
788 (OutputPathInfo
.Type
!= PathFile
) ? "Write" : "Read",
789 ProcessMbr
? "MBR" : "DBR",
790 ErrorStatusDesc
[Status
],