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>
30 #include "EfiUtilityMsgs.h"
35 #define UTILITY_NAME "GenBootSector"
38 // Utility version information
40 #define UTILITY_MAJOR_VERSION 0
41 #define UTILITY_MINOR_VERSION 1
44 #define PARTITION_TABLE_OFFSET 0x1BE
46 #define SIZE_OF_PARTITION_ENTRY 0x10
48 #define PARTITION_ENTRY_STARTLBA_OFFSET 8
50 #define PARTITION_ENTRY_NUM 4
62 PatchTypeFileImage
// input and output are all file image, patching action is same as PatchTypeFloppy
82 CHAR
*ErrorStatusDesc
[] = {
84 "Failed to create files",
85 "Failed to read/write files",
87 "Failed to detect Fat type",
91 typedef struct _DRIVE_TYPE_DESC
{
96 #define DRIVE_TYPE_ITEM(x) {x, #x}
97 DRIVE_TYPE_DESC DriveTypeDesc
[] = {
98 DRIVE_TYPE_ITEM (DRIVE_UNKNOWN
),
99 DRIVE_TYPE_ITEM (DRIVE_NO_ROOT_DIR
),
100 DRIVE_TYPE_ITEM (DRIVE_REMOVABLE
),
101 DRIVE_TYPE_ITEM (DRIVE_FIXED
),
102 DRIVE_TYPE_ITEM (DRIVE_REMOTE
),
103 DRIVE_TYPE_ITEM (DRIVE_CDROM
),
104 DRIVE_TYPE_ITEM (DRIVE_RAMDISK
),
108 typedef struct _DRIVE_INFO
{
110 DRIVE_TYPE_DESC
*DriveType
;
114 typedef struct _PATH_INFO
{
116 CHAR PhysicalPath
[260];
121 #define BOOT_SECTOR_LBA_OFFSET 0x1FA
123 #define IsLetter(x) (((x) >= 'a' && (x) <= 'z') || ((x) >= 'A' && (x) <= 'Z'))
128 DRIVE_INFO
*DriveInfo
132 Get drive information including disk number and drive type,
133 where disknumber is useful for reading/writing disk raw data.
134 NOTE: Floppy disk doesn't have disk number but it doesn't matter because
135 we can reading/writing floppy disk without disk number.
138 VolumeLetter : volume letter, e.g.: C for C:, A for A:
139 DriveInfo : pointer to DRIVE_INFO structure receiving drive information.
147 STORAGE_DEVICE_NUMBER StorageDeviceNumber
;
153 CHAR RootPath
[] = "X:\\"; // "X:\" -> for GetDriveType
154 CHAR VolumeAccessPath
[] = "\\\\.\\X:"; // "\\.\X:" -> to open the volume
156 RootPath
[0] = VolumeAccessPath
[4] = VolumeLetter
;
157 DriveType
= GetDriveType(RootPath
);
158 if (DriveType
!= DRIVE_REMOVABLE
&& DriveType
!= DRIVE_FIXED
) {
162 DriveInfo
->VolumeLetter
= VolumeLetter
;
163 VolumeHandle
= CreateFile (
166 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
172 if (VolumeHandle
== INVALID_HANDLE_VALUE
) {
175 "error E0005: CreateFile failed: Volume = %s, LastError = 0x%x\n",
183 // Get Disk Number. It should fail when operating on floppy. That's ok
184 // because Disk Number is only needed when operating on Hard or USB disk.
186 // To direct write to disk:
187 // for USB and HD: use path = \\.\PHYSICALDRIVEx, where x is Disk Number
188 // for floppy: use path = \\.\X:, where X can be A or B
190 Success
= DeviceIoControl(
192 IOCTL_STORAGE_GET_DEVICE_NUMBER
,
195 &StorageDeviceNumber
,
196 sizeof(StorageDeviceNumber
),
201 // DeviceIoControl should fail if Volume is floppy or network drive.
204 DriveInfo
->DiskNumber
= (UINT
) -1;
205 } else if (StorageDeviceNumber
.DeviceType
!= FILE_DEVICE_DISK
) {
207 // Only care about the disk.
211 DriveInfo
->DiskNumber
= StorageDeviceNumber
.DeviceNumber
;
213 CloseHandle(VolumeHandle
);
216 // Fill in the type string
218 DriveInfo
->DriveType
= NULL
;
219 for (Index
= 0; DriveTypeDesc
[Index
].Description
!= NULL
; Index
++) {
220 if (DriveType
== DriveTypeDesc
[Index
].Type
) {
221 DriveInfo
->DriveType
= &DriveTypeDesc
[Index
];
226 if (DriveInfo
->DriveType
== NULL
) {
228 // Should have a type.
230 fprintf (stderr
, "error E3005: Fatal Error!!!\n");
242 List every drive in current system and their information.
247 DRIVE_INFO DriveInfo
;
249 UINT Mask
= GetLogicalDrives();
251 for (Index
= 0; Index
< MAX_DRIVE
; Index
++) {
252 if (((Mask
>> Index
) & 0x1) == 1) {
253 if (GetDriveInfo ('A' + (CHAR
) Index
, &DriveInfo
)) {
255 // Floppy will occupy 'A' and 'B'
259 DriveInfo
.VolumeLetter
,
260 DriveInfo
.DriveType
->Description
265 "%c: - DiskNum: %u, Type: %s\n",
266 DriveInfo
.VolumeLetter
,
267 (unsigned) DriveInfo
.DiskNumber
,
268 DriveInfo
.DriveType
->Description
278 GetBootSectorOffset (
284 Get the offset of boot sector.
285 For non-MBR disk, offset is just 0
286 for disk with MBR, offset needs to be caculated by parsing MBR
288 NOTE: if no one is active, we will patch MBR to select first partition as active.
291 DiskHandle : HANDLE of disk
292 PathInfo : PATH_INFO structure.
293 WriteToDisk : TRUE indicates writing
297 o.w. : Offset to boot sector
300 BYTE DiskPartition
[0x200];
309 SetFilePointer(DiskHandle
, 0, NULL
, FILE_BEGIN
);
310 if (!ReadFile (DiskHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
)) {
315 // Check Signature, Jmp, and Boot Indicator.
316 // if all pass, we assume MBR found.
319 // Check Signature: 55AA
320 if ((DiskPartition
[0x1FE] == 0x55) && (DiskPartition
[0x1FF] == 0xAA)) {
321 // Check Jmp: (EB ?? 90) or (E9 ?? ??)
322 if (((DiskPartition
[0] != 0xEB) || (DiskPartition
[2] != 0x90)) &&
323 (DiskPartition
[0] != 0xE9)) {
324 // Check Boot Indicator: 0x00 or 0x80
325 // Boot Indicator is the first byte of Partition Entry
327 for (Index
= 0; Index
< PARTITION_ENTRY_NUM
; ++Index
) {
328 if ((DiskPartition
[PARTITION_TABLE_OFFSET
+ Index
* SIZE_OF_PARTITION_ENTRY
] & 0x7F) != 0) {
340 for (Index
= 0; Index
< PARTITION_ENTRY_NUM
; Index
++) {
342 // Found Boot Indicator.
344 if (DiskPartition
[PARTITION_TABLE_OFFSET
+ (Index
* SIZE_OF_PARTITION_ENTRY
)] == 0x80) {
345 DbrOffset
= *(DWORD
*)&DiskPartition
[PARTITION_TABLE_OFFSET
+ (Index
* SIZE_OF_PARTITION_ENTRY
) + PARTITION_ENTRY_STARTLBA_OFFSET
];
350 // If no boot indicator, we manually select 1st partition, and patch MBR.
352 if (Index
== PARTITION_ENTRY_NUM
) {
353 DbrOffset
= *(DWORD
*)&DiskPartition
[PARTITION_TABLE_OFFSET
+ PARTITION_ENTRY_STARTLBA_OFFSET
];
354 if (!PathInfo
->Input
&& (PathInfo
->Type
== PathUsb
)) {
355 SetFilePointer(DiskHandle
, 0, NULL
, FILE_BEGIN
);
356 DiskPartition
[PARTITION_TABLE_OFFSET
] = 0x80;
357 WriteFile (DiskHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
);
366 * Get window file handle for input/ouput disk/file.
372 * @return ERROR_STATUS
384 OpenFlag
= OPEN_ALWAYS
;
385 if (PathInfo
->Input
|| PathInfo
->Type
!= PathFile
) {
386 OpenFlag
= OPEN_EXISTING
;
389 *FileHandle
= CreateFile(
390 PathInfo
->PhysicalPath
,
391 GENERIC_READ
| GENERIC_WRITE
,
395 FILE_ATTRIBUTE_NORMAL
,
398 if (*FileHandle
== INVALID_HANDLE_VALUE
) {
399 return ErrorFileCreate
;
402 if ((PathInfo
->Type
== PathIde
) || (PathInfo
->Type
== PathUsb
)){
403 *DbrOffset
= GetBootSectorOffset (*FileHandle
, PathInfo
);
406 // 1. Process boot sector, set file pointer to the beginning of boot sector
408 SetFilePointer (*FileHandle
, *DbrOffset
* 0x200, NULL
, FILE_BEGIN
);
409 } else if(*DbrOffset
== 0) {
411 // If user want to process Mbr, but no Mbr exists, simply return FALSE
416 // 2. Process MBR, set file pointer to 0
418 SetFilePointer (*FileHandle
, 0, NULL
, FILE_BEGIN
);
426 Writing or reading boot sector or MBR according to the argument.
428 @param InputInfo PATH_INFO instance for input path
429 @param OutputInfo PATH_INFO instance for output path
430 @param ProcessMbr TRUE is to process MBR, otherwise, processing boot sector
436 PATH_INFO
*InputInfo
,
437 PATH_INFO
*OutputInfo
,
441 BYTE DiskPartition
[0x200] = {0};
442 BYTE DiskPartitionBackup
[0x200] = {0};
448 DWORD InputDbrOffset
;
449 DWORD OutputDbrOffset
;
452 // Create file Handle and move file Pointer is pointed to beginning of Mbr or Dbr
454 Status
= GetFileHandle(InputInfo
, ProcessMbr
, &InputHandle
, &InputDbrOffset
);
455 if (Status
!= ErrorSuccess
) {
460 // Create file Handle and move file Pointer is pointed to beginning of Mbr or Dbr
462 Status
= GetFileHandle(OutputInfo
, ProcessMbr
, &OutputHandle
, &OutputDbrOffset
);
463 if (Status
!= ErrorSuccess
) {
468 // Read boot sector from source disk/file
470 if (!ReadFile (InputHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
)) {
471 return ErrorFileReadWrite
;
474 if (InputInfo
->Type
== PathUsb
) {
475 // Manually set BS_DrvNum to 0x80 as window's format.exe has a bug which will clear this field discarding USB disk's MBR.
476 // offset of BS_DrvNum is 0x24 for FAT12/16
479 DrvNumOffset
= GetDrvNumOffset (DiskPartition
);
480 if (DrvNumOffset
== -1) {
484 // Some legacy BIOS require 0x80 discarding MBR.
485 // Question left here: is it needed to check Mbr before set 0x80?
487 DiskPartition
[DrvNumOffset
] = ((InputDbrOffset
> 0) ? 0x80 : 0);
490 if (InputInfo
->Type
== PathIde
) {
492 // Patch LBAOffsetForBootSector
494 *(DWORD
*)&DiskPartition
[BOOT_SECTOR_LBA_OFFSET
] = InputDbrOffset
;
497 if (OutputInfo
->Type
!= PathFile
) {
500 // Use original partition table
502 if (!ReadFile (OutputHandle
, DiskPartitionBackup
, 0x200, &BytesReturn
, NULL
)) {
503 return ErrorFileReadWrite
;
505 memcpy (DiskPartition
+ 0x1BE, DiskPartitionBackup
+ 0x1BE, 0x40);
506 SetFilePointer (OutputHandle
, 0, NULL
, FILE_BEGIN
);
512 // Write boot sector to taget disk/file
514 if (!WriteFile (OutputHandle
, DiskPartition
, 0x200, &BytesReturn
, NULL
)) {
515 return ErrorFileReadWrite
;
518 CloseHandle (InputHandle
);
519 CloseHandle (OutputHandle
);
532 Displays the standard utility information to SDTOUT
544 printf ("%s v%d.%d -Utility to retrieve and update the boot sector or MBR.\n", UTILITY_NAME
, UTILITY_MAJOR_VERSION
, UTILITY_MINOR_VERSION
);
545 printf ("Copyright (c) 2009 Intel Corporation. All rights reserved.\n");
554 printf ("\nUsage: \n\
556 [-l, --list list disks]\n\
557 [-i, --input Filename]\n\
558 [-o, --output Filename]\n\
559 [-m, --mbr process the MBR also]\n\
562 [-q, --quiet disable all messages except fatal errors]\n\
569 Get path information, including physical path for windows platform.
571 @param PathInfo Point to PATH_INFO structure.
573 @return whether path is valid.
580 DRIVE_INFO DriveInfo
;
582 CHAR DiskPathTemplate
[] = "\\\\.\\PHYSICALDRIVE%u";
583 CHAR FloppyPathTemplate
[] = "\\\\.\\%c:";
587 // If path is disk path
589 if (IsLetter(PathInfo
->Path
[0]) && (PathInfo
->Path
[1] == ':') && (PathInfo
->Path
[2] == '\0')) {
590 VolumeLetter
= PathInfo
->Path
[0];
591 if ((VolumeLetter
== 'A') || (VolumeLetter
== 'a') ||
592 (VolumeLetter
== 'B') || (VolumeLetter
== 'b')) {
593 PathInfo
->Type
= PathFloppy
;
594 sprintf (PathInfo
->PhysicalPath
, FloppyPathTemplate
, VolumeLetter
);
598 if (!GetDriveInfo(VolumeLetter
, &DriveInfo
)) {
599 fprintf (stderr
, "ERROR: GetDriveInfo - 0x%x\n", GetLastError ());
603 if (!PathInfo
->Input
&& (DriveInfo
.DriveType
->Type
== DRIVE_FIXED
)) {
604 fprintf (stderr
, "ERROR: Could patch own IDE disk!\n");
608 sprintf(PathInfo
->PhysicalPath
, DiskPathTemplate
, DriveInfo
.DiskNumber
);
609 if (DriveInfo
.DriveType
->Type
== DRIVE_REMOVABLE
) {
610 PathInfo
->Type
= PathUsb
;
611 } else if (DriveInfo
.DriveType
->Type
== DRIVE_FIXED
) {
612 PathInfo
->Type
= PathIde
;
614 fprintf (stderr
, "ERROR, Invalid disk path - %s", PathInfo
->Path
);
621 PathInfo
->Type
= PathFile
;
622 if (PathInfo
->Input
) {
624 // If path is file path, check whether file is valid.
626 f
= fopen (PathInfo
->Path
, "r");
628 fprintf (stderr
, "error E2003: File was not provided!\n");
632 PathInfo
->Type
= PathFile
;
633 strcpy(PathInfo
->PhysicalPath
, PathInfo
->Path
);
648 EFI_STATUS EfiStatus
;
649 PATH_INFO InputPathInfo
= {0};
650 PATH_INFO OutputPathInfo
= {0};
653 SetUtilityName (UTILITY_NAME
);
667 // Parse command line
669 for (Index
= 0; Index
< argc
; Index
++) {
670 if ((stricmp (argv
[Index
], "-l") == 0) || (stricmp (argv
[Index
], "--list") == 0)) {
675 if ((stricmp (argv
[Index
], "-m") == 0) || (stricmp (argv
[Index
], "--mbr") == 0)) {
680 if ((stricmp (argv
[Index
], "-i") == 0) || (stricmp (argv
[Index
], "--input") == 0)) {
681 InputPathInfo
.Path
= argv
[Index
+ 1];
682 InputPathInfo
.Input
= TRUE
;
683 if (InputPathInfo
.Path
== NULL
) {
684 Error (NULL
, 0, 1003, "Invalid option value", "Input file name can't be NULL");
687 if (InputPathInfo
.Path
[0] == '-') {
688 Error (NULL
, 0, 1003, "Invalid option value", "Input file is missing");
695 if ((stricmp (argv
[Index
], "-o") == 0) || (stricmp (argv
[Index
], "--output") == 0)) {
696 OutputPathInfo
.Path
= argv
[Index
+ 1];
697 OutputPathInfo
.Input
= FALSE
;
698 if (OutputPathInfo
.Path
== NULL
) {
699 Error (NULL
, 0, 1003, "Invalid option value", "Output file name can't be NULL");
702 if (OutputPathInfo
.Path
[0] == '-') {
703 Error (NULL
, 0, 1003, "Invalid option value", "Output file is missing");
710 if ((stricmp (argv
[Index
], "-h") == 0) || (stricmp (argv
[Index
], "--help") == 0)) {
715 if (stricmp (argv
[Index
], "--version") == 0) {
720 if ((stricmp (argv
[Index
], "-v") == 0) || (stricmp (argv
[Index
], "--verbose") == 0)) {
724 if ((stricmp (argv
[Index
], "-q") == 0) || (stricmp (argv
[Index
], "--quiet") == 0)) {
728 if ((stricmp (argv
[Index
], "-d") == 0) || (stricmp (argv
[Index
], "--debug") == 0)) {
729 EfiStatus
= AsciiStringToUint64 (argv
[Index
+ 1], FALSE
, &LogLevel
);
730 if (EFI_ERROR (EfiStatus
)) {
731 Error (NULL
, 0, 1003, "Invalid option value", "%s = %s", argv
[Index
], argv
[Index
+ 1]);
735 Error (NULL
, 0, 1003, "Invalid option value", "Debug Level range is 0-9, currnt input level is %d", (int) LogLevel
);
738 SetPrintLevel (LogLevel
);
739 DebugMsg (NULL
, 0, 9, "Debug Mode Set", "Debug Output Mode Level %s is set!", argv
[Index
+ 1]);
745 // Don't recognize the parameter.
747 Error (NULL
, 0, 1000, "Unknown option", "%s", argv
[Index
]);
751 if (InputPathInfo
.Path
== NULL
) {
752 Error (NULL
, 0, 1001, "Missing options", "Input file is missing");
756 if (OutputPathInfo
.Path
== NULL
) {
757 Error (NULL
, 0, 1001, "Missing options", "Output file is missing");
761 if (GetPathInfo(&InputPathInfo
) != ErrorSuccess
) {
762 Error (NULL
, 0, 1003, "Invalid option value", "Input file can't be found.");
766 if (GetPathInfo(&OutputPathInfo
) != ErrorSuccess
) {
767 Error (NULL
, 0, 1003, "Invalid option value", "Output file can't be found.");
772 // Process DBR (Patch or Read)
774 Status
= ProcessBsOrMbr (&InputPathInfo
, &OutputPathInfo
, ProcessMbr
);
776 if (Status
== ErrorSuccess
) {
779 "%s %s: successful!\n",
780 (OutputPathInfo
.Type
!= PathFile
) ? "Write" : "Read",
781 ProcessMbr
? "MBR" : "DBR"
787 "%s: %s %s: failed - %s (LastError: 0x%x)!\n",
788 (Status
== ErrorNoMbr
) ? "WARNING" : "ERROR",
789 (OutputPathInfo
.Type
!= PathFile
) ? "Write" : "Read",
790 ProcessMbr
? "MBR" : "DBR",
791 ErrorStatusDesc
[Status
],