| /** @file | |
| Reading/writing MBR/DBR. | |
| NOTE: | |
| If we write MBR to disk, we just update the MBR code and the partition table wouldn't be over written. | |
| If we process DBR, we will patch MBR to set first partition active if no active partition exists. | |
| Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "CommonLib.h" | |
| #include <errno.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <Common/UefiBaseTypes.h> | |
| #include "ParseInf.h" | |
| #include "EfiUtilityMsgs.h" | |
| // | |
| // Utility Name | |
| // | |
| #define UTILITY_NAME "GnuGenBootSector" | |
| // | |
| // Utility version information | |
| // | |
| #define UTILITY_MAJOR_VERSION 0 | |
| #define UTILITY_MINOR_VERSION 1 | |
| #define MAX_DRIVE 26 | |
| #define PARTITION_TABLE_OFFSET 0x1BE | |
| #define SIZE_OF_PARTITION_ENTRY 0x10 | |
| #define PARTITION_ENTRY_STARTLBA_OFFSET 8 | |
| #define PARTITION_ENTRY_NUM 4 | |
| #define DRIVE_UNKNOWN 0 | |
| #define DRIVE_NO_ROOT_DIR 1 | |
| #define DRIVE_REMOVABLE 2 | |
| #define DRIVE_FIXED 3 | |
| #define DRIVE_REMOTE 4 | |
| #define DRIVE_CDROM 5 | |
| #define DRIVE_RAMDISK 6 | |
| typedef struct _DRIVE_TYPE_DESC { | |
| UINTN Type; | |
| CHAR8 *Description; | |
| } DRIVE_TYPE_DESC; | |
| #define DRIVE_TYPE_ITEM(x) {x, #x} | |
| DRIVE_TYPE_DESC DriveTypeDesc[] = { | |
| DRIVE_TYPE_ITEM (DRIVE_UNKNOWN), | |
| DRIVE_TYPE_ITEM (DRIVE_NO_ROOT_DIR), | |
| DRIVE_TYPE_ITEM (DRIVE_REMOVABLE), | |
| DRIVE_TYPE_ITEM (DRIVE_FIXED), | |
| DRIVE_TYPE_ITEM (DRIVE_REMOTE), | |
| DRIVE_TYPE_ITEM (DRIVE_CDROM), | |
| DRIVE_TYPE_ITEM (DRIVE_RAMDISK), | |
| {(UINTN) -1, NULL} | |
| }; | |
| typedef struct _DRIVE_INFO { | |
| CHAR8 VolumeLetter; | |
| DRIVE_TYPE_DESC *DriveType; | |
| UINTN DiskNumber; | |
| } DRIVE_INFO; | |
| typedef enum { | |
| PathUnknown, | |
| PathFile, | |
| PathFloppy, | |
| PathUsb, | |
| PathIde | |
| } PATH_TYPE; | |
| typedef struct _PATH_INFO { | |
| CHAR8 *Path; | |
| CHAR8 PhysicalPath[260]; | |
| PATH_TYPE Type; | |
| BOOLEAN Input; | |
| } PATH_INFO; | |
| typedef enum { | |
| ErrorSuccess, | |
| ErrorFileCreate, | |
| ErrorFileReadWrite, | |
| ErrorNoMbr, | |
| ErrorFatType, | |
| ErrorPath, | |
| } ERROR_STATUS; | |
| CHAR8 *ErrorStatusDesc[] = { | |
| "Success", | |
| "Failed to create files", | |
| "Failed to read/write files", | |
| "No MBR exists", | |
| "Failed to detect Fat type", | |
| "Inavlid path" | |
| }; | |
| //UnSupported Windows API functions. | |
| UINTN GetLogicalDrives(void) { return 1; } | |
| /** | |
| Get path information, including physical path for Linux platform. | |
| @param PathInfo Point to PATH_INFO structure. | |
| @return whether path is valid. | |
| **/ | |
| ERROR_STATUS | |
| GetPathInfo ( | |
| PATH_INFO *PathInfo | |
| ) | |
| { | |
| FILE *f; | |
| if (strncmp(PathInfo->Path, "/dev/", 5) == 0) { | |
| // | |
| // Process disk path here. | |
| // | |
| // Process floppy disk | |
| if (PathInfo->Path[5] == 'f' && PathInfo->Path[6] == 'd' && PathInfo->Path[8] == '\0') { | |
| PathInfo->Type = PathFloppy; | |
| strcpy (PathInfo->PhysicalPath, PathInfo->Path); | |
| return ErrorSuccess; | |
| } else { | |
| // Other disk types is not supported yet. | |
| fprintf (stderr, "ERROR: It's not a floppy disk!\n"); | |
| return ErrorPath; | |
| } | |
| // Try to open the device. | |
| f = fopen (LongFilePath (PathInfo->Path),"r"); | |
| if (f == NULL) { | |
| printf ("error :open device failed!\n"); | |
| return ErrorPath; | |
| } | |
| fclose (f); | |
| return ErrorSuccess; | |
| } | |
| // Process file path here. | |
| PathInfo->Type = PathFile; | |
| if (PathInfo->Input) { | |
| // If path is file path, check whether file is valid. | |
| printf("Path = %s\n",PathInfo->Path); | |
| f = fopen (LongFilePath (PathInfo->Path), "r"); | |
| if (f == NULL) { | |
| fprintf (stderr, "Test error E2003: File was not provided!\n"); | |
| return ErrorPath; | |
| } | |
| fclose (f); | |
| } | |
| strcpy(PathInfo->PhysicalPath, PathInfo->Path); | |
| return ErrorSuccess; | |
| } | |
| VOID | |
| ListDrive ( | |
| VOID | |
| ) | |
| { | |
| printf("-l or -list not supported!\n"); | |
| } | |
| /** | |
| Writing or reading boot sector or MBR according to the argument. | |
| @param InputInfo PATH_INFO instance for input path | |
| @param OutputInfo PATH_INFO instance for output path | |
| @param ProcessMbr TRUE is to process MBR, otherwise, processing boot sector | |
| @return ERROR_STATUS | |
| **/ | |
| ERROR_STATUS | |
| ProcessBsOrMbr ( | |
| PATH_INFO *InputInfo, | |
| PATH_INFO *OutputInfo, | |
| BOOLEAN ProcessMbr | |
| ) | |
| { | |
| CHAR8 FirstSector[0x200] = {0}; | |
| CHAR8 FirstSectorBackup[0x200] = {0}; | |
| FILE *InputFile; | |
| FILE *OutputFile; | |
| InputFile = fopen (LongFilePath (InputInfo->PhysicalPath), "r"); | |
| if (InputFile == NULL) { | |
| return ErrorFileReadWrite; | |
| } | |
| if (0x200 != fread(FirstSector, 1, 0x200, InputFile)) { | |
| fclose(InputFile); | |
| return ErrorFileReadWrite; | |
| } | |
| fclose(InputFile); | |
| //Not support USB and IDE. | |
| if (InputInfo->Type == PathUsb) { | |
| printf("USB has not been supported yet!"); | |
| return ErrorSuccess; | |
| } | |
| if (InputInfo->Type == PathIde) { | |
| printf("IDE has not been supported yet!"); | |
| return ErrorSuccess; | |
| } | |
| //Process Floppy Disk | |
| OutputFile = fopen (LongFilePath (OutputInfo->PhysicalPath), "r+"); | |
| if (OutputFile == NULL) { | |
| OutputFile = fopen (LongFilePath (OutputInfo->PhysicalPath), "w"); | |
| if (OutputFile == NULL) { | |
| return ErrorFileReadWrite; | |
| } | |
| } | |
| if (OutputInfo->Type != PathFile) { | |
| if (ProcessMbr) { | |
| // | |
| // Use original partition table | |
| // | |
| if (0x200 != fread (FirstSectorBackup, 1, 0x200, OutputFile)) { | |
| fclose(OutputFile); | |
| return ErrorFileReadWrite; | |
| } | |
| memcpy (FirstSector + 0x1BE, FirstSectorBackup + 0x1BE, 0x40); | |
| } | |
| } | |
| if(0x200 != fwrite(FirstSector, 1, 0x200, OutputFile)) { | |
| fclose(OutputFile); | |
| return ErrorFileReadWrite; | |
| } | |
| fclose(OutputFile); | |
| return ErrorSuccess; | |
| } | |
| /** | |
| Displays the standard utility information to SDTOUT | |
| **/ | |
| VOID | |
| Version ( | |
| VOID | |
| ) | |
| { | |
| printf ("%s v%d.%d %s-Utility to retrieve and update the boot sector or MBR.\n", UTILITY_NAME, UTILITY_MAJOR_VERSION, UTILITY_MINOR_VERSION, __BUILD_VERSION); | |
| printf ("Copyright (c) 2007-2014 Intel Corporation. All rights reserved.\n"); | |
| } | |
| VOID | |
| PrintUsage ( | |
| VOID | |
| ) | |
| { | |
| Version(); | |
| printf ("\nUsage: \n\ | |
| GenBootSector\n\ | |
| [-l, --list list disks]\n\ | |
| [-i, --input Filename]\n\ | |
| [-o, --output Filename]\n\ | |
| [-m, --mbr process the MBR also]\n\ | |
| [-v, --verbose]\n\ | |
| [--version]\n\ | |
| [-q, --quiet disable all messages except fatal errors]\n\ | |
| [-d, --debug[#]\n\ | |
| [-h, --help]\n"); | |
| } | |
| int | |
| main ( | |
| int argc, | |
| char *argv[] | |
| ) | |
| { | |
| INTN Index; | |
| BOOLEAN ProcessMbr; | |
| ERROR_STATUS Status; | |
| EFI_STATUS EfiStatus; | |
| PATH_INFO InputPathInfo; | |
| PATH_INFO OutputPathInfo; | |
| UINT64 LogLevel; | |
| SetUtilityName (UTILITY_NAME); | |
| ZeroMem(&InputPathInfo, sizeof(PATH_INFO)); | |
| ZeroMem(&OutputPathInfo, sizeof(PATH_INFO)); | |
| argv ++; | |
| argc --; | |
| ProcessMbr = FALSE; | |
| if (argc == 0) { | |
| PrintUsage(); | |
| return 0; | |
| } | |
| // | |
| // Parse command line | |
| // | |
| for (Index = 0; Index < argc; Index ++) { | |
| if ((stricmp (argv[Index], "-l") == 0) || (stricmp (argv[Index], "--list") == 0)) { | |
| ListDrive (); | |
| return 0; | |
| } | |
| if ((stricmp (argv[Index], "-m") == 0) || (stricmp (argv[Index], "--mbr") == 0)) { | |
| ProcessMbr = TRUE; | |
| continue; | |
| } | |
| if ((stricmp (argv[Index], "-i") == 0) || (stricmp (argv[Index], "--input") == 0)) { | |
| InputPathInfo.Path = argv[Index + 1]; | |
| InputPathInfo.Input = TRUE; | |
| if (InputPathInfo.Path == NULL) { | |
| Error (NULL, 0, 1003, "Invalid option value", "Input file name can't be NULL"); | |
| return 1; | |
| } | |
| if (InputPathInfo.Path[0] == '-') { | |
| Error (NULL, 0, 1003, "Invalid option value", "Input file is missing"); | |
| return 1; | |
| } | |
| ++Index; | |
| continue; | |
| } | |
| if ((stricmp (argv[Index], "-o") == 0) || (stricmp (argv[Index], "--output") == 0)) { | |
| OutputPathInfo.Path = argv[Index + 1]; | |
| OutputPathInfo.Input = FALSE; | |
| if (OutputPathInfo.Path == NULL) { | |
| Error (NULL, 0, 1003, "Invalid option value", "Output file name can't be NULL"); | |
| return 1; | |
| } | |
| if (OutputPathInfo.Path[0] == '-') { | |
| Error (NULL, 0, 1003, "Invalid option value", "Output file is missing"); | |
| return 1; | |
| } | |
| ++Index; | |
| continue; | |
| } | |
| if ((stricmp (argv[Index], "-h") == 0) || (stricmp (argv[Index], "--help") == 0)) { | |
| PrintUsage (); | |
| return 0; | |
| } | |
| if (stricmp (argv[Index], "--version") == 0) { | |
| Version (); | |
| return 0; | |
| } | |
| if ((stricmp (argv[Index], "-v") == 0) || (stricmp (argv[Index], "--verbose") == 0)) { | |
| continue; | |
| } | |
| if ((stricmp (argv[Index], "-q") == 0) || (stricmp (argv[Index], "--quiet") == 0)) { | |
| continue; | |
| } | |
| if ((stricmp (argv[Index], "-d") == 0) || (stricmp (argv[Index], "--debug") == 0)) { | |
| EfiStatus = AsciiStringToUint64 (argv[Index + 1], FALSE, &LogLevel); | |
| if (EFI_ERROR (EfiStatus)) { | |
| Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[Index], argv[Index + 1]); | |
| return 1; | |
| } | |
| if (LogLevel > 9) { | |
| Error (NULL, 0, 1003, "Invalid option value", "Debug Level range is 0-9, currnt input level is %d", (int) LogLevel); | |
| return 1; | |
| } | |
| SetPrintLevel (LogLevel); | |
| DebugMsg (NULL, 0, 9, "Debug Mode Set", "Debug Output Mode Level %s is set!", argv[Index + 1]); | |
| ++Index; | |
| continue; | |
| } | |
| // | |
| // Don't recognize the parameter. | |
| // | |
| Error (NULL, 0, 1000, "Unknown option", "%s", argv[Index]); | |
| return 1; | |
| } | |
| if (InputPathInfo.Path == NULL) { | |
| Error (NULL, 0, 1001, "Missing options", "Input file is missing"); | |
| return 1; | |
| } | |
| if (OutputPathInfo.Path == NULL) { | |
| Error (NULL, 0, 1001, "Missing options", "Output file is missing"); | |
| return 1; | |
| } | |
| if (GetPathInfo(&InputPathInfo) != ErrorSuccess) { | |
| Error (NULL, 0, 1003, "Invalid option value", "Input file can't be found."); | |
| return 1; | |
| } | |
| if (GetPathInfo(&OutputPathInfo) != ErrorSuccess) { | |
| Error (NULL, 0, 1003, "Invalid option value", "Output file can't be found."); | |
| return 1; | |
| } | |
| // | |
| // Process DBR (Patch or Read) | |
| // | |
| Status = ProcessBsOrMbr (&InputPathInfo, &OutputPathInfo, ProcessMbr); | |
| if (Status == ErrorSuccess) { | |
| fprintf ( | |
| stdout, | |
| "%s %s: successful!\n", | |
| (OutputPathInfo.Type != PathFile) ? "Write" : "Read", | |
| ProcessMbr ? "MBR" : "DBR" | |
| ); | |
| return 0; | |
| } else { | |
| fprintf ( | |
| stderr, | |
| "%s: %s %s: failed - %s (LastError: 0x%x)!\n", | |
| (Status == ErrorNoMbr) ? "WARNING" : "ERROR", | |
| (OutputPathInfo.Type != PathFile) ? "Write" : "Read", | |
| ProcessMbr ? "MBR" : "DBR", | |
| ErrorStatusDesc[Status], | |
| errno | |
| ); | |
| return 1; | |
| } | |
| } |