UEFITool/parse.cpp

521 lines
20 KiB
C++
Raw Normal View History

2013-10-08 15:07:03 +08:00
/* parse.cpp
Copyright (c) 2013, Nikolaj Schlej. 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,
WITHWARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
*/
#include "parse.h"
// Implementation of GNU memmem function using Boyer-Moore-Horspool algorithm
UINT8* find_pattern(UINT8* string, UINT32 slen, CONST UINT8* pattern, UINT32 plen)
{
UINT32 scan = 0;
UINT32 bad_char_skip[256];
UINT32 last;
if (plen == 0 || !string || !pattern)
return NULL;
for (scan = 0; scan <= 255; scan++)
bad_char_skip[scan] = plen;
last = plen - 1;
for (scan = 0; scan < last; scan++)
bad_char_skip[pattern[scan]] = last - scan;
while (slen >= plen)
{
for (scan = last; string[scan] == pattern[scan]; scan--)
if (scan == 0)
return string;
slen -= bad_char_skip[string[last]];
string += bad_char_skip[string[last]];
}
return NULL;
}
UINT8 write_to_file(UINT8* buffer, CONST size_t size, const char* name, const char* extension)
{
/*char str[_MAX_PATH]; // Buffer for file name construction
FILE* file;
sprintf(str, "%s.%s", name, extension);
file = fopen((const char*) str, "wb");
if (!file) {
printf("Can't create %s file\n", str);
return ERR_FILE_OPEN;
}
if (fwrite(buffer, sizeof(UINT8), size, file) != size) {
printf("Can't write %s file\n", str);
return ERR_FILE_WRITE;
}
fclose(file);*/
return ERR_SUCCESS;
}
UINT8* parse_region(UINT8* buffer, size_t size, CONST BOOLEAN two_flash_chips, const char* region_name, CONST UINT16 region_base, CONST UINT16 region_limit)
{
UINT8 result = ERR_SUCCESS;
UINT8* region = NULL;
size_t region_size = calculate_region_size(region_base, region_limit);
if (!buffer || !size || !region_name || !region_size)
return NULL;
// Check region base to be in buffer
if (region_base >= size)
{
printf("Warning: %s region stored in descriptor is not found\n", region_name);
if (two_flash_chips)
printf("Two flash chips installed, so it could be in another flash chip\n"
"Make a dump from another flash chip and open it to view information about %s region\n", region_name);
else
printf("Error: one flash chip installed, so it is an error caused by damaged or incomplete dump\n");
printf("Warning: absence of %s region assumed\n", region_name);
return NULL;
}
// Check region to be fully present in buffer
else if (region_base + region_size > size)
{
printf("Warning: %s region stored in descriptor overlaps the end of opened file\n", region_name);
if (two_flash_chips)
printf("Two flash chips installed, so it could be in another flash chip\n"
"Make a dump from another flash chip and open it to view information about %s region\n", region_name);
else
printf("Error: One flash chip installed, so it is an error caused by damaged or incomplete dump\n");
printf("Warning: absence of %s region assumed\n", region_name);
return NULL;
}
// Calculate region address
region = calculate_address_16(buffer, region_base);
// Display information about region
printf("%s region found, size: %d (0x%08X)\n", region_name, region_size, region_size);
// Write all regions that has non-zero size to files
result = write_to_file(region, region_size, region_name, "region");
if (result){
printf("Error: Can't write %s region to file\n", region_name);
return NULL;
}
printf("%s region written to file\n", region_name);
return region;
}
UINT8* find_next_volume(UINT8* buffer, size_t buffer_size)
{
UINT8* pointer;
if (!buffer || !buffer_size)
return NULL;
pointer = find_pattern(buffer, buffer_size, FV_SIGNATURE_STR, FV_SIGNATURE_LENGTH);
if (pointer && pointer - FV_SIGNATURE_OFFSET >= buffer)
return pointer - FV_SIGNATURE_OFFSET;
return NULL;
}
UINT8 calculate_checksum_8(UINT8* buffer, size_t buffer_size)
{
UINT8 counter = 0;
while(buffer_size--)
counter += buffer[buffer_size];
return ~counter + 1;
}
UINT16 calculate_checksum_16(UINT8* buffer, size_t buffer_size)
{
UINT16 counter = 0;
while(buffer_size--)
counter += buffer[buffer_size];
return ~counter + 1;
}
// Format GUID to standard readable form
// Assumes str to have at least 37 bytes free
UINT8 format_guid(EFI_GUID* guid, char* str)
{
UINT8* guid1 = (UINT8*) guid;
UINT8* guid2 = guid1 + 4;
UINT8* guid3 = guid1 + 6;
UINT32* data1 = (UINT32*) guid1;
UINT16* data2 = (UINT16*) guid2;
UINT16* data3 = (UINT16*) guid3;
UINT8* data41 = guid1 + 8;
UINT8* data42 = guid1 + 9;
UINT8* data51 = guid1 + 10;
UINT8* data52 = guid1 + 11;
UINT8* data53 = guid1 + 12;
UINT8* data54 = guid1 + 13;
UINT8* data55 = guid1 + 14;
UINT8* data56 = guid1 + 15;
sprintf(str, "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", *data1, *data2, *data3, *data41, *data42,
*data51, *data52, *data53, *data54, *data55, *data56);
return ERR_SUCCESS;
}
VOID size_to_filesize(size_t size, UINT8* module_size)
{
module_size[2] = (UINT8) ((size) >> 16);
module_size[1] = (UINT8) ((size) >> 8);
module_size[0] = (UINT8) ((size) );
}
size_t filesize_to_size(UINT8* module_size)
{
return (module_size[2] << 16) +
(module_size[1] << 8) +
module_size[0];
}
UINT8 parse_volume(UINT8* buffer, size_t buffer_size, const char* prefix)
{
EFI_FIRMWARE_VOLUME_HEADER* volume_header;
UINT8* current_file;
char str[_MAX_PATH];
char guid[37]; // FD44820B-F1AB-41C0-AE4E-0C55556EB9BD for example
UINT8 empty_byte;
UINT8 result;
size_t header_size;
size_t file_size;
size_t files_counter = 0;
volume_header = (EFI_FIRMWARE_VOLUME_HEADER*) buffer;
if (volume_header->Signature != FV_SIGNATURE_INT)
return ERR_INVALID_VOLUME;
//!TODO: Check zero vector for code, so it could be a boot volume
// Check filesystem GUID to be known
// Check for FFS1
if (find_pattern((UINT8*)&volume_header->FileSystemGuid, sizeof(EFI_GUID), EFI_FIRMWARE_FILE_SYSTEM_GUID, sizeof(EFI_GUID)) == (UINT8*)&volume_header->FileSystemGuid) {
printf("File system: PI FFS v1\n");
// Check for FFS2
} else if (find_pattern((UINT8*)&volume_header->FileSystemGuid, sizeof(EFI_GUID), EFI_FIRMWARE_FILE_SYSTEM2_GUID, sizeof(EFI_GUID)) == (UINT8*)&volume_header->FileSystemGuid) {
printf("File system: PI FFS v2\n");
// Unknown FFS
} else {
printf("File system: unknown\nWarning: volume can't be parsed\n");
return ERR_UNKNOWN_FFS;
}
// Check attributes
// Determine erase polarity
if (volume_header->Attributes & EFI_FVB_ERASE_POLARITY)
empty_byte = 0xFF;
else
empty_byte = 0x00;
printf("Empty byte: 0x%02X\n", empty_byte);
// Check header checksum by recalculating it
if (!calculate_checksum_16(buffer, volume_header->HeaderLength)) {
printf("Warning: volume header checksum is invalid\nVolume can be inacessible for UEFI routines\n");
}
// Check for presence of extended header, only if header revision is not 1
if (volume_header->Revision > 1 && volume_header->ExtHeaderOffset) {
EFI_FIRMWARE_VOLUME_EXT_HEADER* extended_header;
extended_header = (EFI_FIRMWARE_VOLUME_EXT_HEADER*) (buffer + volume_header->ExtHeaderOffset);
result = format_guid(&extended_header->FvName, guid);
if (result)
return result;
printf("Extended header: present\n"
"Volume GUID: %s", guid);
header_size = volume_header->ExtHeaderOffset + extended_header->ExtHeaderSize;
} else {
printf("Extended header: none\n");
header_size = volume_header->HeaderLength;
}
//!TODO: Check block map to make enough space for the whole volume
// Find and parse all files in current volume
for(current_file = buffer + header_size; current_file && current_file < buffer + volume_header->FvLength - header_size;) {
EFI_FFS_FILE_HEADER* file_header = (EFI_FFS_FILE_HEADER*) current_file;
UINT8* byte;
size_t empty_bytes_counter = 0;
// Check if no more files left
for (byte = current_file; *byte == empty_byte; byte++) {
empty_bytes_counter++;
if(empty_bytes_counter >= sizeof(EFI_FFS_FILE_HEADER))
{
printf("No more files left in current volume\n");
return ERR_SUCCESS;
}
}
result = format_guid(&file_header->Name, guid);
if (result)
return result;
printf("FFS file %s found\n", guid);
// Constructing file name
sprintf(str, "%s.%d.%s", prefix, files_counter, guid);
// Getting file size
file_size = filesize_to_size(file_header->Size);
// Store current volume to file
result = write_to_file(current_file, file_size,
str, "file");
if (result){
printf("Error: Can't write FFS file to file\n");
return result;
}
printf("FFS file %s stored to file\n", guid);
//!TODO: Parse current file
// Parsed
printf("FFS file %s parsed\n", guid);
current_file += ALIGN_8(file_size);
files_counter++;
}
return ERR_SUCCESS;
}
UINT8 parse_bios_space(UINT8* buffer, size_t buffer_size)
{
UINT8* current_pointer = NULL;
UINT8* previous_pointer = NULL;
size_t previous_volume_size = 0;
EFI_FIRMWARE_VOLUME_HEADER* volume_header = NULL;
UINT32 files_counter = 0;
size_t rest_size = 0;
UINT8 result = 0;
char str[_MAX_PATH];
// Search for all firmware volume headers
for (current_pointer = buffer, previous_pointer = NULL; current_pointer && current_pointer - buffer <= buffer_size;) {
rest_size = buffer_size - (current_pointer - buffer);
// Search for next firmware volume
//!TODO: add checks for incomplete file
previous_pointer = current_pointer;
current_pointer = find_next_volume(current_pointer, rest_size);
if (current_pointer) {
// Check for padding between previous and current volumes
if (previous_pointer + previous_volume_size < current_pointer) {
sprintf(str, "%d", files_counter);
result = write_to_file(previous_pointer + previous_volume_size,
current_pointer - previous_pointer - previous_volume_size,
str, "padding");
if (result){
printf("Error: Can't write padding to file\n");
return result;
}
printf("Padding between firmware volumes stored to file\n\n");
files_counter++;
}
volume_header = (EFI_FIRMWARE_VOLUME_HEADER*) current_pointer;
printf("Firmware volume found\n");
// Check if volume overlaps the end if buffer
if (current_pointer + volume_header->FvLength > buffer + buffer_size) {
printf("Error: Volume overlaps the end of input buffer.\n");
return ERR_INVALID_VOLUME;
}
// Constructing volume name
sprintf(str, "%d", files_counter);
// Store current volume to file
result = write_to_file(current_pointer, volume_header->FvLength,
str, "volume");
if (result){
printf("Error: Can't write volume to file\n");
return result;
}
printf("Volume stored to file\n");
// Parse volume
result = parse_volume(current_pointer, volume_header->FvLength, str);
if (result)
return result;
printf("Firmware volume parsed\n\n");
// Move to next volume
current_pointer += volume_header->FvLength;
files_counter++;
}
else {// Previous_pointer was the last one (or there was none at all)
// Check for padding between previous volume and end of buffer
if (previous_pointer + previous_volume_size < buffer + buffer_size) {
char str[256];
sprintf(str, "%d", files_counter);
result = write_to_file(previous_pointer + previous_volume_size,
buffer_size + buffer - previous_pointer,
str, "padding");
if (result){
printf("Error: Can't write padding to file\n");
return result;
}
printf("Padding between last firmware volume and end of buffer stored to file\n\n");
files_counter++;
}
}
}
return ERR_SUCCESS;
}
UINT8 parse_buffer(UINT8* buffer, size_t buffer_size)
{
UINT8* original_buffer = buffer;
size_t original_size = buffer_size;
size_t capsule_header_size = 0;
size_t flash_image_size = 0;
FLASH_DESCRIPTOR_HEADER* descriptor_header = NULL;
UINT8 result = ERR_SUCCESS;
// Check buffer to be valid
if (!buffer || !buffer_size)
return ERR_INVALID_PARAMETER;
// Check buffer for being normal EFI capsule header
if (find_pattern(buffer, sizeof(EFI_GUID), EFI_CAPSULE_GUID, sizeof(EFI_GUID)) == buffer) {
EFI_CAPSULE_HEADER* capsule_header = (EFI_CAPSULE_HEADER*) buffer;
printf("EFI Capsule header found\n"
"Header size: %d (0x%08X)\n"
"Header flags: %08X\n"
"Image size: %d (0x%08X)\n\n",
capsule_header->HeaderSize,
capsule_header->HeaderSize,
capsule_header->Flags,
capsule_header->CapsuleImageSize,
capsule_header->CapsuleImageSize);
capsule_header_size = capsule_header->HeaderSize;
}
// Check buffer for being extended Aptio capsule header
else if (find_pattern(buffer, sizeof(EFI_GUID), APTIO_CAPSULE_GUID, sizeof(EFI_GUID)) == buffer) {
APTIO_CAPSULE_HEADER* aptio_capsule_header = (APTIO_CAPSULE_HEADER*) buffer;
printf("AMI Aptio extended capsule header found\n"
"Header size: %d (0x%08X)\n"
"Header flags: %08X\n"
"Image size: %d (0x%08X)\n\n",
aptio_capsule_header->RomImageOffset,
aptio_capsule_header->RomImageOffset,
aptio_capsule_header->CapsuleHeader.Flags,
aptio_capsule_header->CapsuleHeader.CapsuleImageSize - aptio_capsule_header->RomImageOffset,
aptio_capsule_header->CapsuleHeader.CapsuleImageSize - aptio_capsule_header->RomImageOffset);
//!TODO: add more information about extended headed here
capsule_header_size = aptio_capsule_header->RomImageOffset;
}
// Skip capsule header
buffer += capsule_header_size;
flash_image_size = buffer_size - capsule_header_size;
// Consider buffer to be normal flash image without any headers now
// Check buffer for being Intel flash descriptor
descriptor_header = (FLASH_DESCRIPTOR_HEADER*) buffer;
// Check descriptor signature
if (descriptor_header->Signature == FLASH_DESCRIPTOR_SIGNATURE) {
FLASH_DESCRIPTOR_MAP* descriptor_map;
FLASH_DESCRIPTOR_COMPONENT_SECTION* component_section;
FLASH_DESCRIPTOR_REGION_SECTION* region_section;
FLASH_DESCRIPTOR_MASTER_SECTION* master_section;
// Vector of 16 0xFF for sanity check
CONST UINT8 FFVector[] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
// Store the beginning of descriptor as descriptor base address
UINT8* descriptor = buffer;
UINT8* bios_region = NULL;
printf("Intel flash descriptor found\n");
// Check for buffer size to be greater or equal to descriptor region size
if (flash_image_size < FLASH_DESCRIPTOR_SIZE) {
printf("Error: Input file is smaller then mininum descriptor size of 4KB\nAborting\n");
return ERR_INVALID_FLASH_DESCRIPTOR;
}
//Write descriptor to desc.bin
result = write_to_file(descriptor, FLASH_DESCRIPTOR_SIZE, "DESC", "region");
if (result){
printf("Error: Can't write descriptor region to file\nAborting\n");
return result;
}
printf("Descriptor region written to file\n");
// Check FFVector to consist 16 0xFF, as sanity check
if (find_pattern(buffer, 16, FFVector, 16) != buffer)
printf("Warning: Descriptor start vector has something but 0xFF bytes. That's strange\n");
// Skip descriptor header
buffer += sizeof(FLASH_DESCRIPTOR_HEADER);
// Parse descriptor map
descriptor_map = (FLASH_DESCRIPTOR_MAP*) buffer;
component_section = (FLASH_DESCRIPTOR_COMPONENT_SECTION*) calculate_address_8(descriptor, descriptor_map->ComponentBase);
region_section = (FLASH_DESCRIPTOR_REGION_SECTION*) calculate_address_8(descriptor, descriptor_map->RegionBase);
master_section = (FLASH_DESCRIPTOR_MASTER_SECTION*) calculate_address_8(descriptor, descriptor_map->MasterBase);
printf("\nParsing descriptor map\n"
"Flash chips: %d\n"
"Flash regions: %d\n"
"Flash masters: %d\n"
"PCH straps: %d\n"
"PROC straps: %d\n"
"ICC table entries: %d\n",
descriptor_map->NumberOfFlashChips + 1,
descriptor_map->NumberOfRegions + 1,
descriptor_map->NumberOfMasters + 1,
descriptor_map->NumberOfPchStraps,
descriptor_map->NumberOfProcStraps,
descriptor_map->NumberOfIccTableEntries);
printf("Descriptor map parsed\n\n");
// Parse component section
//!TODO: add parsing code
// Parse master section
//!TODO: add parsing code
// Parse region section
printf("Parsing region section\n");
parse_region(descriptor, flash_image_size, descriptor_map->NumberOfFlashChips, "GBE", region_section->GbeBase, region_section->GbeLimit);
parse_region(descriptor, flash_image_size, descriptor_map->NumberOfFlashChips, "ME", region_section->MeBase, region_section->MeLimit);
bios_region = parse_region(descriptor, flash_image_size, descriptor_map->NumberOfFlashChips, "BIOS", region_section->BiosBase, region_section->BiosLimit);
parse_region(descriptor, flash_image_size, descriptor_map->NumberOfFlashChips, "PDR", region_section->PdrBase, region_section->PdrLimit);
printf("Region section parsed\n\n");
// Parsing complete
printf("Descriptor parsing complete\n\n");
// Exiting if no bios region found
if (!bios_region) {
printf("BIOS region not found\nAborting\n");
return ERR_BIOS_REGION_NOT_FOUND;
}
// BIOS region is our buffer now
buffer = bios_region;
buffer_size = calculate_region_size(region_section->BiosBase, region_section->BiosLimit);
}
else {
printf("Intel flash descriptor not found, assuming that input file is BIOS region image\n\n");
}
// We are in the beginning of BIOS space, where firmware volumes are
// Parse BIOS space
result = parse_bios_space(buffer, buffer_size);
if (result)
return result;
return ERR_SUCCESS;
}