blob: 428011ca046c5e1f7303fe5facb052e449be23ed [file] [log] [blame]
/** @file
The tool dumps the contents of a firmware volume
Copyright (c) 1999 - 2018, Intel Corporation. All rights reserved.<BR>
Copyright (c) 2022, Konstantin Aladyshev <aladyshev22@gmail.com><BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#ifdef __GNUC__
#include <unistd.h>
#else
#include <direct.h>
#endif
#include <FvLib.h>
#include <Common/UefiBaseTypes.h>
#include <Common/UefiCapsule.h>
#include <Common/PiFirmwareFile.h>
#include <Common/PiFirmwareVolume.h>
#include <Guid/PiFirmwareFileSystem.h>
#include <IndustryStandard/PeImage.h>
#include <Protocol/GuidedSectionExtraction.h>
#include "Compress.h"
#include "Decompress.h"
#include "VolInfo.h"
#include "CommonLib.h"
#include "EfiUtilityMsgs.h"
#include "FirmwareVolumeBufferLib.h"
#include "OsPath.h"
#include "ParseGuidedSectionTools.h"
#include "StringFuncs.h"
#include "ParseInf.h"
#include "PeCoffLib.h"
//
// Utility global variables
//
EFI_GUID gEfiCrc32GuidedSectionExtractionProtocolGuid = EFI_CRC32_GUIDED_SECTION_EXTRACTION_PROTOCOL_GUID;
EFI_GUID gPeiAprioriFileNameGuid = { 0x1b45cc0a, 0x156a, 0x428a, { 0XAF, 0x62, 0x49, 0x86, 0x4d, 0xa0, 0xe6, 0xe6 }};
EFI_GUID gAprioriGuid = { 0xFC510EE7, 0xFFDC, 0x11D4, { 0xBD, 0x41, 0x00, 0x80, 0xC7, 0x3C, 0x88, 0x81 }};
#define UTILITY_MAJOR_VERSION 1
#define UTILITY_MINOR_VERSION 0
#define UTILITY_NAME "VolInfo"
#define EFI_SECTION_ERROR EFIERR (100)
//
// Structure to keep a list of guid-to-basenames
//
typedef struct _GUID_TO_BASENAME {
struct _GUID_TO_BASENAME *Next;
INT8 Guid[PRINTED_GUID_BUFFER_SIZE];
INT8 BaseName[MAX_LINE_LEN];
} GUID_TO_BASENAME;
static GUID_TO_BASENAME *mGuidBaseNameList = NULL;
//
// Store GUIDed Section guid->tool mapping
//
EFI_HANDLE mParsedGuidedSectionTools = NULL;
CHAR8* mUtilityFilename = NULL;
BOOLEAN EnableHash = FALSE;
CHAR8 *OpenSslPath = NULL;
EFI_STATUS
ParseGuidBaseNameFile (
CHAR8 *FileName
);
EFI_STATUS
FreeGuidBaseNameList (
VOID
);
EFI_STATUS
PrintGuidName (
IN UINT8 *GuidStr
);
EFI_STATUS
ParseSection (
IN UINT8 *SectionBuffer,
IN UINT32 BufferLength
);
EFI_STATUS
DumpDepexSection (
IN UINT8 *Ptr,
IN UINT32 SectionLength
);
STATIC
EFI_STATUS
ReadHeader (
IN FILE *InputFile,
OUT UINT32 *FvSize,
OUT BOOLEAN *ErasePolarity
);
STATIC
EFI_STATUS
PrintAprioriFile (
EFI_FFS_FILE_HEADER *FileHeader
);
STATIC
EFI_STATUS
PrintFileInfo (
EFI_FIRMWARE_VOLUME_HEADER *FvImage,
EFI_FFS_FILE_HEADER *FileHeader,
BOOLEAN ErasePolarity
);
static
EFI_STATUS
PrintFvInfo (
IN VOID *Fv,
IN BOOLEAN IsChildFv
);
static
VOID
LoadGuidedSectionToolsTxt (
IN CHAR8* FirmwareVolumeFilename
);
EFI_STATUS
CombinePath (
IN CHAR8* DefaultPath,
IN CHAR8* AppendPath,
OUT CHAR8* NewPath
);
void
Usage (
VOID
);
UINT32
UnicodeStrLen (
IN CHAR16 *String
)
/*++
Routine Description:
Returns the length of a null-terminated unicode string.
Arguments:
String - The pointer to a null-terminated unicode string.
Returns:
N/A
--*/
{
UINT32 Length;
for (Length = 0; *String != L'\0'; String++, Length++) {
;
}
return Length;
}
VOID
Unicode2AsciiString (
IN CHAR16 *Source,
OUT CHAR8 *Destination
)
/*++
Routine Description:
Convert a null-terminated unicode string to a null-terminated ascii string.
Arguments:
Source - The pointer to the null-terminated input unicode string.
Destination - The pointer to the null-terminated output ascii string.
Returns:
N/A
--*/
{
while (*Source != '\0') {
*(Destination++) = (CHAR8) *(Source++);
}
//
// End the ascii with a NULL.
//
*Destination = '\0';
}
int
main (
int argc,
char *argv[]
)
/*++
Routine Description:
GC_TODO: Add function description
Arguments:
argc - GC_TODO: add argument description
] - GC_TODO: add argument description
Returns:
GC_TODO: add return values
--*/
{
FILE *InputFile;
int BytesRead;
EFI_FIRMWARE_VOLUME_HEADER *FvImage;
UINT32 FvSize;
EFI_STATUS Status;
int Offset;
BOOLEAN ErasePolarity;
UINT64 LogLevel;
CHAR8 *OpenSslEnv;
CHAR8 *OpenSslCommand;
SetUtilityName (UTILITY_NAME);
//
// Print utility header
//
printf ("%s Version %d.%d Build %s\n",
UTILITY_NAME,
UTILITY_MAJOR_VERSION,
UTILITY_MINOR_VERSION,
__BUILD_VERSION
);
if (argc == 1) {
Usage ();
return -1;
}
argc--;
argv++;
LogLevel = 0;
Offset = 0;
//
// Look for help options
//
if ((strcmp(argv[0], "-h") == 0) || (strcmp(argv[0], "--help") == 0) ||
(strcmp(argv[0], "-?") == 0) || (strcmp(argv[0], "/?") == 0)) {
Usage();
return STATUS_SUCCESS;
}
//
// Version has already be printed, so just return success
//
if (strcmp(argv[0], "--version") == 0) {
return STATUS_SUCCESS;
}
//
// If they specified -x xref guid/basename cross-reference files, process it.
// This will print the basename beside each file guid. To use it, specify
// -x xref_filename to processdsc, then use xref_filename as a parameter
// here.
//
while (argc > 0) {
if ((strcmp(argv[0], "-x") == 0) || (strcmp(argv[0], "--xref") == 0)) {
ParseGuidBaseNameFile (argv[1]);
printf("ParseGuidBaseNameFile: %s\n", argv[1]);
argc -= 2;
argv += 2;
continue;
}
if (strcmp(argv[0], "--offset") == 0) {
//
// Hex or decimal?
//
if ((argv[1][0] == '0') && (tolower ((int)argv[1][1]) == 'x')) {
if (sscanf (argv[1], "%x", &Offset) != 1) {
Error (NULL, 0, 1003, "Invalid option value", "Offset = %s", argv[1]);
return GetUtilityStatus ();
}
} else {
if (sscanf (argv[1], "%d", &Offset) != 1) {
Error (NULL, 0, 1003, "Invalid option value", "Offset = %s", argv[1]);
return GetUtilityStatus ();
}
//
// See if they said something like "64K"
//
if (tolower ((int)argv[1][strlen (argv[1]) - 1]) == 'k') {
Offset *= 1024;
}
}
argc -= 2;
argv += 2;
continue;
}
if ((stricmp (argv[0], "--hash") == 0)) {
if (EnableHash == TRUE) {
//
// --hash already given in the option, ignore this one
//
argc --;
argv ++;
continue;
}
EnableHash = TRUE;
OpenSslCommand = "openssl";
OpenSslEnv = getenv("OPENSSL_PATH");
if (OpenSslEnv == NULL) {
OpenSslPath = OpenSslCommand;
} else {
//
// We add quotes to the Openssl Path in case it has space characters
//
OpenSslPath = malloc(2+strlen(OpenSslEnv)+strlen(OpenSslCommand)+1);
if (OpenSslPath == NULL) {
Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
return GetUtilityStatus ();
}
CombinePath(OpenSslEnv, OpenSslCommand, OpenSslPath);
}
if (OpenSslPath == NULL){
Error (NULL, 0, 3000, "Open SSL command not available. Please verify PATH or set OPENSSL_PATH.", NULL);
return GetUtilityStatus ();
}
argc --;
argv ++;
continue;
}
if ((stricmp (argv[0], "-v") == 0) || (stricmp (argv[0], "--verbose") == 0)) {
SetPrintLevel (VERBOSE_LOG_LEVEL);
argc --;
argv ++;
continue;
}
if ((stricmp (argv[0], "-q") == 0) || (stricmp (argv[0], "--quiet") == 0)) {
SetPrintLevel (KEY_LOG_LEVEL);
argc --;
argv ++;
continue;
}
if ((stricmp (argv[0], "-d") == 0) || (stricmp (argv[0], "--debug") == 0)) {
Status = AsciiStringToUint64 (argv[1], FALSE, &LogLevel);
if (EFI_ERROR (Status)) {
Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[0], argv[1]);
return -1;
}
if (LogLevel > 9) {
Error (NULL, 0, 1003, "Invalid option value", "Debug Level range is 0-9, current 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[1]);
argc -= 2;
argv += 2;
continue;
}
mUtilityFilename = argv[0];
argc --;
argv ++;
}
//
// Open the file containing the FV
//
if (mUtilityFilename == NULL) {
Error (NULL, 0, 1001, "Missing option", "Input files are not specified");
return GetUtilityStatus ();
}
InputFile = fopen (LongFilePath (mUtilityFilename), "rb");
if (InputFile == NULL) {
Error (NULL, 0, 0001, "Error opening the input file", mUtilityFilename);
return GetUtilityStatus ();
}
//
// Skip over pad bytes if specified. This is used if they prepend 0xff
// data to the FV image binary.
//
if (Offset != 0) {
fseek (InputFile, Offset, SEEK_SET);
}
//
// Determine size of FV
//
Status = ReadHeader (InputFile, &FvSize, &ErasePolarity);
if (EFI_ERROR (Status)) {
Error (NULL, 0, 0003, "error parsing FV image", "%s Header is invalid", mUtilityFilename);
fclose (InputFile);
return GetUtilityStatus ();
}
//
// Allocate a buffer for the FV image
//
FvImage = malloc (FvSize);
if (FvImage == NULL) {
Error (NULL, 0, 4001, "Resource: Memory can't be allocated", NULL);
fclose (InputFile);
return GetUtilityStatus ();
}
//
// Seek to the start of the image, then read the entire FV to the buffer
//
fseek (InputFile, Offset, SEEK_SET);
BytesRead = fread (FvImage, 1, FvSize, InputFile);
fclose (InputFile);
if ((unsigned int) BytesRead != FvSize) {
Error (NULL, 0, 0004, "error reading FvImage from", mUtilityFilename);
free (FvImage);
return GetUtilityStatus ();
}
LoadGuidedSectionToolsTxt (mUtilityFilename);
PrintFvInfo (FvImage, FALSE);
//
// Clean up
//
free (FvImage);
FreeGuidBaseNameList ();
return GetUtilityStatus ();
}
static
EFI_STATUS
PrintFvInfo (
IN VOID *Fv,
IN BOOLEAN IsChildFv
)
/*++
Routine Description:
GC_TODO: Add function description
Arguments:
Fv - Firmware Volume to print information about
IsChildFv - Flag specifies whether the input FV is a child FV.
Returns:
EFI_STATUS
--*/
{
EFI_STATUS Status;
UINTN NumberOfFiles;
BOOLEAN ErasePolarity;
UINTN FvSize;
EFI_FFS_FILE_HEADER *CurrentFile;
UINTN Key;
Status = FvBufGetSize (Fv, &FvSize);
NumberOfFiles = 0;
ErasePolarity =
(((EFI_FIRMWARE_VOLUME_HEADER*)Fv)->Attributes & EFI_FVB2_ERASE_POLARITY) ?
TRUE : FALSE;
//
// Get the first file
//
Key = 0;
Status = FvBufFindNextFile (Fv, &Key, (VOID **) &CurrentFile);
if (EFI_ERROR (Status)) {
Error (NULL, 0, 0003, "error parsing FV image", "cannot find the first file in the FV image");
return GetUtilityStatus ();
}
//
// Display information about files found
//
while (CurrentFile != NULL) {
//
// Increment the number of files counter
//
NumberOfFiles++;
//
// Display info about this file
//
Status = PrintFileInfo (Fv, CurrentFile, ErasePolarity);
if (EFI_ERROR (Status)) {
Error (NULL, 0, 0003, "error parsing FV image", "failed to parse a file in the FV");
return GetUtilityStatus ();
}
//
// Get the next file
//
Status = FvBufFindNextFile (Fv, &Key, (VOID **) &CurrentFile);
if (Status == EFI_NOT_FOUND) {
CurrentFile = NULL;
} else if (EFI_ERROR (Status)) {
Error (NULL, 0, 0003, "error parsing FV image", "cannot find the next file in the FV image");
return GetUtilityStatus ();
}
}
if (IsChildFv) {
printf ("There are a total of %d files in the child FV\n", (int) NumberOfFiles);
} else {
printf ("There are a total of %d files in this FV\n", (int) NumberOfFiles);
}
return EFI_SUCCESS;
}
UINT32
GetOccupiedSize (
IN UINT32 ActualSize,
IN UINT32 Alignment
)
/*++
Routine Description:
This function returns the next larger size that meets the alignment
requirement specified.
Arguments:
ActualSize The size.
Alignment The desired alignment.
Returns:
EFI_SUCCESS Function completed successfully.
EFI_ABORTED The function encountered an error.
--*/
{
UINT32 OccupiedSize;
OccupiedSize = ActualSize;
while ((OccupiedSize & (Alignment - 1)) != 0) {
OccupiedSize++;
}
return OccupiedSize;
}
static
CHAR8 *
SectionNameToStr (
IN EFI_SECTION_TYPE Type
)
/*++
Routine Description:
Converts EFI Section names to Strings
Arguments:
Type - The EFI Section type
Returns:
CHAR8* - Pointer to the String containing the section name.
--*/
{
CHAR8 *SectionStr;
CHAR8 *SectionTypeStringTable[] = {
//
// 0X00
//
"EFI_SECTION_ALL",
//
// 0x01
//
"EFI_SECTION_COMPRESSION",
//
// 0x02
//
"EFI_SECTION_GUID_DEFINED",
//
// 0x03
//
"Unknown section type - Reserved 0x03",
//
// 0x04
//
"Unknown section type - Reserved 0x04",
//
// 0x05
//
"Unknown section type - Reserved 0x05",
//
// 0x06
//
"Unknown section type - Reserved 0x06",
//
// 0x07
//
"Unknown section type - Reserved 0x07",
//
// 0x08
//
"Unknown section type - Reserved 0x08",
//
// 0x09
//
"Unknown section type - Reserved 0x09",
//
// 0x0A
//
"Unknown section type - Reserved 0x0A",
//
// 0x0B
//
"Unknown section type - Reserved 0x0B",
//
// 0x0C
//
"Unknown section type - Reserved 0x0C",
//
// 0x0D
//
"Unknown section type - Reserved 0x0D",
//
// 0x0E
//
"Unknown section type - Reserved 0x0E",
//
// 0x0F
//
"Unknown section type - Reserved 0x0E",
//
// 0x10
//
"EFI_SECTION_PE32",
//
// 0x11
//
"EFI_SECTION_PIC",
//
// 0x12
//
"EFI_SECTION_TE",
//
// 0x13
//
"EFI_SECTION_DXE_DEPEX",
//
// 0x14
//
"EFI_SECTION_VERSION",
//
// 0x15
//
"EFI_SECTION_USER_INTERFACE",
//
// 0x16
//
"EFI_SECTION_COMPATIBILITY16",
//
// 0x17
//
"EFI_SECTION_FIRMWARE_VOLUME_IMAGE",
//
// 0x18
//
"EFI_SECTION_FREEFORM_SUBTYPE_GUID",
//
// 0x19
//
"EFI_SECTION_RAW",
//
// 0x1A
//
"Unknown section type - 0x1A",
//
// 0x1B
//
"EFI_SECTION_PEI_DEPEX",
//
// 0x1C
//
"EFI_SECTION_MM_DEPEX",
//
// 0x1C+
//
"Unknown section type - Reserved - beyond last defined section"
};
if (Type > EFI_SECTION_LAST_SECTION_TYPE) {
Type = EFI_SECTION_LAST_SECTION_TYPE + 1;
}
SectionStr = malloc (100);
if (SectionStr == NULL) {
printf ("Error: Out of memory resources.\n");
return SectionStr;
}
strcpy (SectionStr, SectionTypeStringTable[Type]);
return SectionStr;
}
STATIC
EFI_STATUS
ReadHeader (
IN FILE *InputFile,
OUT UINT32 *FvSize,
OUT BOOLEAN *ErasePolarity
)
/*++
Routine Description:
This function determines the size of the FV and the erase polarity. The
erase polarity is the FALSE value for file state.
Arguments:
InputFile The file that contains the FV image.
FvSize The size of the FV.
ErasePolarity The FV erase polarity.
Returns:
EFI_SUCCESS Function completed successfully.
EFI_INVALID_PARAMETER A required parameter was NULL or is out of range.
EFI_ABORTED The function encountered an error.
--*/
{
EFI_FIRMWARE_VOLUME_HEADER VolumeHeader;
EFI_FV_BLOCK_MAP_ENTRY BlockMap;
UINTN Signature[2];
UINTN BytesRead;
UINT32 Size;
size_t ReadSize;
BytesRead = 0;
Size = 0;
//
// Check input parameters
//
if (InputFile == NULL || FvSize == NULL || ErasePolarity == NULL) {
Error (__FILE__, __LINE__, 0, "application error", "invalid parameter to function");
return EFI_INVALID_PARAMETER;
}
//
// Read the header
//
ReadSize = fread (&VolumeHeader, sizeof (EFI_FIRMWARE_VOLUME_HEADER) - sizeof (EFI_FV_BLOCK_MAP_ENTRY), 1, InputFile);
if (ReadSize != 1) {
return EFI_ABORTED;
}
BytesRead = sizeof (EFI_FIRMWARE_VOLUME_HEADER) - sizeof (EFI_FV_BLOCK_MAP_ENTRY);
Signature[0] = VolumeHeader.Signature;
Signature[1] = 0;
//
// Print FV header information
//
printf ("Signature: %s (%X)\n", (char *) Signature, (unsigned) VolumeHeader.Signature);
printf ("Attributes: %X\n", (unsigned) VolumeHeader.Attributes);
if (VolumeHeader.Attributes & EFI_FVB2_READ_DISABLED_CAP) {
printf (" EFI_FVB2_READ_DISABLED_CAP\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_READ_ENABLED_CAP) {
printf (" EFI_FVB2_READ_ENABLED_CAP\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_READ_STATUS) {
printf (" EFI_FVB2_READ_STATUS\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_WRITE_DISABLED_CAP) {
printf (" EFI_FVB2_WRITE_DISABLED_CAP\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_WRITE_ENABLED_CAP) {
printf (" EFI_FVB2_WRITE_ENABLED_CAP\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_WRITE_STATUS) {
printf (" EFI_FVB2_WRITE_STATUS\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_LOCK_CAP) {
printf (" EFI_FVB2_LOCK_CAP\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_LOCK_STATUS) {
printf (" EFI_FVB2_LOCK_STATUS\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_STICKY_WRITE) {
printf (" EFI_FVB2_STICKY_WRITE\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_MEMORY_MAPPED) {
printf (" EFI_FVB2_MEMORY_MAPPED\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_ERASE_POLARITY) {
printf (" EFI_FVB2_ERASE_POLARITY\n");
*ErasePolarity = TRUE;
}
#if (PI_SPECIFICATION_VERSION < 0x00010000)
if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT) {
printf (" EFI_FVB2_ALIGNMENT\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_2) {
printf (" EFI_FVB2_ALIGNMENT_2\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_4) {
printf (" EFI_FVB2_ALIGNMENT_4\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_8) {
printf (" EFI_FVB2_ALIGNMENT_8\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_16) {
printf (" EFI_FVB2_ALIGNMENT_16\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_32) {
printf (" EFI_FVB2_ALIGNMENT_32\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_64) {
printf (" EFI_FVB2_ALIGNMENT_64\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_128) {
printf (" EFI_FVB2_ALIGNMENT_128\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_256) {
printf (" EFI_FVB2_ALIGNMENT_256\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_512) {
printf (" EFI_FVB2_ALIGNMENT_512\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_1K) {
printf (" EFI_FVB2_ALIGNMENT_1K\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_2K) {
printf (" EFI_FVB2_ALIGNMENT_2K\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_4K) {
printf (" EFI_FVB2_ALIGNMENT_4K\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_8K) {
printf (" EFI_FVB2_ALIGNMENT_8K\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_16K) {
printf (" EFI_FVB2_ALIGNMENT_16K\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_32K) {
printf (" EFI_FVB2_ALIGNMENT_32K\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT_64K) {
printf (" EFI_FVB2_ALIGNMENT_64K\n");
}
#else
if (VolumeHeader.Attributes & EFI_FVB2_READ_LOCK_CAP) {
printf (" EFI_FVB2_READ_LOCK_CAP\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_READ_LOCK_STATUS) {
printf (" EFI_FVB2_READ_LOCK_STATUS\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_WRITE_LOCK_CAP) {
printf (" EFI_FVB2_WRITE_LOCK_CAP\n");
}
if (VolumeHeader.Attributes & EFI_FVB2_WRITE_LOCK_STATUS) {
printf (" EFI_FVB2_WRITE_LOCK_STATUS\n");
}
switch (VolumeHeader.Attributes & EFI_FVB2_ALIGNMENT) {
case EFI_FVB2_ALIGNMENT_1:
printf (" EFI_FVB2_ALIGNMENT_1\n");
break;
case EFI_FVB2_ALIGNMENT_2:
printf (" EFI_FVB2_ALIGNMENT_2\n");
break;
case EFI_FVB2_ALIGNMENT_4:
printf (" EFI_FVB2_ALIGNMENT_4\n");
break;
case EFI_FVB2_ALIGNMENT_8:
printf (" EFI_FVB2_ALIGNMENT_8\n");
break;
case EFI_FVB2_ALIGNMENT_16:
printf (" EFI_FVB2_ALIGNMENT_16\n");
break;
case EFI_FVB2_ALIGNMENT_32:
printf (" EFI_FVB2_ALIGNMENT_32\n");
break;
case EFI_FVB2_ALIGNMENT_64:
printf (" EFI_FVB2_ALIGNMENT_64\n");
break;
case EFI_FVB2_ALIGNMENT_128:
printf (" EFI_FVB2_ALIGNMENT_128\n");
break;
case EFI_FVB2_ALIGNMENT_256:
printf (" EFI_FVB2_ALIGNMENT_256\n");
break;
case EFI_FVB2_ALIGNMENT_512:
printf (" EFI_FVB2_ALIGNMENT_512\n");
break;
case EFI_FVB2_ALIGNMENT_1K:
printf (" EFI_FVB2_ALIGNMENT_1K\n");
break;
case EFI_FVB2_ALIGNMENT_2K:
printf (" EFI_FVB2_ALIGNMENT_2K\n");
break;
case EFI_FVB2_ALIGNMENT_4K:
printf (" EFI_FVB2_ALIGNMENT_4K\n");
break;
case EFI_FVB2_ALIGNMENT_8K:
printf (" EFI_FVB2_ALIGNMENT_8K\n");
break;
case EFI_FVB2_ALIGNMENT_16K:
printf (" EFI_FVB2_ALIGNMENT_16K\n");
break;
case EFI_FVB2_ALIGNMENT_32K:
printf (" EFI_FVB2_ALIGNMENT_32K\n");
break;
case EFI_FVB2_ALIGNMENT_64K:
printf (" EFI_FVB2_ALIGNMENT_64K\n");
break;
case EFI_FVB2_ALIGNMENT_128K:
printf (" EFI_FVB2_ALIGNMENT_128K\n");
break;
case EFI_FVB2_ALIGNMENT_256K:
printf (" EFI_FVB2_ALIGNMENT_256K\n");
break;
case EFI_FVB2_ALIGNMENT_512K:
printf (" EFI_FVB2_ALIGNMENT_512K\n");
break;
case EFI_FVB2_ALIGNMENT_1M:
printf (" EFI_FVB2_ALIGNMENT_1M\n");
break;
case EFI_FVB2_ALIGNMENT_2M:
printf (" EFI_FVB2_ALIGNMENT_2M\n");
break;
case EFI_FVB2_ALIGNMENT_4M:
printf (" EFI_FVB2_ALIGNMENT_4M\n");
break;
case EFI_FVB2_ALIGNMENT_8M:
printf (" EFI_FVB2_ALIGNMENT_8M\n");
break;
case EFI_FVB2_ALIGNMENT_16M:
printf (" EFI_FVB2_ALIGNMENT_16M\n");
break;
case EFI_FVB2_ALIGNMENT_32M:
printf (" EFI_FVB2_ALIGNMENT_32M\n");
break;
case EFI_FVB2_ALIGNMENT_64M:
printf (" EFI_FVB2_ALIGNMENT_64M\n");
break;
case EFI_FVB2_ALIGNMENT_128M:
printf (" EFI_FVB2_ALIGNMENT_128M\n");
break;
case EFI_FVB2_ALIGNMENT_256M:
printf (" EFI_FVB2_ALIGNMENT_256M\n");
break;
case EFI_FVB2_ALIGNMENT_512M:
printf (" EFI_FVB2_ALIGNMENT_512M\n");
break;
case EFI_FVB2_ALIGNMENT_1G:
printf (" EFI_FVB2_ALIGNMENT_1G\n");
break;
case EFI_FVB2_ALIGNMENT_2G:
printf (" EFI_FVB2_ALIGNMENT_2G\n");
break;
}
#endif
printf ("Header Length: 0x%08X\n", VolumeHeader.HeaderLength);
printf ("File System ID: ");
PrintGuid (&VolumeHeader.FileSystemGuid);
//
// printf ("\n");
//
printf ("Revision: 0x%04X\n", VolumeHeader.Revision);
do {
ReadSize = fread (&BlockMap, sizeof (EFI_FV_BLOCK_MAP_ENTRY), 1, InputFile);
if (ReadSize != 1) {
return EFI_ABORTED;
}
BytesRead += sizeof (EFI_FV_BLOCK_MAP_ENTRY);
if (BlockMap.NumBlocks != 0) {
printf ("Number of Blocks: 0x%08X\n", (unsigned) BlockMap.NumBlocks);
printf ("Block Length: 0x%08X\n", (unsigned) BlockMap.Length);
Size += BlockMap.NumBlocks * BlockMap.Length;
}
} while (!(BlockMap.NumBlocks == 0 && BlockMap.Length == 0));
if (BytesRead != VolumeHeader.HeaderLength) {
printf ("ERROR: Header length not consistent with Block Maps!\n");
return EFI_ABORTED;
}
if (VolumeHeader.FvLength != Size) {
printf ("ERROR: Volume Size not consistent with Block Maps!\n");
return EFI_ABORTED;
}
printf ("Total Volume Size: 0x%08X\n", (unsigned) Size);
*FvSize = Size;
//
// rewind (InputFile);
//
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
PrintAprioriFile (
EFI_FFS_FILE_HEADER *FileHeader
)
/*++
Routine Description:
Print GUIDs from the APRIORI file
Arguments:
FileHeader - The file header
Returns:
EFI_SUCCESS - The APRIORI file was parsed correctly
EFI_SECTION_ERROR - Problem with file parsing
--*/
{
UINT8 GuidBuffer[PRINTED_GUID_BUFFER_SIZE];
UINT32 HeaderSize;
HeaderSize = FvBufGetFfsHeaderSize (FileHeader);
if (FileHeader->Type != EFI_FV_FILETYPE_FREEFORM)
return EFI_SECTION_ERROR;
EFI_COMMON_SECTION_HEADER* SectionHeader = (EFI_COMMON_SECTION_HEADER *) ((UINTN) FileHeader + HeaderSize);
if (SectionHeader->Type != EFI_SECTION_RAW)
return EFI_SECTION_ERROR;
UINT32 SectionLength = GetSectionFileLength (SectionHeader);
EFI_GUID* FileName = (EFI_GUID *) ((UINT8 *) SectionHeader + sizeof (EFI_COMMON_SECTION_HEADER));
while (((UINT8 *) FileName) < ((UINT8 *) SectionHeader + SectionLength)) {
PrintGuidToBuffer (FileName, GuidBuffer, sizeof (GuidBuffer), TRUE);
printf ("%s ", GuidBuffer);
PrintGuidName (GuidBuffer);
printf ("\n");
FileName++;
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
PrintFileInfo (
EFI_FIRMWARE_VOLUME_HEADER *FvImage,
EFI_FFS_FILE_HEADER *FileHeader,
BOOLEAN ErasePolarity
)
/*++
Routine Description:
GC_TODO: Add function description
Arguments:
FvImage - GC_TODO: add argument description
FileHeader - GC_TODO: add argument description
ErasePolarity - GC_TODO: add argument description
Returns:
EFI_SUCCESS - GC_TODO: Add description for return value
EFI_ABORTED - GC_TODO: Add description for return value
--*/
{
UINT32 FileLength;
UINT8 FileState;
UINT8 Checksum;
EFI_FFS_FILE_HEADER2 BlankHeader;
EFI_STATUS Status;
UINT8 GuidBuffer[PRINTED_GUID_BUFFER_SIZE];
UINT32 HeaderSize;
#if (PI_SPECIFICATION_VERSION < 0x00010000)
UINT16 *Tail;
#endif
//
// Check if we have free space
//
HeaderSize = FvBufGetFfsHeaderSize(FileHeader);
if (ErasePolarity) {
memset (&BlankHeader, -1, HeaderSize);
} else {
memset (&BlankHeader, 0, HeaderSize);
}
if (memcmp (&BlankHeader, FileHeader, HeaderSize) == 0) {
return EFI_SUCCESS;
}
//
// Print file information.
//
printf ("============================================================\n");
printf ("File Name: ");
PrintGuidToBuffer (&FileHeader->Name, GuidBuffer, sizeof (GuidBuffer), TRUE);
printf ("%s ", GuidBuffer);
PrintGuidName (GuidBuffer);
printf ("\n");
//
// PrintGuid (&FileHeader->Name);
// printf ("\n");
//
FileLength = FvBufGetFfsFileSize (FileHeader);
printf ("File Offset: 0x%08X\n", (unsigned) ((UINTN) FileHeader - (UINTN) FvImage));
printf ("File Length: 0x%08X\n", (unsigned) FileLength);
printf ("File Attributes: 0x%02X\n", FileHeader->Attributes);
printf ("File State: 0x%02X\n", FileHeader->State);
//
// Print file state
//
FileState = GetFileState (ErasePolarity, FileHeader);
switch (FileState) {
case EFI_FILE_HEADER_CONSTRUCTION:
printf (" EFI_FILE_HEADER_CONSTRUCTION\n");
return EFI_SUCCESS;
case EFI_FILE_HEADER_INVALID:
printf (" EFI_FILE_HEADER_INVALID\n");
return EFI_SUCCESS;
case EFI_FILE_HEADER_VALID:
printf (" EFI_FILE_HEADER_VALID\n");
Checksum = CalculateSum8 ((UINT8 *) FileHeader, HeaderSize);
Checksum = (UINT8) (Checksum - FileHeader->IntegrityCheck.Checksum.File);
Checksum = (UINT8) (Checksum - FileHeader->State);
if (Checksum != 0) {
printf ("ERROR: Header checksum invalid.\n");
return EFI_ABORTED;
}
return EFI_SUCCESS;
case EFI_FILE_DELETED:
printf (" EFI_FILE_DELETED\n");
case EFI_FILE_MARKED_FOR_UPDATE:
printf (" EFI_FILE_MARKED_FOR_UPDATE\n");
case EFI_FILE_DATA_VALID:
printf (" EFI_FILE_DATA_VALID\n");
//
// Calculate header checksum
//
Checksum = CalculateSum8 ((UINT8 *) FileHeader, HeaderSize);
Checksum = (UINT8) (Checksum - FileHeader->IntegrityCheck.Checksum.File);
Checksum = (UINT8) (Checksum - FileHeader->State);
if (Checksum != 0) {
Error (NULL, 0, 0003, "error parsing FFS file", "FFS file with Guid %s has invalid header checksum", GuidBuffer);
return EFI_ABORTED;
}
FileLength = FvBufGetFfsFileSize (FileHeader);
if (FileHeader->Attributes & FFS_ATTRIB_CHECKSUM) {
//
// Calculate file checksum
//
Checksum = CalculateSum8 ((UINT8 *)FileHeader + HeaderSize, FileLength - HeaderSize);
Checksum = Checksum + FileHeader->IntegrityCheck.Checksum.File;
if (Checksum != 0) {
Error (NULL, 0, 0003, "error parsing FFS file", "FFS file with Guid %s has invalid file checksum", GuidBuffer);
return EFI_ABORTED;
}
} else {
if (FileHeader->IntegrityCheck.Checksum.File != FFS_FIXED_CHECKSUM) {
Error (NULL, 0, 0003, "error parsing FFS file", "FFS file with Guid %s has invalid header checksum -- not set to fixed value of 0xAA", GuidBuffer);
return EFI_ABORTED;
}
}
#if (PI_SPECIFICATION_VERSION < 0x00010000)
//
// Verify tail if present
//
if (FileHeader->Attributes & FFS_ATTRIB_TAIL_PRESENT) {
//
// Verify tail is complement of integrity check field in the header.
//
Tail = (UINT16 *) ((UINTN) FileHeader + GetLength (FileHeader->Size) - sizeof (EFI_FFS_INTEGRITY_CHECK));
if (FileHeader->IntegrityCheck.TailReference != (UINT16)~(*Tail)) {
Error (NULL, 0, 0003, "error parsing FFS file", \
"FFS file with Guid %s failed in the integrity check, tail is not the complement of the header field", GuidBuffer);
return EFI_ABORTED;
}
}
#endif
break;
default:
Error (NULL, 0, 0003, "error parsing FFS file", "FFS file with Guid %s has the invalid/unrecognized file state bits", GuidBuffer);
return EFI_ABORTED;
}
printf ("File Type: 0x%02X ", FileHeader->Type);
switch (FileHeader->Type) {
case EFI_FV_FILETYPE_RAW:
printf ("EFI_FV_FILETYPE_RAW\n");
break;
case EFI_FV_FILETYPE_FREEFORM:
printf ("EFI_FV_FILETYPE_FREEFORM\n");
break;
case EFI_FV_FILETYPE_SECURITY_CORE:
printf ("EFI_FV_FILETYPE_SECURITY_CORE\n");
break;
case EFI_FV_FILETYPE_PEI_CORE:
printf ("EFI_FV_FILETYPE_PEI_CORE\n");
break;
case EFI_FV_FILETYPE_DXE_CORE:
printf ("EFI_FV_FILETYPE_DXE_CORE\n");
break;
case EFI_FV_FILETYPE_PEIM:
printf ("EFI_FV_FILETYPE_PEIM\n");
break;
case EFI_FV_FILETYPE_DRIVER:
printf ("EFI_FV_FILETYPE_DRIVER\n");
break;
case EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER:
printf ("EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER\n");
break;
case EFI_FV_FILETYPE_APPLICATION:
printf ("EFI_FV_FILETYPE_APPLICATION\n");
break;
case EFI_FV_FILETYPE_SMM:
printf ("EFI_FV_FILETYPE_MM\n");
break;
case EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE:
printf ("EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE\n");
break;
case EFI_FV_FILETYPE_COMBINED_SMM_DXE:
printf ("EFI_FV_FILETYPE_COMBINED_MM_DXE\n");
break;
case EFI_FV_FILETYPE_SMM_CORE:
printf ("EFI_FV_FILETYPE_MM_CORE\n");
break;
case EFI_FV_FILETYPE_MM_STANDALONE:
printf ("EFI_FV_FILETYPE_MM_STANDALONE\n");
break;
case EFI_FV_FILETYPE_MM_CORE_STANDALONE:
printf ("EFI_FV_FILETYPE_MM_CORE_STANDALONE\n");
break;
case EFI_FV_FILETYPE_FFS_PAD:
printf ("EFI_FV_FILETYPE_FFS_PAD\n");
break;
default:
printf ("\nERROR: Unrecognized file type %X.\n", FileHeader->Type);
return EFI_ABORTED;
break;
}
switch (FileHeader->Type) {
case EFI_FV_FILETYPE_ALL:
case EFI_FV_FILETYPE_RAW:
case EFI_FV_FILETYPE_FFS_PAD:
break;
default:
//
// All other files have sections
//
Status = ParseSection (
(UINT8 *) ((UINTN) FileHeader + HeaderSize),
FvBufGetFfsFileSize (FileHeader) - HeaderSize
);
if (EFI_ERROR (Status)) {
//
// printf ("ERROR: Parsing the FFS file.\n");
//
return EFI_ABORTED;
}
break;
}
if (!CompareGuid (
&FileHeader->Name,
&gPeiAprioriFileNameGuid
))
{
printf("\n");
printf("PEI APRIORI FILE:\n");
return PrintAprioriFile (FileHeader);
}
if (!CompareGuid (
&FileHeader->Name,
&gAprioriGuid
))
{
printf("\n");
printf("DXE APRIORI FILE:\n");
return PrintAprioriFile (FileHeader);
}
return EFI_SUCCESS;
}
EFI_STATUS
RebaseImageRead (
IN VOID *FileHandle,
IN UINTN FileOffset,
IN OUT UINT32 *ReadSize,
OUT VOID *Buffer
)
/*++
Routine Description:
Support routine for the PE/COFF Loader that reads a buffer from a PE/COFF file
Arguments:
FileHandle - The handle to the PE/COFF file
FileOffset - The offset, in bytes, into the file to read
ReadSize - The number of bytes to read from the file starting at FileOffset
Buffer - A pointer to the buffer to read the data into.
Returns:
EFI_SUCCESS - ReadSize bytes of data were read into Buffer from the PE/COFF file starting at FileOffset
--*/
{
CHAR8 *Destination8;
CHAR8 *Source8;
UINT32 Length;
Destination8 = Buffer;
Source8 = (CHAR8 *) ((UINTN) FileHandle + FileOffset);
Length = *ReadSize;
while (Length--) {
*(Destination8++) = *(Source8++);
}
return EFI_SUCCESS;
}
EFI_STATUS
SetAddressToSectionHeader (
IN CHAR8 *FileName,
IN OUT UINT8 *FileBuffer,
IN UINT64 NewPe32BaseAddress
)
/*++
Routine Description:
Set new base address into the section header of PeImage
Arguments:
FileName - Name of file
FileBuffer - Pointer to PeImage.
NewPe32BaseAddress - New Base Address for PE image.
Returns:
EFI_SUCCESS Set new base address into this image successfully.
--*/
{
EFI_STATUS Status;
PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
UINTN Index;
EFI_IMAGE_OPTIONAL_HEADER_UNION *ImgHdr;
EFI_IMAGE_SECTION_HEADER *SectionHeader;
//
// Initialize context
//
memset (&ImageContext, 0, sizeof (ImageContext));
ImageContext.Handle = (VOID *) FileBuffer;
ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE) RebaseImageRead;
Status = PeCoffLoaderGetImageInfo (&ImageContext);
if (EFI_ERROR (Status)) {
Error (NULL, 0, 3000, "Invalid", "The input PeImage %s is not valid", FileName);
return Status;
}
if (ImageContext.RelocationsStripped) {
Error (NULL, 0, 3000, "Invalid", "The input PeImage %s has no relocation to be fixed up", FileName);
return Status;
}
//
// Get PeHeader pointer
//
ImgHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)(FileBuffer + ImageContext.PeCoffHeaderOffset);
//
// Get section header list
//
SectionHeader = (EFI_IMAGE_SECTION_HEADER *) (
(UINTN) ImgHdr +
sizeof (UINT32) +
sizeof (EFI_IMAGE_FILE_HEADER) +
ImgHdr->Pe32.FileHeader.SizeOfOptionalHeader
);
//
// Set base address into the first section header that doesn't point to code section.
//
for (Index = 0; Index < ImgHdr->Pe32.FileHeader.NumberOfSections; Index ++, SectionHeader ++) {
if ((SectionHeader->Characteristics & EFI_IMAGE_SCN_CNT_CODE) == 0) {
*(UINT64 *) &SectionHeader->PointerToRelocations = NewPe32BaseAddress;
break;
}
}
//
// BaseAddress is set to section header.
//
return EFI_SUCCESS;
}
EFI_STATUS
RebaseImage (
IN CHAR8 *FileName,
IN OUT UINT8 *FileBuffer,
IN UINT64 NewPe32BaseAddress
)
/*++
Routine Description:
Set new base address into PeImage, and fix up PeImage based on new address.
Arguments:
FileName - Name of file
FileBuffer - Pointer to PeImage.
NewPe32BaseAddress - New Base Address for PE image.
Returns:
EFI_INVALID_PARAMETER - BaseAddress is not valid.
EFI_SUCCESS - Update PeImage is correctly.
--*/
{
EFI_STATUS Status;
PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
UINTN Index;
EFI_IMAGE_OPTIONAL_HEADER_UNION *ImgHdr;
UINT8 *MemoryImagePointer;
EFI_IMAGE_SECTION_HEADER *SectionHeader;
//
// Initialize context
//
memset (&ImageContext, 0, sizeof (ImageContext));
ImageContext.Handle = (VOID *) FileBuffer;
ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE) RebaseImageRead;
Status = PeCoffLoaderGetImageInfo (&ImageContext);
if (EFI_ERROR (Status)) {
Error (NULL, 0, 3000, "Invalid", "The input PeImage %s is not valid", FileName);
return Status;
}
if (ImageContext.RelocationsStripped) {
Error (NULL, 0, 3000, "Invalid", "The input PeImage %s has no relocation to be fixed up", FileName);
return Status;
}
//
// Get PeHeader pointer
//
ImgHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)(FileBuffer + ImageContext.PeCoffHeaderOffset);
//
// Load and Relocate Image Data
//
MemoryImagePointer = (UINT8 *) malloc ((UINTN) ImageContext.ImageSize + ImageContext.SectionAlignment);
if (MemoryImagePointer == NULL) {
Error (NULL, 0, 4001, "Resource", "memory cannot be allocated on rebase of %s", FileName);
return EFI_OUT_OF_RESOURCES;
}
memset ((VOID *) MemoryImagePointer, 0, (UINTN) ImageContext.ImageSize + ImageContext.SectionAlignment);
ImageContext.ImageAddress = ((UINTN) MemoryImagePointer + ImageContext.SectionAlignment - 1) & (~((INT64)ImageContext.SectionAlignment - 1));
Status = PeCoffLoaderLoadImage (&ImageContext);
if (EFI_ERROR (Status)) {
Error (NULL, 0, 3000, "Invalid", "LocateImage() call failed on rebase of %s", FileName);
free ((VOID *) MemoryImagePointer);
return Status;
}
ImageContext.DestinationAddress = NewPe32BaseAddress;
Status = PeCoffLoaderRelocateImage (&ImageContext);
if (EFI_ERROR (Status)) {
Error (NULL, 0, 3000, "Invalid", "RelocateImage() call failed on rebase of %s", FileName);
free ((VOID *) MemoryImagePointer);
return Status;
}
//
// Copy Relocated data to raw image file.
//
SectionHeader = (EFI_IMAGE_SECTION_HEADER *) (
(UINTN) ImgHdr +
sizeof (UINT32) +
sizeof (EFI_IMAGE_FILE_HEADER) +
ImgHdr->Pe32.FileHeader.SizeOfOptionalHeader
);
for (Index = 0; Index < ImgHdr->Pe32.FileHeader.NumberOfSections; Index ++, SectionHeader ++) {
CopyMem (
FileBuffer + SectionHeader->PointerToRawData,
(VOID*) (UINTN) (ImageContext.ImageAddress + SectionHeader->VirtualAddress),
SectionHeader->SizeOfRawData
);
}
free ((VOID *) MemoryImagePointer);
//
// Update Image Base Address
//
if (ImgHdr->Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
ImgHdr->Pe32.OptionalHeader.ImageBase = (UINT32) NewPe32BaseAddress;
} else if (ImgHdr->Pe32Plus.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
ImgHdr->Pe32Plus.OptionalHeader.ImageBase = NewPe32BaseAddress;
} else {
Error (NULL, 0, 3000, "Invalid", "unknown PE magic signature %X in PE32 image %s",
ImgHdr->Pe32.OptionalHeader.Magic,
FileName
);
return EFI_ABORTED;
}
//
// Set new base address into section header
//
Status = SetAddressToSectionHeader (FileName, FileBuffer, NewPe32BaseAddress);
return Status;
}
EFI_STATUS
CombinePath (
IN CHAR8* DefaultPath,
IN CHAR8* AppendPath,
OUT CHAR8* NewPath
)
{
UINT32 DefaultPathLen;
UINT64 Index;
CHAR8 QuotesStr[] = "\"";
strcpy(NewPath, QuotesStr);
DefaultPathLen = strlen(DefaultPath);
strcat(NewPath, DefaultPath);
Index = 0;
for (; Index < DefaultPathLen + 1; Index ++) {
if (NewPath[Index] == '\\' || NewPath[Index] == '/') {
if (NewPath[Index + 1] != '\0') {
NewPath[Index] = '/';
}
}
}
if (NewPath[Index -1] != '/') {
NewPath[Index] = '/';
NewPath[Index + 1] = '\0';
}
strcat(NewPath, AppendPath);
strcat(NewPath, QuotesStr);
return EFI_SUCCESS;
}
EFI_STATUS
ParseSection (
IN UINT8 *SectionBuffer,
IN UINT32 BufferLength
)
/*++
Routine Description:
Parses EFI Sections
Arguments:
SectionBuffer - Buffer containing the section to parse.
BufferLength - Length of SectionBuffer
Returns:
EFI_SECTION_ERROR - Problem with section parsing.
(a) compression errors
(b) unrecognized section
EFI_UNSUPPORTED - Do not know how to parse the section.
EFI_SUCCESS - Section successfully parsed.
EFI_OUT_OF_RESOURCES - Memory allocation failed.
--*/
{
EFI_SECTION_TYPE Type;
UINT8 *Ptr;
UINT32 SectionLength;
UINT32 SectionHeaderLen;
CHAR8 *SectionName;
EFI_STATUS Status;
UINT32 ParsedLength;
UINT8 *CompressedBuffer;
UINT32 CompressedLength;
UINT8 *UncompressedBuffer;
UINT32 UncompressedLength;
UINT8 *ToolOutputBuffer;
UINT32 ToolOutputLength;
UINT8 CompressionType;
UINT32 DstSize;
UINT32 ScratchSize;
UINT8 *ScratchBuffer;
DECOMPRESS_FUNCTION DecompressFunction;
GETINFO_FUNCTION GetInfoFunction;
// CHAR16 *name;
CHAR8 *ExtractionTool;
CHAR8 *ToolInputFile;
CHAR8 *ToolOutputFile;
CHAR8 *SystemCommand;
EFI_GUID *EfiGuid;
UINT16 DataOffset;
UINT16 Attributes;
UINT32 RealHdrLen;
CHAR8 *ToolInputFileName;
CHAR8 *ToolOutputFileName;
CHAR8 *UIFileName;
CHAR8 *VersionString;
ParsedLength = 0;
ToolInputFileName = NULL;
ToolOutputFileName = NULL;
while (ParsedLength < BufferLength) {
Ptr = SectionBuffer + ParsedLength;
SectionLength = GetLength (((EFI_COMMON_SECTION_HEADER *) Ptr)->Size);
Type = ((EFI_COMMON_SECTION_HEADER *) Ptr)->Type;
//
// This is sort of an odd check, but is necessary because FFS files are
// padded to a QWORD boundary, meaning there is potentially a whole section
// header worth of 0xFF bytes.
//
if (SectionLength == 0xffffff && Type == 0xff) {
ParsedLength += 4;
continue;
}
//
// Get real section file size
//
SectionLength = GetSectionFileLength ((EFI_COMMON_SECTION_HEADER *) Ptr);
SectionHeaderLen = GetSectionHeaderLength((EFI_COMMON_SECTION_HEADER *)Ptr);
SectionName = SectionNameToStr (Type);
if (SectionName != NULL) {
printf ("------------------------------------------------------------\n");
printf (" Type: %s\n Size: 0x%08X\n", SectionName, (unsigned) SectionLength);
free (SectionName);
}
switch (Type) {
case EFI_SECTION_RAW:
case EFI_SECTION_PIC:
case EFI_SECTION_TE:
// default is no more information
break;
case EFI_SECTION_PE32:
if (EnableHash) {
ToolInputFileName = "edk2Temp_InputEfi.tmp";
ToolOutputFileName = "edk2Temp_OutputHash.tmp";
RebaseImage(ToolInputFileName, (UINT8*)Ptr + SectionHeaderLen, 0);
PutFileImage (
ToolInputFileName,
(CHAR8*)Ptr + SectionHeaderLen,
SectionLength - SectionHeaderLen
);
SystemCommand = malloc (
strlen (OPENSSL_COMMAND_FORMAT_STRING) +
strlen (OpenSslPath) +
strlen (ToolInputFileName) +
strlen (ToolOutputFileName) +
1
);
if (SystemCommand == NULL) {
Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
return EFI_OUT_OF_RESOURCES;
}
sprintf (
SystemCommand,
OPENSSL_COMMAND_FORMAT_STRING,
OpenSslPath,
ToolOutputFileName,
ToolInputFileName
);
if (system (SystemCommand) != EFI_SUCCESS) {
Error (NULL, 0, 3000, "Open SSL command not available. Please verify PATH or set OPENSSL_PATH.", NULL);
}
else {
FILE *fp;
CHAR8 *StrLine;
CHAR8 *NewStr;
UINT32 nFileLen;
if((fp = fopen(ToolOutputFileName,"r")) == NULL) {
Error (NULL, 0, 0004, "Hash the PE32 image failed.", NULL);
}
else {
fseek(fp,0,SEEK_SET);
fseek(fp,0,SEEK_END);
nFileLen = ftell(fp);
fseek(fp,0,SEEK_SET);
StrLine = malloc(nFileLen);
if (StrLine == NULL) {
fclose(fp);
free (SystemCommand);
Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
return EFI_OUT_OF_RESOURCES;
}
fgets(StrLine, nFileLen, fp);
NewStr = strrchr (StrLine, '=');
printf (" SHA1: %s\n", NewStr + 1);
free (StrLine);
fclose(fp);
}
}
remove(ToolInputFileName);
remove(ToolOutputFileName);
free (SystemCommand);
}
break;
case EFI_SECTION_USER_INTERFACE:
UIFileName = (CHAR8 *) malloc (UnicodeStrLen (((EFI_USER_INTERFACE_SECTION *) Ptr)->FileNameString) + 1);
if (UIFileName == NULL) {
Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
return EFI_OUT_OF_RESOURCES;
}
Unicode2AsciiString (((EFI_USER_INTERFACE_SECTION *) Ptr)->FileNameString, UIFileName);
printf (" String: %s\n", UIFileName);
free (UIFileName);
break;
case EFI_SECTION_FIRMWARE_VOLUME_IMAGE:
printf ("/------------ Firmware Volume section start ---------------\\\n");
Status = PrintFvInfo (Ptr + SectionHeaderLen, TRUE);
if (EFI_ERROR (Status)) {
Error (NULL, 0, 0003, "printing of FV section contents failed", NULL);
return EFI_SECTION_ERROR;
}
printf ("\\------------ Firmware Volume section end -----------------/\n");
break;
case EFI_SECTION_COMPATIBILITY16:
//
// Section does not contain any further header information.
//
break;
case EFI_SECTION_FREEFORM_SUBTYPE_GUID:
printf (" Guid: ");
if (SectionHeaderLen == sizeof (EFI_COMMON_SECTION_HEADER))
PrintGuid (&((EFI_FREEFORM_SUBTYPE_GUID_SECTION *)Ptr)->SubTypeGuid);
else
PrintGuid (&((EFI_FREEFORM_SUBTYPE_GUID_SECTION2 *)Ptr)->SubTypeGuid);
printf ("\n");
break;
case EFI_SECTION_PEI_DEPEX:
case EFI_SECTION_DXE_DEPEX:
case EFI_SECTION_SMM_DEPEX:
DumpDepexSection (Ptr, SectionLength);
break;
case EFI_SECTION_VERSION:
printf (" Build Number: 0x%04X\n", *(UINT16 *)(Ptr + SectionHeaderLen));
VersionString = (CHAR8 *) malloc (UnicodeStrLen (((EFI_VERSION_SECTION *) Ptr)->VersionString) + 1);
if (VersionString == NULL) {
Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
return EFI_OUT_OF_RESOURCES;
}
Unicode2AsciiString (((EFI_VERSION_SECTION *) Ptr)->VersionString, VersionString);
printf (" Version String: %s\n", VersionString);
break;
case EFI_SECTION_COMPRESSION:
UncompressedBuffer = NULL;
if (SectionHeaderLen == sizeof (EFI_COMMON_SECTION_HEADER)) {
RealHdrLen = sizeof(EFI_COMPRESSION_SECTION);
UncompressedLength = ((EFI_COMPRESSION_SECTION *)Ptr)->UncompressedLength;
CompressionType = ((EFI_COMPRESSION_SECTION *)Ptr)->CompressionType;
} else {
RealHdrLen = sizeof(EFI_COMPRESSION_SECTION2);
UncompressedLength = ((EFI_COMPRESSION_SECTION2 *)Ptr)->UncompressedLength;
CompressionType = ((EFI_COMPRESSION_SECTION2 *)Ptr)->CompressionType;
}
CompressedLength = SectionLength - RealHdrLen;
printf (" Uncompressed Length: 0x%08X\n", (unsigned) UncompressedLength);
if (CompressionType == EFI_NOT_COMPRESSED) {
printf (" Compression Type: EFI_NOT_COMPRESSED\n");
if (CompressedLength != UncompressedLength) {
Error (
NULL,
0,
0,
"file is not compressed, but the compressed length does not match the uncompressed length",
NULL
);
return EFI_SECTION_ERROR;
}
UncompressedBuffer = Ptr + RealHdrLen;
} else if (CompressionType == EFI_STANDARD_COMPRESSION) {
GetInfoFunction = EfiGetInfo;
DecompressFunction = EfiDecompress;
printf (" Compression Type: EFI_STANDARD_COMPRESSION\n");
CompressedBuffer = Ptr + RealHdrLen;
Status = GetInfoFunction (CompressedBuffer, CompressedLength, &DstSize, &ScratchSize);
if (EFI_ERROR (Status)) {
Error (NULL, 0, 0003, "error getting compression info from compression section", NULL);
return EFI_SECTION_ERROR;
}
if (DstSize != UncompressedLength) {
Error (NULL, 0, 0003, "compression error in the compression section", NULL);
return EFI_SECTION_ERROR;
}
ScratchBuffer = malloc (ScratchSize);
if (ScratchBuffer == NULL) {
Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
return EFI_OUT_OF_RESOURCES;
}
UncompressedBuffer = malloc (UncompressedLength);
if (UncompressedBuffer == NULL) {
free (ScratchBuffer);
Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
return EFI_OUT_OF_RESOURCES;
}
Status = DecompressFunction (
CompressedBuffer,
CompressedLength,
UncompressedBuffer,
UncompressedLength,
ScratchBuffer,
ScratchSize
);
free (ScratchBuffer);
if (EFI_ERROR (Status)) {
Error (NULL, 0, 0003, "decompress failed", NULL);
free (UncompressedBuffer);
return EFI_SECTION_ERROR;
}
} else {
Error (NULL, 0, 0003, "unrecognized compression type", "type 0x%X", CompressionType);
return EFI_SECTION_ERROR;
}
printf ("/------------ Encapsulation section start -----------------\\\n");
Status = ParseSection (UncompressedBuffer, UncompressedLength);
printf ("\\------------ Encapsulation section end -------------------/\n");
if (CompressionType == EFI_STANDARD_COMPRESSION) {
//
// We need to deallocate Buffer
//
free (UncompressedBuffer);
}
if (EFI_ERROR (Status)) {
Error (NULL, 0, 0003, "failed to parse section", NULL);
return EFI_SECTION_ERROR;
}
break;
case EFI_SECTION_GUID_DEFINED:
if (SectionHeaderLen == sizeof(EFI_COMMON_SECTION_HEADER)) {
EfiGuid = &((EFI_GUID_DEFINED_SECTION *) Ptr)->SectionDefinitionGuid;
DataOffset = ((EFI_GUID_DEFINED_SECTION *) Ptr)->DataOffset;
Attributes = ((EFI_GUID_DEFINED_SECTION *) Ptr)->Attributes;
} else {
EfiGuid = &((EFI_GUID_DEFINED_SECTION2 *) Ptr)->SectionDefinitionGuid;
DataOffset = ((EFI_GUID_DEFINED_SECTION2 *) Ptr)->DataOffset;
Attributes = ((EFI_GUID_DEFINED_SECTION2 *) Ptr)->Attributes;
}
printf (" SectionDefinitionGuid: ");
PrintGuid (EfiGuid);
printf ("\n");
printf (" DataOffset: 0x%04X\n", (unsigned) DataOffset);
printf (" Attributes: 0x%04X\n", (unsigned) Attributes);
ExtractionTool =
LookupGuidedSectionToolPath (
mParsedGuidedSectionTools,
EfiGuid
);
if (ExtractionTool != NULL) {
#ifndef __GNUC__
ToolInputFile = CloneString (tmpnam (NULL));
ToolOutputFile = CloneString (tmpnam (NULL));
#else
char tmp1[] = "/tmp/fileXXXXXX";
char tmp2[] = "/tmp/fileXXXXXX";
int fd1;
int fd2;
fd1 = mkstemp(tmp1);
fd2 = mkstemp(tmp2);
ToolInputFile = CloneString(tmp1);
ToolOutputFile = CloneString(tmp2);
close(fd1);
close(fd2);
#endif
if ((ToolInputFile == NULL) || (ToolOutputFile == NULL)) {
if (ToolInputFile != NULL) {
free (ToolInputFile);
}
if (ToolOutputFile != NULL) {
free (ToolOutputFile);
}
free (ExtractionTool);
Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
return EFI_OUT_OF_RESOURCES;
}
//
// Construction 'system' command string
//
SystemCommand = malloc (
strlen (EXTRACT_COMMAND_FORMAT_STRING) +
strlen (ExtractionTool) +
strlen (ToolInputFile) +
strlen (ToolOutputFile) +
1
);
if (SystemCommand == NULL) {
free (ToolInputFile);
free (ToolOutputFile);
free (ExtractionTool);
Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
return EFI_OUT_OF_RESOURCES;
}
sprintf (
SystemCommand,
EXTRACT_COMMAND_FORMAT_STRING,
ExtractionTool,
ToolOutputFile,
ToolInputFile
);
free (ExtractionTool);
if (!CompareGuid (
EfiGuid,
&gEfiCrc32GuidedSectionExtractionProtocolGuid
)
) {
DataOffset -= 4;
}
Status =
PutFileImage (
ToolInputFile,
(CHAR8*)Ptr + DataOffset,
SectionLength - DataOffset
);
system (SystemCommand);
remove (ToolInputFile);
free (ToolInputFile);
Status =
GetFileImage (
ToolOutputFile,
(CHAR8 **)&ToolOutputBuffer,
&ToolOutputLength
);
remove (ToolOutputFile);
free (ToolOutputFile);
free (SystemCommand);
if (EFI_ERROR (Status)) {
Error (NULL, 0, 0004, "unable to read decoded GUIDED section", NULL);
return EFI_SECTION_ERROR;
}
printf ("/------------ Encapsulation section start -----------------\\\n");
Status = ParseSection (
ToolOutputBuffer,
ToolOutputLength
);
if (EFI_ERROR (Status)) {
Error (NULL, 0, 0003, "parse of decoded GUIDED section failed", NULL);
return EFI_SECTION_ERROR;
}
printf ("\\------------ Encapsulation section end -------------------/\n");
//
// Check for CRC32 sections which we can handle internally if needed.
//
} else if (!CompareGuid (
EfiGuid,
&gEfiCrc32GuidedSectionExtractionProtocolGuid
)
) {
//
// CRC32 guided section
//
printf ("/------------ Encapsulation section start -----------------\\\n");
Status = ParseSection (
Ptr + DataOffset,
SectionLength - DataOffset
);
if (EFI_ERROR (Status)) {
Error (NULL, 0, 0003, "parse of CRC32 GUIDED section failed", NULL);
return EFI_SECTION_ERROR;
}
printf ("\\------------ Encapsulation section end -------------------/\n");
} else {
//
// We don't know how to parse it now.
//
Error (NULL, 0, 0003, "Error parsing section", \
"EFI_SECTION_GUID_DEFINED cannot be parsed at this time. Tool to decode this section should have been defined in GuidedSectionTools.txt (built in the FV directory).");
return EFI_UNSUPPORTED;
}
break;
default:
//
// Unknown section, return error
//
Error (NULL, 0, 0003, "unrecognized section type found", "section type = 0x%X", Type);
return EFI_SECTION_ERROR;
}
ParsedLength += SectionLength;
//
// We make then next section begin on a 4-byte boundary
//
ParsedLength = GetOccupiedSize (ParsedLength, 4);
}
if (ParsedLength < BufferLength) {
Error (NULL, 0, 0003, "sections do not completely fill the sectioned buffer being parsed", NULL);
return EFI_SECTION_ERROR;
}
return EFI_SUCCESS;
}
EFI_STATUS
DumpDepexSection (
IN UINT8 *Ptr,
IN UINT32 SectionLength
)
/*++
Routine Description:
GC_TODO: Add function description
Arguments:
Ptr - GC_TODO: add argument description
SectionLength - GC_TODO: add argument description
Returns:
EFI_SUCCESS - GC_TODO: Add description for return value
--*/
{
UINT8 GuidBuffer[PRINTED_GUID_BUFFER_SIZE];
//
// Need at least a section header + data
//
if (SectionLength <= sizeof (EFI_COMMON_SECTION_HEADER)) {
return EFI_SUCCESS;
}
Ptr += GetSectionHeaderLength((EFI_COMMON_SECTION_HEADER *)Ptr);
SectionLength -= GetSectionHeaderLength((EFI_COMMON_SECTION_HEADER *)Ptr);
while (SectionLength > 0) {
printf (" ");
switch (*Ptr) {
case EFI_DEP_BEFORE:
printf ("BEFORE\n");
Ptr++;
SectionLength--;
break;
case EFI_DEP_AFTER:
printf ("AFTER\n");
Ptr++;
SectionLength--;
break;
case EFI_DEP_PUSH:
printf ("PUSH\n ");
PrintGuidToBuffer ((EFI_GUID *) (Ptr + 1), GuidBuffer, sizeof (GuidBuffer), TRUE);
printf ("%s ", GuidBuffer);
PrintGuidName (GuidBuffer);
printf ("\n");
//
// PrintGuid ((EFI_GUID *)(Ptr + 1));
//
Ptr += 17;
SectionLength -= 17;
break;
case EFI_DEP_AND:
printf ("AND\n");
Ptr++;
SectionLength--;
break;
case EFI_DEP_OR:
printf ("OR\n");
Ptr++;
SectionLength--;
break;
case EFI_DEP_NOT:
printf ("NOT\n");
Ptr++;
SectionLength--;
break;
case EFI_DEP_TRUE:
printf ("TRUE\n");
Ptr++;
SectionLength--;
break;
case EFI_DEP_FALSE:
printf ("FALSE\n");
Ptr++;
SectionLength--;
break;
case EFI_DEP_END:
printf ("END DEPEX\n");
Ptr++;
SectionLength--;
break;
case EFI_DEP_SOR:
printf ("SOR\n");
Ptr++;
SectionLength--;
break;
default:
printf ("Unrecognized byte in depex: 0x%X\n", *Ptr);
return EFI_SUCCESS;
}
}
return EFI_SUCCESS;
}
EFI_STATUS
PrintGuidName (
IN UINT8 *GuidStr
)
/*++
Routine Description:
GC_TODO: Add function description
Arguments:
GuidStr - GC_TODO: add argument description
Returns:
EFI_SUCCESS - GC_TODO: Add description for return value
EFI_INVALID_PARAMETER - GC_TODO: Add description for return value
--*/
{
GUID_TO_BASENAME *GPtr;
//
// If we have a list of guid-to-basenames, then go through the list to
// look for a guid string match. If found, print the basename to stdout,
// otherwise return a failure.
//
GPtr = mGuidBaseNameList;
while (GPtr != NULL) {
if (_stricmp ((CHAR8*) GuidStr, (CHAR8*) GPtr->Guid) == 0) {
printf ("%s", GPtr->BaseName);
return EFI_SUCCESS;
}
GPtr = GPtr->Next;
}
return EFI_INVALID_PARAMETER;
}
EFI_STATUS
ParseGuidBaseNameFile (
CHAR8 *FileName
)
/*++
Routine Description:
GC_TODO: Add function description
Arguments:
FileName - GC_TODO: add argument description
Returns:
EFI_DEVICE_ERROR - GC_TODO: Add description for return value
EFI_OUT_OF_RESOURCES - GC_TODO: Add description for return value
EFI_SUCCESS - GC_TODO: Add description for return value
--*/
{
FILE *Fptr;
CHAR8 Line[MAX_LINE_LEN];
CHAR8 FormatString[MAX_LINE_LEN];
GUID_TO_BASENAME *GPtr;
if ((Fptr = fopen (LongFilePath (FileName), "r")) == NULL) {
printf ("ERROR: Failed to open input cross-reference file '%s'\n", FileName);
return EFI_DEVICE_ERROR;
}
//
// Generate the format string for fscanf
//
sprintf (
FormatString,
"%%%us %%%us",
(unsigned) sizeof (GPtr->Guid) - 1,
(unsigned) sizeof (GPtr->BaseName) - 1
);
while (fgets (Line, sizeof (Line), Fptr) != NULL) {
//
// Allocate space for another guid/basename element
//
GPtr = malloc (sizeof (GUID_TO_BASENAME));
if (GPtr == NULL) {
fclose (Fptr);
return EFI_OUT_OF_RESOURCES;
}
memset ((char *) GPtr, 0, sizeof (GUID_TO_BASENAME));
if (sscanf (Line, FormatString, GPtr->Guid, GPtr->BaseName) == 2) {
GPtr->Next = mGuidBaseNameList;
mGuidBaseNameList = GPtr;
} else {
//
// Some sort of error. Just continue.
//
free (GPtr);
}
}
fclose (Fptr);
return EFI_SUCCESS;
}
EFI_STATUS
FreeGuidBaseNameList (
VOID
)
/*++
Routine Description:
GC_TODO: Add function description
Arguments:
None
Returns:
EFI_SUCCESS - GC_TODO: Add description for return value
--*/
{
GUID_TO_BASENAME *Next;
while (mGuidBaseNameList != NULL) {
Next = mGuidBaseNameList->Next;
free (mGuidBaseNameList);
mGuidBaseNameList = Next;
}
return EFI_SUCCESS;
}
static
VOID
LoadGuidedSectionToolsTxt (
IN CHAR8* FirmwareVolumeFilename
)
{
CHAR8* PeerFilename;
CHAR8* Places[] = {
NULL,
//NULL,
};
UINTN Index;
Places[0] = FirmwareVolumeFilename;
//Places[1] = mUtilityFilename;
mParsedGuidedSectionTools = NULL;
for (Index = 0; Index < (sizeof(Places)/sizeof(Places[0])); Index++) {
PeerFilename = OsPathPeerFilePath (Places[Index], "GuidedSectionTools.txt");
//printf("Loading %s...\n", PeerFilename);
if (OsPathExists (PeerFilename)) {
mParsedGuidedSectionTools = ParseGuidedSectionToolsFile (PeerFilename);
}
free (PeerFilename);
if (mParsedGuidedSectionTools != NULL) {
return;
}
}
}
void
Usage (
VOID
)
/*++
Routine Description:
GC_TODO: Add function description
Arguments:
None
Returns:
GC_TODO: add return values
--*/
{
//
// Summary usage
//
fprintf (stdout, "Usage: %s [options] <input_file>\n\n", UTILITY_NAME);
//
// Copyright declaration
//
fprintf (stdout, "Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.\n\n");
fprintf (stdout, " Display Tiano Firmware Volume FFS image information\n\n");
//
// Details Option
//
fprintf (stdout, "optional arguments:\n");
fprintf (stdout, " -h, --help\n\
Show this help message and exit\n");
fprintf (stdout, " --version\n\
Show program's version number and exit\n");
fprintf (stdout, " -d [DEBUG], --debug [DEBUG]\n\
Output DEBUG statements, where DEBUG_LEVEL is 0 (min) - 9 (max)\n");
fprintf (stdout, " -v, --verbose\n\
Print informational statements\n");
fprintf (stdout, " -q, --quiet\n\
Returns the exit code, error messages will be displayed\n");
fprintf (stdout, " -s, --silent\n\
Returns only the exit code; informational and error\n\
messages are not displayed\n");
fprintf (stdout, " -x XREF_FILENAME, --xref XREF_FILENAME\n\
Parse the basename to file-guid cross reference file(s)\n");
fprintf (stdout, " -f OFFSET, --offset OFFSET\n\
The offset from the start of the input file to start \n\
processing an FV\n");
fprintf (stdout, " --hash\n\
Generate HASH value of the entire PE image\n");
fprintf (stdout, " --sfo\n\
Reserved for future use\n");
}