mirror of
https://github.com/LongSoft/UEFITool.git
synced 2024-11-21 23:48:22 +08:00
4afe74850d
Initial public commit
521 lines
20 KiB
C++
521 lines
20 KiB
C++
/* 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;
|
|
}
|