mirror of https://github.com/acidanthera/audk.git
2675 lines
68 KiB
C
2675 lines
68 KiB
C
/*++
|
|
|
|
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 <UefiBaseTypes.h>
|
|
#include <Base.h>
|
|
#include <CommonLib.h>
|
|
|
|
#include "EfiUtilityMsgs.h"
|
|
#include "MultiPhase.h"
|
|
#include "Capsule.h"
|
|
#include "Guid/Capsule.h"
|
|
#include "FirmwareVolumeImageFormat.h"
|
|
#include "FirmwareVolumeHeader.h"
|
|
#include "FirmwareFileSystem.h" // for FV header GUID
|
|
#include "Guid/FirmwareFileSystem.h" // for FV header GUID
|
|
#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]);
|
|
}
|
|
}
|