blob: 617e95927ef6566d62ef88f03f11dd0a77f561ca [file] [log] [blame]
/*++
Copyright (c) 2002 - 2005, Intel Corporation. All rights reserved
This software and associated documentation (if any) is furnished
under a license and may only be used or copied in accordance
with the terms of the license. Except as permitted by such
license, no part of this software or documentation may be
reproduced, stored in a retrieval system, or transmitted in any
form or by any means without the express written consent of
Intel Corporation.
Module Name:
GenCapsuleHdr.c
Abstract:
Generate a capsule header for a file, and optionally prepend the
header to a file or list of files.
--*/
#define _UNICODE
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <Common/UefiBaseTypes.h>
#include <Common/MultiPhase.h>
#include <Common/Capsule.h>
#include <Common/FirmwareVolumeImageFormat.h>
#include <Common/FirmwareVolumeHeader.h>
#include <Common/FirmwareFileSystem.h> // for FV header GUID
#include <Guid/Capsule.h>
#include <Guid/FirmwareFileSystem.h> // for FV header GUID
#include "CommonLib.h"
#include "EfiUtilityMsgs.h"
#define MAX_PATH 256
#define PROGRAM_NAME "GenCapsuleHdr"
#define UNICODE_BACKSLASH L'\\'
#define UNICODE_FILE_START 0xFEFF
#define UNICODE_CR 0x000D
#define UNICODE_LF 0x000A
#define UNICODE_NULL 0x0000
#define UNICODE_SPACE L' '
#define UNICODE_SLASH L'/'
#define UNICODE_DOUBLE_QUOTE L'"'
#define UNICODE_A L'A'
#define UNICODE_F L'F'
#define UNICODE_Z L'Z'
#define UNICODE_a L'a'
#define UNICODE_f L'f'
#define UNICODE_z L'z'
#define UNICODE_0 L'0'
#define UNICODE_9 L'9'
#define UNICODE_TAB L'\t'
#define OEM_HEADER_STRING L"OemHeader"
#define AUTHOR_INFO_STRING L"AuthorInfo"
#define REVISION_INFO_STRING L"RevisionInfo"
#define SHORT_DESCRIPTION_STRING L"ShortDescription"
#define LONG_DESCRIPTION_STRING L"LongDescription"
#define EQUAL_STRING L"="
#define OPEN_BRACE_STRING L"{"
#define CLOSE_BRACE_STRING L"}"
#define GUID_STRING L"GUID"
#define DATA_STRING L"DATA"
#if (EFI_SPECIFICATION_VERSION >= 0x00020000)
#define UEFI_CAPSULE_HEADER_NO_FALAGS 0
#define UEFI_CAPSULE_HEADER_RESET_FALAGS CAPSULE_FLAGS_PERSIST_ACROSS_RESET
#define UEFI_CAPSULE_HEADER_ALL_FALAGS (CAPSULE_FLAGS_PERSIST_ACROSS_RESET | CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE)
#endif
typedef wchar_t WCHAR;
typedef struct _FILE_LIST {
struct _FILE_LIST *Next;
INT8 FileName[MAX_PATH];
} FILE_LIST;
typedef struct _SIZE_LIST {
struct _SIZE_LIST *Next;
UINT32 Size;
} SIZE_LIST;
typedef struct {
INT8 FileName[MAX_PATH];
WCHAR *FileBuffer;
WCHAR *FileBufferPtr;
UINT32 FileSize;
FILE *FilePtr;
BOOLEAN EndOfFile;
UINT32 LineNum;
} SOURCE_FILE;
//
// Here's all our globals.
//
static struct {
BOOLEAN Dump;
BOOLEAN Verbose;
BOOLEAN JoinMode;
INT8 ScriptFileName[MAX_PATH];
INT8 OutputFileName[MAX_PATH];
FILE_LIST *FileList;
FILE *OutFptr;
SIZE_LIST *SizeList;
SIZE_LIST *LastSize;
SIZE_LIST *CurrentSize;
} mOptions;
static EFI_GUID mEfiCapsuleHeaderGuid = EFI_CAPSULE_GUID;
void
CreateGuid (
EFI_GUID *Guid
);
static
STATUS
ProcessArgs (
int Argc,
char *Argv[]
);
static
void
SkipWhiteSpace (
SOURCE_FILE *SourceFile
);
static
STATUS
GetHexValue (
SOURCE_FILE *SourceFile,
UINT32 *Value,
UINT32 NumDigits
);
static
BOOLEAN
GetSplitFileName (
INT8 *BaseFileName,
INT8 *NewFileName,
UINT32 SequenceNumber
);
static
STATUS
SplitCapsule (
INT8 *CapsuleFileName
);
static
void
Usage (
VOID
);
static
void
DumpCapsule (
VOID
);
static
STATUS
JoinCapsule (
VOID
);
static
STATUS
DumpCapsuleHeaderStrings (
UINT8 *SectionName,
WCHAR *Buffer
);
static
STATUS
CheckFirmwareVolumeHeader (
INT8 *FileName,
INT8 *Buffer,
UINT32 BufferSize
);
static
BOOLEAN
IsToken (
SOURCE_FILE *File,
WCHAR *Token
);
static
BOOLEAN
GetNumber (
INT8 *Str,
UINT32 *Value
);
static
STATUS
ProcessScriptFile (
INT8 *ScriptFileName,
FILE *OutFptr,
EFI_CAPSULE_HEADER *CapsuleHeader
);
static
STATUS
ParseCapsuleInfo (
SOURCE_FILE *SourceFile,
FILE *OutFptr,
WCHAR *SectionName
);
static
STATUS
CreateCapsule (
VOID
);
static
STATUS
ParseOemInfo (
SOURCE_FILE *SourceFile,
FILE *OutFptr
);
static
BOOLEAN
IsWhiteSpace (
WCHAR Char
);
static
BOOLEAN
EndOfFile (
SOURCE_FILE *File
);
int
main (
int Argc,
char *Argv[]
)
/*++
Routine Description:
Call the routine to process the command-line arguments, then
dispatch to the appropriate function.
Arguments:
Standard C main() argc and argv.
Returns:
0 if successful
nonzero otherwise
--*/
// GC_TODO: Argc - add argument and description to function comment
// GC_TODO: ] - add argument and description to function comment
{
STATUS Status;
FILE_LIST *NextFile;
//
// Specify our program name to the error printing routines.
//
SetUtilityName (PROGRAM_NAME);
//
// Process the command-line arguments
//
Status = ProcessArgs (Argc, Argv);
if (Status == STATUS_SUCCESS) {
if (mOptions.Dump) {
DumpCapsule ();
} else if (mOptions.JoinMode) {
JoinCapsule ();
} else {
CreateCapsule ();
}
}
//
// Cleanup
//
while (mOptions.FileList != NULL) {
NextFile = mOptions.FileList->Next;
free (mOptions.FileList);
mOptions.FileList = NextFile;
}
while (mOptions.SizeList != NULL) {
mOptions.CurrentSize = mOptions.SizeList->Next;
free (mOptions.SizeList);
mOptions.SizeList = mOptions.CurrentSize;
}
return GetUtilityStatus ();
}
static
STATUS
CreateCapsule (
VOID
)
/*++
Routine Description:
GC_TODO: Add function description
Arguments:
None
Returns:
GC_TODO: add return values
--*/
{
FILE *InFptr;
FILE_LIST *FileList;
INT8 *Buffer;
UINT32 Size;
EFI_CAPSULE_HEADER CapsuleHeader;
UINT8 Zero;
UINT8 FirstFile;
UINT32 CapsuleHeaderSize;
long InsertedBlockMapEntryOffset;
EFI_FV_BLOCK_MAP_ENTRY InsertedBlockMapEntry;
UINT64 FirmwareVolumeSize;
long FileSize;
EFI_FIRMWARE_VOLUME_HEADER FVHeader;
Buffer = NULL;
InFptr = NULL;
FirmwareVolumeSize = 0;
CapsuleHeaderSize = 0;
InsertedBlockMapEntryOffset = 0;
memset (&InsertedBlockMapEntry, 0, sizeof (EFI_FV_BLOCK_MAP_ENTRY));
memset (&FVHeader, 0, sizeof (EFI_FIRMWARE_VOLUME_HEADER));
if ((mOptions.OutFptr = fopen (mOptions.OutputFileName, "wb")) == NULL) {
Error (NULL, 0, 0, mOptions.OutputFileName, "failed to open output file for writing");
return STATUS_ERROR;
}
memset ((char *) &CapsuleHeader, 0, sizeof (CapsuleHeader));
memcpy ((void *) &CapsuleHeader.CapsuleGuid, (void *) &mEfiCapsuleHeaderGuid, sizeof (EFI_GUID));
CapsuleHeader.HeaderSize = sizeof (EFI_CAPSULE_HEADER);
CapsuleHeader.CapsuleImageSize = sizeof (EFI_CAPSULE_HEADER);
if (mOptions.ScriptFileName[0] != 0) {
if (ProcessScriptFile (mOptions.ScriptFileName, mOptions.OutFptr, &CapsuleHeader) != STATUS_SUCCESS) {
goto Done;
}
} else {
//
// Insert a default capsule header
#if (EFI_SPECIFICATION_VERSION >= 0x00020000)
CapsuleHeader.HeaderSize = sizeof (EFI_CAPSULE_HEADER);
CapsuleHeader.Flags = UEFI_CAPSULE_HEADER_ALL_FALAGS;
#endif
CapsuleHeader.OffsetToCapsuleBody = sizeof (EFI_CAPSULE_HEADER);
if (fwrite ((void *) &CapsuleHeader, sizeof (CapsuleHeader), 1, mOptions.OutFptr) != 1) {
Error (NULL, 0, 0, mOptions.OutputFileName, "failed to write to file");
goto Done;
}
}
CapsuleHeaderSize = CapsuleHeader.OffsetToCapsuleBody;
//
// Now copy the contents of any other files specified on the command
// line to the output file. Files must be FFS files, which are aligned
// on 8-byte boundaries. Don't align the first file, since it's the start
// of the image once the capsule header has been removed.
//
FileList = mOptions.FileList;
FirstFile = 1;
Zero = 0;
while (FileList != NULL) {
if ((InFptr = fopen (FileList->FileName, "rb")) == NULL) {
Error (NULL, 0, 0, FileList->FileName, "failed to open file for reading");
goto Done;
}
//
// Allocate a buffer into which we can read the file.
//
fseek (InFptr, 0, SEEK_END);
Size = ftell (InFptr);
rewind (InFptr);
Buffer = (char *) malloc (Size);
if (Buffer == NULL) {
Error (__FILE__, __LINE__, 0, FileList->FileName, "failed to allocate buffer to read file into");
goto Done;
}
if (fread ((void *) Buffer, Size, 1, InFptr) != 1) {
Error (NULL, 0, 0, FileList->FileName, "failed to read file contents");
goto Done;
}
if (Size > 0) {
//
// Align the write of the first bytes from the file if not the first file
//
if (FirstFile) {
//
// First file must be a firmware volume. Double-check, and then insert
// an additional block map entry so we can add more files from the command line
//
if (CheckFirmwareVolumeHeader (FileList->FileName, Buffer, Size) != STATUS_SUCCESS) {
goto Done;
}
//
// Save a copy of the firmware volume header for later
//
memcpy (&FVHeader, Buffer, sizeof (EFI_FIRMWARE_VOLUME_HEADER));
FirmwareVolumeSize = FVHeader.FvLength;
if (FileList->Next != NULL) {
//
// Copy the firmware volume header
//
InsertedBlockMapEntryOffset = CapsuleHeaderSize + FVHeader.HeaderLength;
if (fwrite (Buffer, FVHeader.HeaderLength, 1, mOptions.OutFptr) != 1) {
Error (NULL, 0, 0, mOptions.OutputFileName, "failed to write to file");
goto Done;
}
if (fwrite (&InsertedBlockMapEntry, sizeof (EFI_FV_BLOCK_MAP_ENTRY), 1, mOptions.OutFptr) != 1) {
Error (NULL, 0, 0, mOptions.OutputFileName, "failed to write to file");
goto Done;
}
if (fwrite (
Buffer + FVHeader.HeaderLength,
Size - FVHeader.HeaderLength,
1,
mOptions.OutFptr
) != 1) {
Error (NULL, 0, 0, mOptions.OutputFileName, "failed to write to file");
goto Done;
}
} else {
//
// Copy the file contents as-is
//
if (fwrite ((void *) Buffer, Size, 1, mOptions.OutFptr) != 1) {
Error (NULL, 0, 0, mOptions.OutputFileName, "failed to write to file");
goto Done;
}
}
} else {
while ((ftell (mOptions.OutFptr) - CapsuleHeaderSize) & 0x07) {
if (fwrite ((void *) &Zero, 1, 1, mOptions.OutFptr) != 1) {
Error (NULL, 0, 0, mOptions.OutputFileName, "failed to write to file");
goto Done;
}
}
if (fwrite ((void *) Buffer, Size, 1, mOptions.OutFptr) != 1) {
Error (NULL, 0, 0, mOptions.OutputFileName, "failed to write to file");
goto Done;
}
}
FirstFile = 0;
}
free (Buffer);
Buffer = NULL;
fclose (InFptr);
InFptr = NULL;
FileList = FileList->Next;
}
Done:
if (Buffer != NULL) {
free (Buffer);
}
if (InFptr != NULL) {
fclose (InFptr);
}
//
// If we inserted an additional block map entry, then fix it up. Fix up the
// FV header as well to reflect our new size.
//
if (InsertedBlockMapEntryOffset != 0) {
FileSize = ftell (mOptions.OutFptr);
InsertedBlockMapEntry.NumBlocks = 1;
InsertedBlockMapEntry.BlockLength = (UINT32) ((UINT64) FileSize - (UINT64) CapsuleHeaderSize - FirmwareVolumeSize - sizeof (EFI_FV_BLOCK_MAP_ENTRY));
fseek (mOptions.OutFptr, InsertedBlockMapEntryOffset, SEEK_SET);
fwrite (&InsertedBlockMapEntry, sizeof (EFI_FV_BLOCK_MAP_ENTRY), 1, mOptions.OutFptr);
//
// Fix up the firmware volume header and write it out
//
fseek (mOptions.OutFptr, CapsuleHeaderSize, SEEK_SET);
FVHeader.FvLength = FileSize - CapsuleHeaderSize;
FVHeader.HeaderLength += sizeof (EFI_FV_BLOCK_MAP_ENTRY);
fwrite (&FVHeader, sizeof (EFI_FIRMWARE_VOLUME_HEADER) - sizeof (EFI_FV_BLOCK_MAP_ENTRY), 1, mOptions.OutFptr);
//
// Reposition to the end of the file
//
}
//
// Close files and free the global string lists we allocated memory for
//
if (mOptions.OutFptr != NULL) {
//
// We should now know the full capsule image size. Update the header and write it again.
//
fseek (mOptions.OutFptr, 0, SEEK_END);
Size = ftell (mOptions.OutFptr);
CapsuleHeader.CapsuleImageSize = Size;
fseek (mOptions.OutFptr, 0, SEEK_SET);
if (fwrite ((void *) &CapsuleHeader, sizeof (CapsuleHeader), 1, mOptions.OutFptr) != 1) {
Error (NULL, 0, 0, mOptions.OutputFileName, "failed to write to file");
}
fseek (mOptions.OutFptr, 0, SEEK_END);
fclose (mOptions.OutFptr);
mOptions.OutFptr = NULL;
}
//
// If they are doing split capsule output, then split it up now.
//
if ((mOptions.Dump == 0) && (GetUtilityStatus () == STATUS_SUCCESS) && (mOptions.SizeList != NULL)) {
SplitCapsule (mOptions.OutputFileName);
}
return STATUS_SUCCESS;
}
static
STATUS
ProcessScriptFile (
INT8 *ScriptFileName,
FILE *OutFptr,
EFI_CAPSULE_HEADER *CapsuleHeader
)
/*++
Routine Description:
Parse a capsule header script file.
Arguments:
ScriptFileName - name of script file to parse
OutFptr - output to dump binary data
CapsuleHeader - capsule header to update with size info
of parsed fields in the script file
Returns:
STATUS_SUCCESS - if all went well
--*/
{
#if 0
STATUS Status;
SOURCE_FILE SourceFile;
WCHAR *WScriptFileName;
BOOLEAN InComment;
if (fwrite (CapsuleHeader, sizeof (EFI_CAPSULE_HEADER), 1, OutFptr) != 1) {
Error (NULL, 0, 0, "failed to write capsule header to output file", NULL);
return STATUS_ERROR;
}
memset (&SourceFile, 0, sizeof (SOURCE_FILE));
strcpy (SourceFile.FileName, ScriptFileName);
Status = STATUS_ERROR;
//
// Open the input unicode script file and read it into a buffer
//
WScriptFileName = (WCHAR *) malloc ((strlen (ScriptFileName) + 1) * sizeof (WCHAR));
if (WScriptFileName == NULL) {
Error (__FILE__, __LINE__, 0, "failed to allocate memory", NULL);
return STATUS_ERROR;
}
swprintf (WScriptFileName, L"%S", ScriptFileName);
if ((SourceFile.FilePtr = _wfopen (WScriptFileName, L"r")) == NULL) {
free (WScriptFileName);
Error (NULL, 0, 0, ScriptFileName, "failed to open script file for reading");
goto Done;
}
free (WScriptFileName);
fseek (SourceFile.FilePtr, 0, SEEK_END);
SourceFile.FileSize = ftell (SourceFile.FilePtr);
rewind (SourceFile.FilePtr);
SourceFile.FileBuffer = (WCHAR *) malloc (SourceFile.FileSize + sizeof (WCHAR));
if (SourceFile.FileBuffer == NULL) {
Error (__FILE__, __LINE__, 0, ScriptFileName, "failed to allocate memory to read in file contents");
goto Done;
}
if (fread (SourceFile.FileBuffer, SourceFile.FileSize, 1, SourceFile.FilePtr) != 1) {
Error (NULL, 0, 0, ScriptFileName, "failed to read file contents");
goto Done;
}
SourceFile.FileBufferPtr = SourceFile.FileBuffer;
SourceFile.LineNum = 1;
if (SourceFile.FileBuffer[0] != UNICODE_FILE_START) {
Error (ScriptFileName, 1, 0, "file does not appear to be a unicode file", NULL);
goto Done;
}
SourceFile.FileBufferPtr++;
SourceFile.FileBuffer[SourceFile.FileSize / sizeof (WCHAR)] = 0;
//
// Walk the source file buffer and replace all carriage returns with 0 so
// we can print from the file contents on parse errors.
//
InComment = 0;
while (!EndOfFile (&SourceFile)) {
if (SourceFile.FileBufferPtr[0] == UNICODE_CR) {
SourceFile.FileBufferPtr[0] = 0;
InComment = 0;
} else if (SourceFile.FileBufferPtr[0] == UNICODE_LF) {
InComment = 0;
} else if (InComment) {
SourceFile.FileBufferPtr[0] = UNICODE_SPACE;
} else if ((SourceFile.FileBufferPtr[0] == UNICODE_SLASH) && (SourceFile.FileBufferPtr[1] == UNICODE_SLASH)) {
InComment = 1;
SourceFile.FileBufferPtr[0] = UNICODE_SPACE;
}
SourceFile.FileBufferPtr++;
}
//
// Reposition to the start of the file, but skip over the unicode file start
//
SourceFile.FileBufferPtr = SourceFile.FileBuffer;
SourceFile.FileBufferPtr++;
SourceFile.EndOfFile = 0;
CapsuleHeader->OffsetToOemDefinedHeader = ftell (OutFptr);
//
// Parse the OEM bytes
//
if (ParseOemInfo (&SourceFile, OutFptr) != STATUS_SUCCESS) {
goto Done;
}
//
// Parse the author information
//
CapsuleHeader->OffsetToAuthorInformation = ftell (OutFptr);
if (ParseCapsuleInfo (&SourceFile, OutFptr, AUTHOR_INFO_STRING) != STATUS_SUCCESS) {
goto Done;
}
//
// Parse the revision information
//
CapsuleHeader->OffsetToRevisionInformation = ftell (OutFptr);
if (ParseCapsuleInfo (&SourceFile, OutFptr, REVISION_INFO_STRING) != STATUS_SUCCESS) {
goto Done;
}
//
// Parse the short description
//
CapsuleHeader->OffsetToShortDescription = ftell (OutFptr);
if (ParseCapsuleInfo (&SourceFile, OutFptr, SHORT_DESCRIPTION_STRING) != STATUS_SUCCESS) {
goto Done;
}
//
// Parse the long description
//
CapsuleHeader->OffsetToLongDescription = ftell (OutFptr);
if (ParseCapsuleInfo (&SourceFile, OutFptr, LONG_DESCRIPTION_STRING) != STATUS_SUCCESS) {
goto Done;
}
//
// Better be end of contents
//
SkipWhiteSpace (&SourceFile);
if (!EndOfFile (&SourceFile)) {
Error (ScriptFileName, SourceFile.LineNum, 0, NULL, "expected end-of-file, not %.20S", SourceFile.FileBufferPtr);
goto Done;
}
CapsuleHeader->OffsetToCapsuleBody = ftell (OutFptr);
rewind (OutFptr);
fwrite (CapsuleHeader, sizeof (EFI_CAPSULE_HEADER), 1, OutFptr);
fseek (OutFptr, 0, SEEK_END);
Status = STATUS_SUCCESS;
Done:
if (SourceFile.FilePtr != NULL) {
fclose (SourceFile.FilePtr);
}
if (SourceFile.FileBuffer != NULL) {
free (SourceFile.FileBuffer);
}
return Status;
#endif
return STATUS_SUCCESS;
}
//
// Parse the OEM data of format:
// OemInfo {
// GUID = 12345676-1234-1234-123456789ABC
// DATA = 0x01, 0x02, 0x03...
// }
//
static
STATUS
ParseOemInfo (
SOURCE_FILE *SourceFile,
FILE *OutFptr
)
/*++
Routine Description:
GC_TODO: Add function description
Arguments:
SourceFile - GC_TODO: add argument description
OutFptr - GC_TODO: add argument description
Returns:
GC_TODO: add return values
--*/
{
long OemHeaderOffset;
UINT32 Data;
EFI_CAPSULE_OEM_HEADER OemHeader;
STATUS Status;
UINT32 DigitCount;
WCHAR *SaveFilePos;
UINT8 ByteData;
Status = STATUS_ERROR;
memset (&OemHeader, 0, sizeof (EFI_CAPSULE_OEM_HEADER));
OemHeaderOffset = ftell (OutFptr);
OemHeader.HeaderSize = sizeof (EFI_CAPSULE_OEM_HEADER);
if (fwrite (&OemHeader, sizeof (EFI_CAPSULE_OEM_HEADER), 1, OutFptr) != 1) {
Error (NULL, 0, 0, "failed to write OEM header to output file", NULL);
goto Done;
}
if (!IsToken (SourceFile, OEM_HEADER_STRING)) {
Error (
SourceFile->FileName,
SourceFile->LineNum,
0,
NULL,
"expected %S, not %.20S",
OEM_HEADER_STRING,
SourceFile->FileBufferPtr
);
goto Done;
}
if (!IsToken (SourceFile, EQUAL_STRING)) {
Error (
SourceFile->FileName,
SourceFile->LineNum,
0,
NULL,
"expected %S, not %.20S",
EQUAL_STRING,
SourceFile->FileBufferPtr
);
goto Done;
}
if (!IsToken (SourceFile, OPEN_BRACE_STRING)) {
Error (
SourceFile->FileName,
SourceFile->LineNum,
0,
NULL,
"expected %S, not %.20S",
OPEN_BRACE_STRING,
SourceFile->FileBufferPtr
);
goto Done;
}
//
// Look for: GUID = xxxxxxx-xxxx-xxxx-xxxxxxxxxxxxx
//
if (!IsToken (SourceFile, GUID_STRING)) {
Error (
SourceFile->FileName,
SourceFile->LineNum,
0,
NULL,
"expected %S, not %.20S",
GUID_STRING,
SourceFile->FileBufferPtr
);
goto Done;
}
if (!IsToken (SourceFile, EQUAL_STRING)) {
Error (
SourceFile->FileName,
SourceFile->LineNum,
0,
NULL,
"expected %S, not %.20S",
EQUAL_STRING,
SourceFile->FileBufferPtr
);
goto Done;
}
//
// Parse the xxxxxxxx-xxxx-xxxx-xxxx portion of the GUID
//
SkipWhiteSpace (SourceFile);
if (GetHexValue (SourceFile, &Data, 8) != STATUS_SUCCESS) {
Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid GUID", NULL);
goto Done;
}
OemHeader.OemGuid.Data1 = Data;
if (!IsToken (SourceFile, L"-")) {
Error (
SourceFile->FileName,
SourceFile->LineNum,
0,
NULL,
"expected dash in GUID, not %S",
SourceFile->FileBufferPtr
);
goto Done;
}
//
// Get 3 word values
//
for (DigitCount = 0; DigitCount < 3; DigitCount++) {
if (GetHexValue (SourceFile, &Data, 4) != STATUS_SUCCESS) {
Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid GUID", NULL);
goto Done;
}
switch (DigitCount) {
case 0:
OemHeader.OemGuid.Data2 = (UINT16) Data;
break;
case 1:
OemHeader.OemGuid.Data3 = (UINT16) Data;
break;
case 2:
OemHeader.OemGuid.Data4[1] = (UINT8) Data;
OemHeader.OemGuid.Data4[0] = (UINT8) (Data >> 8);
break;
}
if (!IsToken (SourceFile, L"-")) {
Error (
SourceFile->FileName,
SourceFile->LineNum,
0,
NULL,
"expected dash in GUID, not %S",
SourceFile->FileBufferPtr
);
goto Done;
}
}
//
// Pick up the last 6 bytes of the GUID
//
SaveFilePos = SourceFile->FileBufferPtr;
for (DigitCount = 0; DigitCount < 6; DigitCount++) {
if (GetHexValue (SourceFile, &Data, 2) != STATUS_SUCCESS) {
Error (SourceFile->FileName, SourceFile->LineNum, 0, "invalid GUID", NULL);
goto Done;
}
OemHeader.OemGuid.Data4[DigitCount + 2] = (UINT8) Data;
}
//
// Now read raw OEM data bytes. May or may not be present.
// DATA = 0x01, 0x02, 0x02...
//
if (IsToken (SourceFile, CLOSE_BRACE_STRING)) {
Status = STATUS_SUCCESS;
goto Done;
}
if (!IsToken (SourceFile, DATA_STRING)) {
Error (
SourceFile->FileName,
SourceFile->LineNum,
0,
NULL,
"expected %S, not %.20S",
DATA_STRING,
SourceFile->FileBufferPtr
);
goto Done;
}
if (!IsToken (SourceFile, EQUAL_STRING)) {
Error (
SourceFile->FileName,
SourceFile->LineNum,
0,
NULL,
"expected %S, not %.20S",
EQUAL_STRING,
SourceFile->FileBufferPtr
);
goto Done;
}
while (!EndOfFile (SourceFile)) {
if (IsToken (SourceFile, CLOSE_BRACE_STRING)) {
Status = STATUS_SUCCESS;
goto Done;
}
if (IsToken (SourceFile, L"0x")) {
if (swscanf (SourceFile->FileBufferPtr, L"%x", &Data) != 1) {
Error (
SourceFile->FileName,
SourceFile->LineNum,
0,
NULL,
"expected hex byte value, not %.20S",
SourceFile->FileBufferPtr
);
goto Done;
}
if (Data &~0xFF) {
Error (
SourceFile->FileName,
SourceFile->LineNum,
0,
NULL,
"expected byte hex byte value at %.20S",
SourceFile->FileBufferPtr
);
goto Done;
}
//
// Skip over the hex digits, then write the data
//
while (iswxdigit (SourceFile->FileBufferPtr[0])) {
SourceFile->FileBufferPtr++;
}
ByteData = (UINT8) Data;
if (fwrite (&ByteData, 1, 1, OutFptr) != 1) {
Error (NULL, 0, 0, "failed to write OEM data to output file", NULL);
goto Done;
}
OemHeader.HeaderSize++;
//
// Optional comma
//
IsToken (SourceFile, L",");
} else {
Error (
SourceFile->FileName,
SourceFile->LineNum,
0,
NULL,
"expected hex OEM data, not %.20S",
SourceFile->FileBufferPtr
);
goto Done;
}
}
if (EndOfFile (SourceFile)) {
Error (
SourceFile->FileName,
SourceFile->LineNum,
0,
NULL,
"expected %S close to OEM header data",
CLOSE_BRACE_STRING
);
goto Done;
}
Status = STATUS_SUCCESS;
Done:
//
// re-write the oem header if no errors
//
if (Status == STATUS_SUCCESS) {
fseek (OutFptr, OemHeaderOffset, SEEK_SET);
if (fwrite (&OemHeader, sizeof (EFI_CAPSULE_OEM_HEADER), 1, OutFptr) != 1) {
Error (NULL, 0, 0, "failed to write OEM header to output file", NULL);
goto Done;
}
fseek (OutFptr, 0, SEEK_END);
}
return Status;
}
static
STATUS
ParseCapsuleInfo (
SOURCE_FILE *SourceFile,
FILE *OutFptr,
WCHAR *SectionName
)
// GC_TODO: function comment should start with '/*++'
//
// GC_TODO: function comment is missing 'Routine Description:'
// GC_TODO: function comment is missing 'Arguments:'
// GC_TODO: function comment is missing 'Returns:'
// GC_TODO: SourceFile - add argument and description to function comment
// GC_TODO: OutFptr - add argument and description to function comment
// GC_TODO: SectionName - add argument and description to function comment
// Parse: eng "string " "parts"
// spa "string " "parts"
// Write out: "eng string parts\0spa string parts\0\0
//
{
STATUS Status;
int StringCount;
WCHAR Zero;
WCHAR Spacebar;
Status = STATUS_ERROR;
Zero = 0;
Spacebar = UNICODE_SPACE;
if (!IsToken (SourceFile, SectionName)) {
Error (
SourceFile->FileName,
SourceFile->LineNum,
0,
NULL,
"expected %S, not %.20S",
SectionName,
SourceFile->FileBufferPtr
);
goto Done;
}
if (!IsToken (SourceFile, EQUAL_STRING)) {
Error (
SourceFile->FileName,
SourceFile->LineNum,
0,
NULL,
"expected %S, not %.20S",
EQUAL_STRING,
SourceFile->FileBufferPtr
);
goto Done;
}
if (!IsToken (SourceFile, OPEN_BRACE_STRING)) {
Error (
SourceFile->FileName,
SourceFile->LineNum,
0,
NULL,
"expected %S, not %.20S",
OPEN_BRACE_STRING,
SourceFile->FileBufferPtr
);
goto Done;
}
while (!EndOfFile (SourceFile)) {
if (IsToken (SourceFile, CLOSE_BRACE_STRING)) {
break;
}
//
// Look for language identifier (3 lowercase chars)
//
if ((SourceFile->FileBufferPtr[0] >= UNICODE_a) &&
(SourceFile->FileBufferPtr[0] <= UNICODE_z) &&
(SourceFile->FileBufferPtr[1] >= UNICODE_a) &&
(SourceFile->FileBufferPtr[1] <= UNICODE_z) &&
(SourceFile->FileBufferPtr[2] >= UNICODE_a) &&
(SourceFile->FileBufferPtr[2] <= UNICODE_z) &&
IsWhiteSpace (SourceFile->FileBufferPtr[3])
) {
//
// Write the 3 chars followed by a spacebar, and then look for opening quote
//
fwrite (SourceFile->FileBufferPtr, sizeof (WCHAR), 1, OutFptr);
SourceFile->FileBufferPtr++;
fwrite (SourceFile->FileBufferPtr, sizeof (WCHAR), 1, OutFptr);
SourceFile->FileBufferPtr++;
fwrite (SourceFile->FileBufferPtr, sizeof (WCHAR), 1, OutFptr);
SourceFile->FileBufferPtr++;
fwrite (&Spacebar, sizeof (WCHAR), 1, OutFptr);
StringCount = 0;
while (IsToken (SourceFile, L"\"")) {
StringCount++;
while (!EndOfFile (SourceFile)) {
if (SourceFile->FileBufferPtr[0] == UNICODE_DOUBLE_QUOTE) {
SourceFile->FileBufferPtr++;
break;
} else if ((SourceFile->FileBufferPtr[0] == UNICODE_LF) || (SourceFile->FileBufferPtr[0] == 0)) {
Error (SourceFile->FileName, SourceFile->LineNum, 0, "missing closing quote on string", NULL);
goto Done;
} else {
fwrite (SourceFile->FileBufferPtr, sizeof (WCHAR), 1, OutFptr);
SourceFile->FileBufferPtr++;
}
}
}
if (StringCount == 0) {
Error (
SourceFile->FileName,
SourceFile->LineNum,
0,
NULL,
"expected quoted string, not %.20S",
SourceFile->FileBufferPtr
);
goto Done;
}
//
// This string's null terminator
//
fwrite (&Zero, sizeof (WCHAR), 1, OutFptr);
} else {
Error (
SourceFile->FileName,
SourceFile->LineNum,
0,
NULL,
"expected valid language identifer, not %.20S",
SourceFile->FileBufferPtr
);
goto Done;
}
}
//
// Double null terminator
//
fwrite (&Zero, sizeof (WCHAR), 1, OutFptr);
Status = STATUS_SUCCESS;
Done:
return Status;
}
static
STATUS
SplitCapsule (
INT8 *CapsuleFileName
)
/*++
Routine Description:
We've created an entire capsule image. Now split it up into the
size pieces they requested.
Arguments:
CapsuleFileName - name of an existing capsule file on disk
Returns:
STATUS_SUCCESS - if no problems
Notes:
This implementation reads in the entire capsule image from
disk, then overwrites the original file with the first
in the series.
--*/
{
#if 0
EFI_CAPSULE_HEADER *CapHdr;
EFI_CAPSULE_HEADER Hdr;
FILE *CapFptr;
FILE *OutFptr;
UINT32 SizeLeft;
UINT32 CurrentSize;
UINT32 DataSize;
UINT32 SequenceNumber;
INT8 *Buffer;
INT8 FileName[MAX_PATH];
STATUS Status;
UINT32 FileSize;
//
// Figure out the total size, then rewind the input file and
// read the entire thing in
//
if ((CapFptr = fopen (CapsuleFileName, "rb")) == NULL) {
Error (NULL, 0, 0, CapsuleFileName, "failed to open capsule image for reading");
return STATUS_ERROR;
}
OutFptr = NULL;
Status = STATUS_SUCCESS;
fseek (CapFptr, 0, SEEK_END);
SizeLeft = ftell (CapFptr);
fseek (CapFptr, 0, SEEK_SET);
CapHdr = (EFI_CAPSULE_HEADER *) malloc (SizeLeft);
if (CapHdr == NULL) {
Error (NULL, 0, 0, "memory allocation failure", NULL);
goto FailDone;
}
if (fread (CapHdr, SizeLeft, 1, CapFptr) != 1) {
Error (NULL, 0, 0, "failed to read capsule contents", "split failed");
goto FailDone;
}
fclose (CapFptr);
CapFptr = NULL;
//
// Get a GUID to fill in the InstanceId GUID in the header
//
CreateGuid (&CapHdr->InstanceId);
SequenceNumber = 0;
//
// If the split size is larger than the original capsule image, then
// we're done.
//
if (mOptions.SizeList->Size >= SizeLeft) {
mOptions.SizeList->Size = SizeLeft;
goto Done;
}
//
// First size has to be big enough for the original header
//
if (mOptions.SizeList->Size < CapHdr->OffsetToCapsuleBody) {
Error (NULL, 0, 0, "first split size is insufficient for the original capsule header", NULL);
goto FailDone;
}
//
// Initialize the header we'll use on all but the first part
//
memset (&Hdr, 0, sizeof (Hdr));
Hdr.CapsuleGuid = CapHdr->CapsuleGuid;
Hdr.HeaderSize = sizeof (Hdr);
Hdr.Flags = CapHdr->Flags;
Hdr.InstanceId = CapHdr->InstanceId;
Hdr.CapsuleImageSize = CapHdr->CapsuleImageSize;
Hdr.OffsetToCapsuleBody = Hdr.HeaderSize;
Hdr.SequenceNumber = 1;
//
// printf ("Created %s - 0x%X bytes\n", CapsuleFileName, mOptions.SizeList->Size);
//
Buffer = (UINT8 *) CapHdr;
//
// Walk the list of sizes and write out a capsule header, and
// then the raw capsule data.
//
// SizeLeft -= mOptions.SizeList->Size;
//
mOptions.CurrentSize = mOptions.SizeList;
while (SizeLeft) {
CurrentSize = mOptions.CurrentSize->Size;
GetSplitFileName (mOptions.OutputFileName, FileName, SequenceNumber);
if ((OutFptr = fopen (FileName, "wb")) == NULL) {
Error (NULL, 0, 0, FileName, "failed to open split file for writing");
goto FailDone;
}
if (Buffer == (UINT8 *) CapHdr) {
//
// First part -- write out original header and data
//
if (fwrite (Buffer, CurrentSize, 1, OutFptr) != 1) {
Error (NULL, 0, 0, FileName, "failed to write to split image file");
goto FailDone;
}
SizeLeft -= CurrentSize;
Buffer += CurrentSize;
DataSize = CurrentSize;
FileSize = CurrentSize;
} else {
//
// Not the first part. Write the default header, and then the raw bytes from the
// original image.
//
if (CurrentSize <= sizeof (Hdr)) {
Error (NULL, 0, 0, "split size too small for capsule header + data", "0x%X", CurrentSize);
goto FailDone;
}
DataSize = CurrentSize - sizeof (Hdr);
if (DataSize > SizeLeft) {
DataSize = SizeLeft;
}
if (fwrite (&Hdr, sizeof (Hdr), 1, OutFptr) != 1) {
Error (NULL, 0, 0, FileName, "failed to write capsule header to output file");
fclose (OutFptr);
goto FailDone;
}
if (fwrite (Buffer, DataSize, 1, OutFptr) != 1) {
Error (NULL, 0, 0, FileName, "failed to write capsule data to output file");
fclose (OutFptr);
goto FailDone;
}
Hdr.SequenceNumber++;
Buffer += DataSize;
SizeLeft -= DataSize;
FileSize = DataSize + sizeof (Hdr);
}
//
// Next size in list if there is one
//
if (mOptions.CurrentSize->Next != NULL) {
mOptions.CurrentSize = mOptions.CurrentSize->Next;
}
SequenceNumber++;
fclose (OutFptr);
OutFptr = NULL;
printf ("Created %s - 0x%X bytes (0x%X bytes of data)\n", FileName, FileSize, DataSize);
}
goto Done;
FailDone:
Status = STATUS_ERROR;
Done:
if (CapHdr != NULL) {
free (CapHdr);
}
if (CapFptr != NULL) {
fclose (CapFptr);
}
if (OutFptr != NULL) {
fclose (OutFptr);
}
return Status;
#endif
return STATUS_SUCCESS;
}
static
BOOLEAN
GetSplitFileName (
INT8 *BaseFileName,
INT8 *NewFileName,
UINT32 SequenceNumber
)
/*++
Routine Description:
GC_TODO: Add function description
Arguments:
BaseFileName - GC_TODO: add argument description
NewFileName - GC_TODO: add argument description
SequenceNumber - GC_TODO: add argument description
Returns:
GC_TODO: add return values
--*/
{
/*++
Routine Description:
Given an initial split capsule file name and a sequence number,
create an appropriate file name for this split of a capsule image.
Arguments:
BaseFileName - name of of the first split file in the series
NewFileName - output name of the split file
SequenceNumber - 0-based sequence number of split images
Returns:
TRUE - name created successfully
FALSE - otherwise
--*/
INT8 *Ptr;
INT8 *Part2Start;
UINT32 Digits;
UINT32 Len;
UINT32 BaseOffset;
//
// Work back from the end of the file name and see if there is a number somewhere
//
for (Ptr = BaseFileName + strlen (BaseFileName) - 1; (Ptr > BaseFileName) && !isdigit (*Ptr); Ptr--)
;
if ((Ptr == BaseFileName) && (!isdigit (*Ptr))) {
//
// Found no number, so just add it to the end
//
sprintf (NewFileName, "%s%d", BaseFileName, SequenceNumber);
return TRUE;
} else {
//
// Found a number. Look back to find the first digit.
//
Part2Start = Ptr + 1;
for (Digits = 1; isdigit (*Ptr) && (Ptr > BaseFileName); Ptr--, Digits++)
;
if (!isdigit (*Ptr)) {
Ptr++;
Digits--;
}
BaseOffset = atoi (Ptr);
SequenceNumber = SequenceNumber + BaseOffset;
if (Digits > 1) {
//
// Copy the first part of the original file name to the new filename
// This is the path for filenames with format path\name001.cap
//
Len = (UINT32) Ptr - (UINT32) BaseFileName;
strncpy (NewFileName, BaseFileName, Len);
sprintf (NewFileName + Len, "%0*d", Digits, SequenceNumber);
strcat (NewFileName, Part2Start);
return TRUE;
} else {
//
// Only one digit found. This is the path for filenames with
// format path\name1.cap
//
Len = (UINT32) Ptr - (UINT32) BaseFileName + 1;
strncpy (NewFileName, BaseFileName, Len);
sprintf (NewFileName + Len - 1, "%d", SequenceNumber);
strcat (NewFileName, Part2Start);
return TRUE;
}
}
}
static
BOOLEAN
IsWhiteSpace (
WCHAR Char
)
/*++
Routine Description:
GC_TODO: Add function description
Arguments:
Char - GC_TODO: add argument description
Returns:
GC_TODO: add return values
--*/
{
switch (Char) {
case UNICODE_SPACE:
case UNICODE_TAB:
case UNICODE_NULL:
case UNICODE_CR:
case UNICODE_LF:
return TRUE;
default:
return FALSE;
}
}
static
BOOLEAN
IsToken (
SOURCE_FILE *File,
WCHAR *Token
)
/*++
Routine Description:
GC_TODO: Add function description
Arguments:
File - GC_TODO: add argument description
Token - GC_TODO: add argument description
Returns:
GC_TODO: add return values
--*/
{
SkipWhiteSpace (File);
if (EndOfFile (File)) {
return FALSE;
}
if (wcsncmp (Token, File->FileBufferPtr, wcslen (Token)) == 0) {
File->FileBufferPtr += wcslen (Token);
return TRUE;
}
return FALSE;
}
static
STATUS
CheckFirmwareVolumeHeader (
INT8 *FileName,
INT8 *Buffer,
UINT32 BufferSize
)
/*++
Routine Description:
GC_TODO: Add function description
Arguments:
FileName - GC_TODO: add argument description
Buffer - GC_TODO: add argument description
BufferSize - GC_TODO: add argument description
Returns:
GC_TODO: add return values
--*/
{
EFI_FIRMWARE_VOLUME_HEADER *Hdr;
EFI_GUID FVHeaderGuid = EFI_FIRMWARE_FILE_SYSTEM_GUID;
Hdr = (EFI_FIRMWARE_VOLUME_HEADER *) Buffer;
if (Hdr->Signature != EFI_FVH_SIGNATURE) {
Error (NULL, 0, 0, FileName, "file does not appear to be a firmware volume (bad signature)");
return STATUS_ERROR;
}
if (Hdr->Revision != EFI_FVH_REVISION) {
Error (NULL, 0, 0, FileName, "unsupported firmware volume header version");
return STATUS_ERROR;
}
if (Hdr->FvLength > BufferSize) {
Error (NULL, 0, 0, FileName, "malformed firmware volume -- FvLength > file size");
return STATUS_ERROR;
}
if (memcmp (&Hdr->FileSystemGuid, &FVHeaderGuid, sizeof (EFI_GUID)) != 0) {
Error (NULL, 0, 0, FileName, "invalid FFS GUID in firmware volume header");
return STATUS_ERROR;
}
return STATUS_SUCCESS;
}
static
void
DumpCapsule (
VOID
)
/*++
Routine Description:
GC_TODO: Add function description
Arguments:
None
Returns:
GC_TODO: add return values
--*/
{
#if 0
FILE *InFptr;
FILE_LIST *FileList;
EFI_CAPSULE_HEADER CapsuleHeader;
EFI_FIRMWARE_VOLUME_HEADER FVHeader;
EFI_CAPSULE_OEM_HEADER *OemHeader;
UINT8 *BPtr;
UINT32 FileSize;
UINT32 CapsuleHeaderDataSize;
UINT8 ByteCount;
UINT8 *CapsuleHeaderData;
BOOLEAN SplitImage;
InFptr = NULL;
CapsuleHeaderData = NULL;
FileList = mOptions.FileList;
while (FileList != NULL) {
if ((InFptr = fopen (FileList->FileName, "rb")) == NULL) {
Error (NULL, 0, 0, FileList->FileName, "failed to open file for reading");
goto Done;
}
if (fread (&CapsuleHeader, sizeof (EFI_CAPSULE_HEADER), 1, InFptr) != 1) {
Error (NULL, 0, 0, FileList->FileName, "failed to read capsule header");
goto Done;
}
fseek (InFptr, 0, SEEK_END);
FileSize = ftell (InFptr);
if (CapsuleHeader.CapsuleImageSize > FileSize) {
SplitImage = TRUE;
} else {
SplitImage = FALSE;
}
printf (
"Capsule %s Size=0x%X CargoSize=0x%X\n",
FileList->FileName,
FileSize,
FileSize - CapsuleHeader.OffsetToCapsuleBody
);
printf (
" GUID %08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
CapsuleHeader.CapsuleGuid.Data1,
(UINT32) CapsuleHeader.CapsuleGuid.Data2,
(UINT32) CapsuleHeader.CapsuleGuid.Data3,
(UINT32) CapsuleHeader.CapsuleGuid.Data4[0],
(UINT32) CapsuleHeader.CapsuleGuid.Data4[1],
(UINT32) CapsuleHeader.CapsuleGuid.Data4[2],
(UINT32) CapsuleHeader.CapsuleGuid.Data4[3],
(UINT32) CapsuleHeader.CapsuleGuid.Data4[4],
(UINT32) CapsuleHeader.CapsuleGuid.Data4[5],
(UINT32) CapsuleHeader.CapsuleGuid.Data4[6],
(UINT32) CapsuleHeader.CapsuleGuid.Data4[7]
);
if (memcmp (&CapsuleHeader.CapsuleGuid, &mEfiCapsuleHeaderGuid, sizeof (EFI_GUID)) != 0) {
printf (" INVALID GUID");
}
printf ("\n");
printf (" Header size 0x%08X\n", CapsuleHeader.HeaderSize);
printf (" Flags 0x%08X\n", CapsuleHeader.Flags);
if (!SplitImage) {
printf (" Capsule image size 0x%08X\n", CapsuleHeader.CapsuleImageSize);
} else {
printf (" Capsule image size 0x%08X (split)\n", CapsuleHeader.CapsuleImageSize);
}
printf (" Sequence number %d\n", CapsuleHeader.SequenceNumber);
printf (
" InstanceId %08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X\n",
CapsuleHeader.InstanceId.Data1,
(UINT32) CapsuleHeader.InstanceId.Data2,
(UINT32) CapsuleHeader.InstanceId.Data3,
(UINT32) CapsuleHeader.InstanceId.Data4[0],
(UINT32) CapsuleHeader.InstanceId.Data4[1],
(UINT32) CapsuleHeader.InstanceId.Data4[2],
(UINT32) CapsuleHeader.InstanceId.Data4[3],
(UINT32) CapsuleHeader.InstanceId.Data4[4],
(UINT32) CapsuleHeader.InstanceId.Data4[5],
(UINT32) CapsuleHeader.InstanceId.Data4[6],
(UINT32) CapsuleHeader.InstanceId.Data4[7]
);
printf (" Offset to capsule 0x%X\n", CapsuleHeader.OffsetToCapsuleBody);
//
// Dump header data if there
//
CapsuleHeaderDataSize = CapsuleHeader.OffsetToCapsuleBody - CapsuleHeader.HeaderSize;
if (CapsuleHeaderDataSize != 0) {
CapsuleHeaderData = (UINT8 *) malloc (CapsuleHeaderDataSize);
if (CapsuleHeaderData == NULL) {
Error (
NULL,
0,
0,
"failed to allocate memory to read in capsule header data",
"0x%X bytes",
CapsuleHeaderDataSize
);
goto Done;
}
fseek (InFptr, CapsuleHeader.HeaderSize, SEEK_SET);
if (fread (CapsuleHeaderData, CapsuleHeaderDataSize, 1, InFptr) != 1) {
Error (
NULL,
0,
0,
"failed to read capsule header data contents from file",
"0x%X bytes",
CapsuleHeaderDataSize
);
goto Done;
}
//
// ************************************************************************
//
// OEM HEADER
//
// ************************************************************************
//
if (CapsuleHeader.OffsetToOemDefinedHeader != 0) {
OemHeader = (EFI_CAPSULE_OEM_HEADER *) (CapsuleHeaderData + CapsuleHeader.OffsetToOemDefinedHeader - CapsuleHeader.HeaderSize);
printf (" OEM Header\n");
printf (
" GUID %08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X\n",
OemHeader->OemGuid.Data1,
(UINT32) OemHeader->OemGuid.Data2,
(UINT32) OemHeader->OemGuid.Data3,
(UINT32) OemHeader->OemGuid.Data4[0],
(UINT32) OemHeader->OemGuid.Data4[1],
(UINT32) OemHeader->OemGuid.Data4[2],
(UINT32) OemHeader->OemGuid.Data4[3],
(UINT32) OemHeader->OemGuid.Data4[4],
(UINT32) OemHeader->OemGuid.Data4[5],
(UINT32) OemHeader->OemGuid.Data4[6],
(UINT32) OemHeader->OemGuid.Data4[7]
);
printf (" Header size: 0x%X\n", OemHeader->HeaderSize);
printf (" OEM data");
BPtr = (UINT8 *) (OemHeader + 1);
for (ByteCount = 0; ByteCount < OemHeader->HeaderSize - sizeof (EFI_CAPSULE_OEM_HEADER); ByteCount++) {
if ((ByteCount & 0x7) == 0) {
printf ("\n ");
}
printf ("%02X ", (UINT32) *BPtr);
BPtr++;
}
printf ("\n");
}
//
// ************************************************************************
//
// Author, revision, short description, and long description information
//
// ************************************************************************
//
if (CapsuleHeader.OffsetToAuthorInformation != 0) {
if (DumpCapsuleHeaderStrings (
"Author information",
(WCHAR *) (CapsuleHeaderData + CapsuleHeader.OffsetToAuthorInformation - CapsuleHeader.HeaderSize)
) != STATUS_SUCCESS) {
goto Done;
}
}
if (CapsuleHeader.OffsetToRevisionInformation != 0) {
if (DumpCapsuleHeaderStrings (
"Revision information",
(WCHAR *) (CapsuleHeaderData + CapsuleHeader.OffsetToRevisionInformation - CapsuleHeader.HeaderSize)
) != STATUS_SUCCESS) {
goto Done;
}
}
if (CapsuleHeader.OffsetToShortDescription != 0) {
if (DumpCapsuleHeaderStrings (
"Short description",
(WCHAR *) (CapsuleHeaderData + CapsuleHeader.OffsetToShortDescription - CapsuleHeader.HeaderSize)
) != STATUS_SUCCESS) {
goto Done;
}
}
if (CapsuleHeader.OffsetToLongDescription != 0) {
if (DumpCapsuleHeaderStrings (
"Long description",
(WCHAR *) (CapsuleHeaderData + CapsuleHeader.OffsetToLongDescription - CapsuleHeader.HeaderSize)
) != STATUS_SUCCESS) {
goto Done;
}
}
}
//
// If it's not a split image, or it is a split image and this is the first in the series, then
// dump the cargo volume.
//
if ((!SplitImage) || (CapsuleHeader.SequenceNumber == 0)) {
printf (" Cargo FV dump\n");
fseek (InFptr, CapsuleHeader.OffsetToCapsuleBody, SEEK_SET);
if (fread (&FVHeader, sizeof (EFI_FIRMWARE_VOLUME_HEADER), 1, InFptr) != 1) {
Error (NULL, 0, 0, FileList->FileName, "failed to read cargo FV header");
goto Done;
}
printf (" FV length 0x%X", FVHeader.FvLength);
if (FileSize - CapsuleHeader.OffsetToCapsuleBody != FVHeader.FvLength) {
if (!SplitImage) {
printf (" ERROR: expected 0x%X to jive with file size on disk", FileSize - CapsuleHeader.OffsetToCapsuleBody);
}
}
printf ("\n");
printf (" Signature 0x%X ", FVHeader.Signature);
if (FVHeader.Signature == EFI_FVH_SIGNATURE) {
printf ("_FVH\n");
} else {
printf ("INVALID\n");
}
printf (" FV header length 0x%X\n", (UINT32) FVHeader.HeaderLength);
printf (" Revision 0x%X\n", (UINT32) FVHeader.Revision);
printf ("\n");
}
FileList = FileList->Next;
}
Done:
if (InFptr != NULL) {
fclose (InFptr);
}
if (CapsuleHeaderData != NULL) {
free (CapsuleHeaderData);
}
#endif
}
static
STATUS
JoinCapsule (
VOID
)
/*++
Routine Description:
Join split capsule images into a single image. This is the
support function for the -j command-line option.
Arguments:
None.
Returns:
STATUS_SUCCESS - no problems encountered
--*/
{
#if 0
UINT32 Size;
FILE *InFptr;
FILE *OutFptr;
INT8 *Buffer;
FILE_LIST *FileList;
STATUS Status;
EFI_CAPSULE_HEADER CapHdr;
EFI_CAPSULE_HEADER *CapHdrPtr;
UINT32 SizeLeft;
UINT32 SequenceNumber;
//
// Must have at least two files for join mode
//
if ((mOptions.FileList == NULL) || (mOptions.FileList->Next == NULL)) {
Error (NULL, 0, 0, "must specify at least two file names to join", NULL);
return STATUS_ERROR;
}
//
// Open the output file
//
if ((OutFptr = fopen (mOptions.OutputFileName, "wb")) == NULL) {
Error (NULL, 0, 0, mOptions.OutputFileName, "failed to open output file for writing");
return STATUS_ERROR;
}
FileList = mOptions.FileList;
Buffer = NULL;
SequenceNumber = 0;
InFptr = NULL;
SizeLeft = 0;
while (FileList != NULL) {
if ((InFptr = fopen (FileList->FileName, "rb")) == NULL) {
Error (NULL, 0, 0, FileList->FileName, "failed to open file for reading");
goto FailDone;
}
//
// Allocate a buffer into which we can read the file.
//
fseek (InFptr, 0, SEEK_END);
Size = ftell (InFptr);
rewind (InFptr);
Buffer = (char *) malloc (Size);
if (Buffer == NULL) {
Error (__FILE__, __LINE__, 0, FileList->FileName, "failed to allocate buffer to read file into");
goto FailDone;
}
CapHdrPtr = (EFI_CAPSULE_HEADER *) Buffer;
if (fread ((void *) Buffer, Size, 1, InFptr) != 1) {
Error (NULL, 0, 0, FileList->FileName, "failed to read file contents");
goto FailDone;
}
//
// Check the header for validity. Check size first.
//
if (Size < sizeof (EFI_CAPSULE_HEADER)) {
Error (NULL, 0, 0, FileList->FileName, "file size is insufficient for a capsule header");
goto FailDone;
}
//
// Check GUID
//
if (memcmp (&CapHdrPtr->CapsuleGuid, &mEfiCapsuleHeaderGuid, sizeof (EFI_GUID)) != 0) {
Error (NULL, 0, 0, FileList->FileName, "invalid capsule GUID");
goto FailDone;
}
//
// Check sequence number
//
if (CapHdrPtr->SequenceNumber != SequenceNumber) {
Error (
NULL,
0,
0,
FileList->FileName,
"invalid sequence number %d (expected %d)",
CapHdrPtr->SequenceNumber,
SequenceNumber
);
goto FailDone;
}
//
// If the first file, read save the capsule header
//
if (SequenceNumber == 0) {
memcpy (&CapHdr, CapHdrPtr, sizeof (EFI_CAPSULE_HEADER));
//
// Erase the InstanceId GUID
//
memset (&CapHdrPtr->InstanceId, 0, sizeof (EFI_GUID));
if (fwrite (Buffer, Size, 1, OutFptr) != 1) {
Error (NULL, 0, 0, FileList->FileName, "failed to write contents to output file");
goto FailDone;
}
if (CapHdr.CapsuleImageSize < Size) {
Error (NULL, 0, 0, FileList->FileName, "capsule image size in capsule header < image size");
goto FailDone;
}
SizeLeft = CapHdr.CapsuleImageSize - Size;
} else {
//
// Check the GUID against the first file's GUID
//
if (memcmp (&CapHdr.CapsuleGuid, &CapHdrPtr->CapsuleGuid, sizeof (EFI_GUID)) != 0) {
Error (NULL, 0, 0, FileList->FileName, "GUID does not match first file's GUID");
goto FailDone;
}
//
// Make sure we're not throwing out any header info
//
if (CapHdrPtr->OffsetToCapsuleBody > sizeof (EFI_CAPSULE_HEADER)) {
//
// Could be the split information, so just emit a warning
//
Warning (
NULL,
0,
0,
FileList->FileName,
"image appears to have additional capsule header information -- ignoring"
);
} else if (CapHdrPtr->OffsetToCapsuleBody < sizeof (EFI_CAPSULE_HEADER)) {
Error (NULL, 0, 0, FileList->FileName, "offset to capsule body in capsule header is insufficient");
goto FailDone;
}
if (fwrite (Buffer + CapHdrPtr->OffsetToCapsuleBody, Size - CapHdrPtr->OffsetToCapsuleBody, 1, OutFptr) != 1) {
Error (NULL, 0, 0, mOptions.OutputFileName, "failed to write to file");
goto FailDone;
}
if (SizeLeft < (Size - CapHdrPtr->OffsetToCapsuleBody)) {
Error (NULL, 0, 0, "sum of image sizes exceeds size specified in initial capsule header", NULL);
goto FailDone;
}
//
// printf ("FILE: %s OffsetToCapsuleBody=0x%X\n", FileList->FileName, CapHdrPtr->OffsetToCapsuleBody);
//
SizeLeft = SizeLeft - (Size - CapHdrPtr->OffsetToCapsuleBody);
}
//
// printf ("FILE: %s sizeleft=0x%X\n", FileList->FileName, SizeLeft);
//
fclose (InFptr);
InFptr = NULL;
free (Buffer);
Buffer = NULL;
FileList = FileList->Next;
SequenceNumber++;
}
if (SizeLeft) {
Error (NULL, 0, 0, "sum of capsule images is insufficient", "0x%X bytes missing", SizeLeft);
goto FailDone;
}
Status = STATUS_SUCCESS;
goto Done;
FailDone:
Status = STATUS_ERROR;
Done:
if (InFptr != NULL) {
fclose (InFptr);
}
if (OutFptr != NULL) {
fclose (OutFptr);
}
if (Buffer != NULL) {
free (Buffer);
}
return Status;
#endif
return STATUS_SUCCESS;
}
static
STATUS
DumpCapsuleHeaderStrings (
UINT8 *SectionName,
WCHAR *Buffer
)
/*++
Routine Description:
Given a pointer to string data from a capsule header, dump
the strings.
Arguments:
SectionName - name of the capsule header section to which
the string data pertains
Buffer - pointer to string data from a capsule header
Returns:
STATUS_SUCCESS - all went well
--*/
{
printf (" %s\n", SectionName);
while (*Buffer) {
printf (" Language: %S\n", Buffer);
while (*Buffer) {
Buffer++;
}
Buffer++;
while (*Buffer) {
if (wcslen (Buffer) > 60) {
printf (" %.60S\n", Buffer);
Buffer += 60;
} else {
printf (" %S\n", Buffer);
Buffer += wcslen (Buffer);
}
}
Buffer++;
}
return STATUS_SUCCESS;
}
static
STATUS
GetHexValue (
SOURCE_FILE *SourceFile,
UINT32 *Value,
UINT32 NumDigits
)
/*++
Routine Description:
Scan a hex value from the input stream.
Arguments:
SourceFile - input file contents
Value - returned value
NumDigits - number of digits to read
Returns:
STATUS_SUCCESS - if NumDigits were read from the file
STATUS_ERROR - otherwise
--*/
{
WCHAR *SaveFilePos;
UINT32 Digits;
WCHAR Nibble;
SaveFilePos = SourceFile->FileBufferPtr;
*Value = 0;
Digits = NumDigits;
while (Digits > 0) {
Nibble = SourceFile->FileBufferPtr[0];
if ((Nibble >= UNICODE_0) && (Nibble <= UNICODE_9)) {
*Value = (*Value << 4) | (Nibble - UNICODE_0);
} else if ((Nibble >= UNICODE_A) && (Nibble <= UNICODE_F)) {
*Value = (*Value << 4) | (Nibble - UNICODE_A + 0x10);
} else if ((Nibble >= UNICODE_a) && (Nibble <= UNICODE_f)) {
*Value = (*Value << 4) | (Nibble - UNICODE_a + 0x10);
} else {
Error (
SourceFile->FileName,
SourceFile->LineNum,
0,
NULL,
"expected %d valid hex nibbles at %.20S",
NumDigits,
SaveFilePos
);
return STATUS_ERROR;
}
SourceFile->FileBufferPtr++;
Digits--;
}
return STATUS_SUCCESS;
}
static
BOOLEAN
EndOfFile (
SOURCE_FILE *File
)
/*++
Routine Description:
GC_TODO: Add function description
Arguments:
File - GC_TODO: add argument description
Returns:
GC_TODO: add return values
--*/
{
if ((UINT32) File->FileBufferPtr - (UINT32) File->FileBuffer >= File->FileSize) {
File->EndOfFile = TRUE;
}
//
// Reposition to the end of the file if we went beyond
//
if (File->EndOfFile) {
File->FileBufferPtr = File->FileBuffer + File->FileSize / sizeof (WCHAR);
}
return File->EndOfFile;
}
static
void
SkipWhiteSpace (
SOURCE_FILE *SourceFile
)
/*++
Routine Description:
GC_TODO: Add function description
Arguments:
SourceFile - GC_TODO: add argument description
Returns:
GC_TODO: add return values
--*/
{
while (!EndOfFile (SourceFile)) {
switch (*SourceFile->FileBufferPtr) {
case UNICODE_NULL:
case UNICODE_CR:
case UNICODE_SPACE:
case UNICODE_TAB:
SourceFile->FileBufferPtr++;
break;
case UNICODE_LF:
SourceFile->FileBufferPtr++;
SourceFile->LineNum++;
break;
default:
return ;
}
}
}
//
// Parse a number. Possible format:
// 1234
// 1234k
// 1234K
// 1M
// 1m
// 0x100
//
static
BOOLEAN
GetNumber (
INT8 *Str,
UINT32 *Value
)
/*++
Routine Description:
GC_TODO: Add function description
Arguments:
Str - GC_TODO: add argument description
Value - GC_TODO: add argument description
Returns:
GC_TODO: add return values
--*/
{
UINT32 LValue;
*Value = 0;
LValue = 0;
if (!isdigit (Str[0])) {
return FALSE;
}
//
// Look for hex number
//
if ((Str[0] == '0') && (tolower (Str[1]) == 'x')) {
Str += 2;
if (Str[0] == 0) {
return FALSE;
}
while (Str[0]) {
if ((Str[0] >= '0') && (Str[0] <= '9')) {
LValue = (LValue << 4) | (Str[0] - '0');
} else if ((Str[0] >= 'A') && (Str[0] <= 'F')) {
LValue = (LValue << 4) | (Str[0] - 'A' + 0x10);
} else if ((Str[0] >= 'a') && (Str[0] <= 'f')) {
LValue = (LValue << 4) | (Str[0] - 'a' + 0x10);
} else {
break;
}
Str++;
}
} else {
LValue = atoi (Str);
while (isdigit (*Str)) {
Str++;
}
}
//
// If string left over, better be one character we recognize
//
if (Str[0]) {
if (Str[1]) {
return FALSE;
}
switch (Str[0]) {
case 'k':
case 'K':
LValue *= 1024;
break;
case 'm':
case 'M':
LValue *= 1024 * 1024;
break;
default:
return FALSE;
}
}
*Value = LValue;
return TRUE;
}
//
// Process the command-line arguments
//
static
STATUS
ProcessArgs (
int Argc,
char *Argv[]
)
/*++
Routine Description:
Processes command line arguments.
Arguments:
Argc - Number of command line arguments
Argv[] - Array of files input on command line
Returns:
STATUS_ERROR - Function exited with an error
STATUS_SUCCESS - Function executed successfully
--*/
{
FILE_LIST *NewFile;
FILE_LIST *LastFile;
SIZE_LIST *NewSize;
NewFile = NULL;
NewSize = NULL;
//
// Clear our globals
//
memset ((char *) &mOptions, 0, sizeof (mOptions));
//
// Skip program name
//
Argc--;
Argv++;
if (Argc == 0) {
Usage ();
return STATUS_ERROR;
}
//
// Process until no more options
//
while ((Argc > 0) && (Argv[0][0] == '-')) {
if (stricmp (Argv[0], "-script") == 0) {
//
// Check for one more arg
//
if (Argc > 1) {
//
// Save the file name
//
if (strlen (Argv[1]) >= sizeof (mOptions.ScriptFileName)) {
Error (NULL, 0, 0, NULL, "input script file name length exceeds internal buffer size");
if (NewFile != NULL) {
free (NewFile);
}
if (NewSize != NULL) {
free (NewSize);
}
return STATUS_ERROR;
}
strcpy (mOptions.ScriptFileName, Argv[1]);
} else {
Error (NULL, 0, 0, Argv[0], "missing script file name with option");
Usage ();
if (NewFile != NULL) {
free (NewFile);
}
if (NewSize != NULL) {
free (NewSize);
}
return STATUS_ERROR;
}
Argc--;
Argv++;
//
// -o outfilename -- specify output file name (required)
//
} else if (stricmp (Argv[0], "-o") == 0) {
//
// check for one more arg
//
if (Argc > 1) {
//
// Try to open the file
//
// if ((mOptions.OutFptr = fopen (Argv[1], "wb")) == NULL) {
// Error (NULL, 0, 0, Argv[1], "failed to open output file for writing");
// return STATUS_ERROR;
// }
//
strcpy (mOptions.OutputFileName, Argv[1]);
} else {
Error (NULL, 0, 0, Argv[0], "missing output filename with option");
Usage ();
if (NewFile != NULL) {
free (NewFile);
}
if (NewSize != NULL) {
free (NewSize);
}
return STATUS_ERROR;
}
Argc--;
Argv++;
} else if (stricmp (Argv[0], "-j") == 0) {
mOptions.JoinMode = TRUE;
//
// -split <size> option (multiple allowed)
//
} else if (stricmp (Argv[0], "-split") == 0) {
if (Argc > 1) {
NewSize = (SIZE_LIST *) malloc (sizeof (SIZE_LIST));
if (NewSize == NULL) {
Error (NULL, 0, 0, "memory allocation failure", NULL);
if (NewFile != NULL) {
free (NewFile);
}
if (NewSize != NULL) {
free (NewSize);
}
return STATUS_ERROR;
}
memset (NewSize, 0, sizeof (SIZE_LIST));
//
// Get the size from the next arg, and then add this size
// to our size list
//
if (!GetNumber (Argv[1], &NewSize->Size)) {
Error (NULL, 0, 0, Argv[1], "invalid split size argument");
if (NewFile != NULL) {
free (NewFile);
}
if (NewSize != NULL) {
free (NewSize);
}
return STATUS_ERROR;
}
if (mOptions.SizeList == NULL) {
mOptions.SizeList = NewSize;
mOptions.CurrentSize = NewSize;
} else {
mOptions.LastSize->Next = NewSize;
}
mOptions.LastSize = NewSize;
free (NewSize);
} else {
Error (NULL, 0, 0, Argv[0], "missing size parameter with option");
Usage ();
if (NewFile != NULL) {
free (NewFile);
}
if (NewSize != NULL) {
free (NewSize);
}
return STATUS_ERROR;
}
Argc--;
Argv++;
} else if ((stricmp (Argv[0], "-h") == 0) || (strcmp (Argv[0], "-?") == 0)) {
Usage ();
if (NewFile != NULL) {
free (NewFile);
}
if (NewSize != NULL) {
free (NewSize);
}
return STATUS_ERROR;
//
// Default minimum header
//
} else if (stricmp (Argv[0], "-dump") == 0) {
mOptions.Dump = TRUE;
} else if (stricmp (Argv[0], "-v") == 0) {
mOptions.Verbose = TRUE;
} else {
Error (NULL, 0, 0, Argv[0], "unrecognized option");
Usage ();
if (NewFile != NULL) {
free (NewFile);
}
if (NewSize != NULL) {
free (NewSize);
}
return STATUS_ERROR;
}
Argc--;
Argv++;
}
//
// Can't -j join files and -s split output capsule
//
if ((mOptions.SizeList != NULL) && (mOptions.JoinMode)) {
Error (NULL, 0, 0, "cannot specify both -j and -size", NULL);
if (NewFile != NULL) {
free (NewFile);
}
if (NewSize != NULL) {
free (NewSize);
}
return STATUS_ERROR;
}
//
// Must have specified an output file name if not -dump
//
if ((mOptions.Dump == 0) && (mOptions.OutputFileName[0] == 0)) {
Error (NULL, 0, 0, NULL, "-o OutputFileName must be specified");
Usage ();
if (NewFile != NULL) {
free (NewFile);
}
if (NewSize != NULL) {
free (NewSize);
}
return STATUS_ERROR;
}
//
// Rest of arguments are input files. The first one is a firmware
// volume image, and the rest are FFS files that are to be inserted
// into the firmware volume.
//
LastFile = NULL;
while (Argc > 0) {
NewFile = (FILE_LIST *) malloc (sizeof (FILE_LIST));
if (NewFile == NULL) {
Error (NULL, 0, 0, "memory allocation failure", NULL);
if (NewFile != NULL) {
free (NewFile);
}
if (NewSize != NULL) {
free (NewSize);
}
return STATUS_ERROR;
}
memset ((char *) NewFile, 0, sizeof (FILE_LIST));
strcpy (NewFile->FileName, Argv[0]);
if (mOptions.FileList == NULL) {
mOptions.FileList = NewFile;
} else {
if (LastFile == NULL) {
LastFile = NewFile;
} else {
LastFile->Next = NewFile;
}
}
LastFile = NewFile;
Argc--;
Argv++;
}
//
// Must have provided at least one file name
//
if (mOptions.FileList == NULL) {
Error (NULL, 0, 0, "must specify at least one file name", NULL);
Usage ();
if (NewFile != NULL) {
free (NewFile);
}
if (NewSize != NULL) {
free (NewSize);
}
return STATUS_ERROR;
}
return STATUS_SUCCESS;
}
static
void
Usage (
VOID
)
/*++
Routine Description:
Print usage information for this utility.
Arguments:
None.
Returns:
Nothing.
--*/
{
int Index;
static const char *Str[] = {
PROGRAM_NAME " -- create a capsule header",
" Usage: "PROGRAM_NAME " {options} [CapsuleFV]",
//
// {FfsFileNames}",
//
" Options include:",
" -h or -? for this help information",
" -script fname to take capsule header info from unicode script",
" file fname",
" -o fname write output to file fname (required)",
" -split size split capsule image into multiple output files",
" -dump to dump a capsule header",
" -v for verbose output\n",
" -j to join split capsule images into a single image",
"",
" CapsuleFV is the name of an existing well-formed Tiano firmware",
" volume file.",
//
// FfsFileNames are the names of one or more Tiano FFS files to",
// " insert into the output capsule image.",
//
NULL
};
for (Index = 0; Str[Index] != NULL; Index++) {
fprintf (stdout, "%s\n", Str[Index]);
}
}