| /*++ | |
| Copyright (c) 2002-2006 Intel Corporation. All rights reserved | |
| This program and the accompanying materials are licensed and made available | |
| under the terms and conditions of the BSD License which accompanies this | |
| distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| 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 UTILITY_NAME "GenCapsuleHdr" | |
| #define UTILITY_MAJOR_VERSION 1 | |
| #define UTILITY_MINOR_VERSION 0 | |
| #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 | |
| Version ( | |
| VOID | |
| ); | |
| 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 (UTILITY_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; | |
| } | |
| if ((strcmp(Argv[0], "-h") == 0) || (strcmp(Argv[0], "--help") == 0) || | |
| (strcmp(Argv[0], "-?") == 0) || (strcmp(Argv[0], "/?") == 0)) { | |
| Usage(); | |
| return STATUS_ERROR; | |
| } | |
| if ((strcmp(Argv[0], "-V") == 0) || (strcmp(Argv[0], "--version") == 0)) { | |
| Version(); | |
| return STATUS_ERROR; | |
| } | |
| if (Argc == 1) { | |
| 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 | |
| Version( | |
| void | |
| ) | |
| /*++ | |
| Routine Description: | |
| Print out version information for this utility. | |
| Arguments: | |
| None | |
| Returns: | |
| None | |
| --*/ | |
| { | |
| printf ("%s v%d.%d -EDK utility to create a capsule header.\n", UTILITY_NAME, UTILITY_MAJOR_VERSION, UTILITY_MINOR_VERSION); | |
| printf ("Copyright (c) 1999-2006 Intel Corporation. All rights reserved.\n"); | |
| } | |
| static | |
| void | |
| Usage ( | |
| VOID | |
| ) | |
| /*++ | |
| Routine Description: | |
| Print usage information for this utility. | |
| Arguments: | |
| None. | |
| Returns: | |
| Nothing. | |
| --*/ | |
| { | |
| int Index; | |
| static const char *Str[] = { | |
| "\nUsage: "UTILITY_NAME " {options} [CapsuleFV]", | |
| // | |
| // {FfsFileNames}", | |
| // | |
| " Options include:", | |
| " -h,--help,-?,/? to display help messages", | |
| " -V,--version to display version 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 | |
| }; | |
| Version(); | |
| for (Index = 0; Str[Index] != NULL; Index++) { | |
| fprintf (stdout, "%s\n", Str[Index]); | |
| } | |
| } |