/*++ Copyright (c) 2004-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: Microcode.c Abstract: Utility for working with microcode patch files in the Intel Platform Innovation Framework for EFI build environment. --*/ #include #include // for memset() #include #include // for malloc() #include "EfiUtilityMsgs.h" #include "Microcode.h" #define MAX_LINE_LEN 256 #define STATUS_IGNORE 0xA // // Structure definition for a microcode header // typedef struct { unsigned int HeaderVersion; unsigned int PatchId; unsigned int Date; unsigned int CpuId; unsigned int Checksum; unsigned int LoaderVersion; unsigned int PlatformId; unsigned int DataSize; // if 0, then TotalSize = 2048, and TotalSize field is invalid unsigned int TotalSize; // number of bytes unsigned int Reserved[3]; } MICROCODE_IMAGE_HEADER; static STATUS MicrocodeReadData ( FILE *InFptr, unsigned int *Data ); void MicrocodeConstructor ( void ) /*++ Routine Description: Constructor of module Microcode Arguments: None Returns: None --*/ { } void MicrocodeDestructor ( void ) /*++ Routine Description: Destructor of module Microcode Arguments: None Returns: None --*/ { } static STATUS MicrocodeReadData ( FILE *InFptr, unsigned int *Data ) /*++ Routine Description: Read a 32-bit microcode data value from a text file and convert to raw binary form. Arguments: InFptr - file pointer to input text file Data - pointer to where to return the data parsed Returns: STATUS_SUCCESS - no errors or warnings, Data contains valid information STATUS_ERROR - errors were encountered --*/ { char Line[MAX_LINE_LEN]; char *cptr; unsigned int ctr; Line[MAX_LINE_LEN - 1] = 0; *Data = 0; if (fgets (Line, MAX_LINE_LEN, InFptr) == NULL) { return STATUS_ERROR; } // // If it was a binary file, then it may have overwritten our null terminator // if (Line[MAX_LINE_LEN - 1] != 0) { return STATUS_ERROR; } // Strip leading white-space characters (except carriage returns) from Line // if (isspace(Line[0]) && Line[0] != '\n') { while (isspace(Line[0])) { for (ctr = 0; ctr < strlen(Line); ctr++) if (Line[ctr] != '\n') Line[ctr] = Line[ctr + 1]; } } // // Look for // dd 000000001h ; comment // dd XXXXXXXX // DD XXXXXXXXX // DD XXXXXXXXX // for (cptr = Line; *cptr && isspace(*cptr); cptr++) { } if ((tolower(cptr[0]) == 'd') && (tolower(cptr[1]) == 'd') && isspace (cptr[2])) { // // Skip blanks and look for a hex digit // cptr += 3; for (; *cptr && isspace(*cptr); cptr++) { } if (isxdigit (*cptr)) { if (sscanf (cptr, "%X", Data) != 1) { return STATUS_ERROR; } } return STATUS_SUCCESS; } if (strlen(Line) == 1) { return STATUS_IGNORE; } if (tolower(cptr[0]) == ';') { return STATUS_IGNORE; } return STATUS_ERROR; } STATUS MicrocodeParseFile ( char *InFileName, char *OutFileName ) /*++ Routine Description: Parse a microcode text file, and write the binary results to an output file. Arguments: InFileName - input text file to parse OutFileName - output file to write raw binary data from parsed input file Returns: STATUS_SUCCESS - no errors or warnings STATUS_ERROR - errors were encountered --*/ { FILE *InFptr; FILE *OutFptr; STATUS Status; MICROCODE_IMAGE_HEADER *Header; unsigned int Size; unsigned int Size2; unsigned int Data; unsigned int Checksum; char *Buffer; char *Ptr; char *OrigPtr; unsigned int TotalSize; Status = STATUS_ERROR; InFptr = NULL; OutFptr = NULL; Buffer = NULL; // // Open the input text file // if ((InFptr = fopen (InFileName, "r")) == NULL) { Error (NULL, 0, 0, InFileName, "failed to open input microcode file for reading"); return STATUS_ERROR; } // // Make two passes on the input file. The first pass is to determine how // much data is in the file so we can allocate a working buffer. Then // we'll allocate a buffer and re-read the file into the buffer for processing. // Size = 0; do { Status = MicrocodeReadData (InFptr, &Data); if (Status == STATUS_SUCCESS) { Size += sizeof (Data); } if (Status == STATUS_IGNORE) { Status = STATUS_SUCCESS; } } while (Status == STATUS_SUCCESS); // // Error if no data. // if (Size == 0) { Error (NULL, 0, 0, InFileName, "no parse-able data found in file"); goto Done; } if (Size < sizeof (MICROCODE_IMAGE_HEADER)) { Error (NULL, 0, 0, InFileName, "amount of parse-able data is insufficient to contain a microcode header"); goto Done; } // // Allocate a buffer for the data // Buffer = (char *) _malloc (Size); if (Buffer == NULL) { Error (NULL, 0, 0, "memory allocation failure", NULL); goto Done; } // // Re-read the file, storing the data into our buffer // fseek (InFptr, 0, SEEK_SET); Ptr = Buffer; OrigPtr = Ptr; do { OrigPtr = Ptr; Status = MicrocodeReadData (InFptr, &Data); if (Status == STATUS_SUCCESS) { *(unsigned int *) Ptr = Data; Ptr += sizeof (Data); } if (Status == STATUS_IGNORE) { Ptr = OrigPtr; Status = STATUS_SUCCESS; } } while (Status == STATUS_SUCCESS); // // Can't do much checking on the header because, per the spec, the // DataSize field may be 0, which means DataSize = 2000 and TotalSize = 2K, // and the TotalSize field is invalid (actually missing). Thus we can't // even verify the Reserved fields are 0. // Header = (MICROCODE_IMAGE_HEADER *) Buffer; if (Header->DataSize == 0) { TotalSize = 2048; } else { TotalSize = Header->TotalSize; } if (TotalSize != Size) { Error (NULL, 0, 0, InFileName, "file contents do not contain expected TotalSize 0x%04X", TotalSize); goto Done; } // // Checksum the contents // Ptr = Buffer; Checksum = 0; Size2 = 0; while (Size2 < Size) { Checksum += *(unsigned int *) Ptr; Ptr += 4; Size2 += 4; } if (Checksum != 0) { Error (NULL, 0, 0, InFileName, "checksum failed on file contents"); goto Done; } // // Open the output file and write the buffer contents // if ((OutFptr = fopen (OutFileName, "wb")) == NULL) { Error (NULL, 0, 0, OutFileName, "failed to open output microcode file for writing"); goto Done; } if (fwrite (Buffer, Size, 1, OutFptr) != 1) { Error (NULL, 0, 0, OutFileName, "failed to write microcode data to output file"); goto Done; } Status = STATUS_SUCCESS; Done: if (Buffer != NULL) { free (Buffer); } if (InFptr != NULL) { fclose (InFptr); } if (OutFptr != NULL) { fclose (OutFptr); if (Status == STATUS_ERROR) { remove (OutFileName); } } return Status; }