Pull out get file util

Update CMakeLists.txt
This commit is contained in:
Ben V. Brown 2023-08-01 17:41:10 +10:00
parent 9893a49376
commit d052d2ebe4
5 changed files with 271 additions and 258 deletions

View File

@ -13,6 +13,7 @@ add_library(file_parsers
"${CMAKE_CURRENT_SOURCE_DIR}/dfu/dfu_file.c" "${CMAKE_CURRENT_SOURCE_DIR}/dfu/dfu_file.c"
"${CMAKE_CURRENT_SOURCE_DIR}/dfu/dfu_crc.c" "${CMAKE_CURRENT_SOURCE_DIR}/dfu/dfu_crc.c"
"${CMAKE_CURRENT_SOURCE_DIR}/parse_file.c" "${CMAKE_CURRENT_SOURCE_DIR}/parse_file.c"
"${CMAKE_CURRENT_SOURCE_DIR}/get_file_contents.c"
) )
target_include_directories(file_parsers PUBLIC target_include_directories(file_parsers PUBLIC

View File

@ -3,46 +3,48 @@
// //
#include "dfu_file.h" #include "dfu_file.h"
#define DFU_SUFFIX_LENGTH 16 #define DFU_SUFFIX_LENGTH 16
#define LMDFU_PREFIX_LENGTH 8 #define LMDFU_PREFIX_LENGTH 8
#define LPCDFU_PREFIX_LENGTH 16 #define LPCDFU_PREFIX_LENGTH 16
struct dfu_file { struct dfu_file {
/* File name */ /* File name */
const char* name; const char* name;
/* Pointer to file loaded into memory */ /* Pointer to file loaded into memory */
const uint8_t* firmware; const uint8_t* firmware;
/* Different sizes */ /* Different sizes */
struct { struct {
off_t total; off_t total;
off_t firmware; off_t firmware;
int prefix; int prefix;
int suffix; int suffix;
} size; } size;
/* From prefix fields */ /* From prefix fields */
uint32_t lmdfu_address; uint32_t lmdfu_address;
/* From prefix fields */ /* From prefix fields */
uint32_t prefix_type; uint32_t prefix_type;
/* From DFU suffix fields */ /* From DFU suffix fields */
uint32_t dwCRC; uint32_t dwCRC;
uint16_t bcdDFU; uint16_t bcdDFU;
uint16_t idVendor; uint16_t idVendor;
uint16_t idProduct; uint16_t idProduct;
uint16_t bcdDevice; uint16_t bcdDevice;
}; };
enum prefix_type { enum prefix_type {
ZERO_PREFIX, ZERO_PREFIX,
DFUSE_PREFIX, DFUSE_PREFIX,
LMDFU_PREFIX, LMDFU_PREFIX,
LPCDFU_UNENCRYPTED_PREFIX LPCDFU_UNENCRYPTED_PREFIX
}; };
struct dfu_file parse_dfu_suffix(const uint8_t* file_contents, struct dfu_file parse_dfu_suffix(const uint8_t* file_contents,
size_t file_contents_length); size_t file_contents_length);
ssize_t parse_target(const uint8_t* data, uint8_t* out_ealt, ssize_t parse_target(const uint8_t* data,
uint8_t** out_data, size_t* out_data_size, uint8_t* out_ealt,
uint8_t** out_data,
size_t* out_data_size,
size_t* out_data_address); size_t* out_data_address);
ssize_t get_file_contents(const char* file_path_on_disk, ssize_t get_file_contents(const char* file_path_on_disk,
uint8_t** file_contents); uint8_t** file_contents);
@ -64,259 +66,209 @@ ssize_t get_file_contents(const char* file_path_on_disk,
* free(payload); * free(payload);
*/ */
int int dfu_file_parse(const char* file_path_on_disk,
dfu_file_parse(const char* file_path_on_disk, uint8_t** payload, uint8_t** payload,
size_t* payload_length, size_t* payload_address) { size_t* payload_length,
uint8_t* dfu_file_contents = NULL; size_t* payload_address) {
ssize_t file_size uint8_t* dfu_file_contents = NULL;
= get_file_contents(file_path_on_disk, &dfu_file_contents); ssize_t file_size = get_file_contents(file_path_on_disk, &dfu_file_contents);
if (file_size <= 0 || dfu_file_contents == NULL) { if (file_size <= 0 || dfu_file_contents == NULL) {
return -1; return -1;
}
// Parse DFU data
struct dfu_file dfu_info = parse_dfu_suffix(dfu_file_contents, file_size);
// Check if its for a BL* chip
// if (dfu_info.idVendor != 0x28E9) {
// free(dfu_file_contents);
// return -1;
// }
// Okay we have done validation, walk firmware and extract the blob and the
// offset
size_t data_consumed = 0;
while (data_consumed < dfu_info.size.firmware) {
uint8_t ealt = 0;
uint8_t* blob = NULL;
size_t blob_size = 0;
size_t blob_address = 0;
ssize_t res = parse_target(dfu_info.firmware + data_consumed, &ealt, &blob,
&blob_size, &blob_address);
if (res < 0) {
break;
} }
// Parse DFU data if (ealt == 0 && blob_size > 0) {
struct dfu_file dfu_info = parse_dfu_suffix(dfu_file_contents, file_size); // Firmware slot, lets prep this and return
// Check if its for a BL* chip *payload = calloc(blob_size, 1);
// if (dfu_info.idVendor != 0x28E9) { *payload_length = blob_size;
// free(dfu_file_contents); *payload_address = blob_address;
// return -1; memcpy(*payload, blob, blob_size);
// } free(dfu_file_contents);
// Okay we have done validation, walk firmware and extract the blob and the return 1;
// offset
size_t data_consumed = 0;
while (data_consumed < dfu_info.size.firmware) {
uint8_t ealt = 0;
uint8_t* blob = NULL;
size_t blob_size = 0;
size_t blob_address = 0;
ssize_t res = parse_target(dfu_info.firmware + data_consumed, &ealt,
&blob, &blob_size, &blob_address);
if (res < 0) {
break;
}
if (ealt == 0 && blob_size > 0) {
// Firmware slot, lets prep this and return
*payload = calloc(blob_size, 1);
*payload_length = blob_size;
*payload_address = blob_address;
memcpy(*payload, blob, blob_size);
free(dfu_file_contents);
return 1;
}
data_consumed += res;
} }
data_consumed += res;
}
return 0; return 0;
} }
// Read next target, output data+size+alt. Returns bytes consumed // Read next target, output data+size+alt. Returns bytes consumed
ssize_t ssize_t parse_target(const uint8_t* data,
parse_target(const uint8_t* data, uint8_t* out_ealt, uint8_t** out_data, uint8_t* out_ealt,
size_t* out_data_size, size_t* out_data_address) { uint8_t** out_data,
if (data == NULL || out_ealt == NULL || out_data == NULL size_t* out_data_size,
|| out_data_size == NULL) { size_t* out_data_address) {
return -99; if (data == NULL || out_ealt == NULL || out_data == NULL ||
} out_data_size == NULL) {
if (data[0] != 'T' || data[1] != 'a') { return -99;
return -1; }
} if (data[0] != 'T' || data[1] != 'a') {
return -1;
}
*out_ealt = data[6]; *out_ealt = data[6];
uint8_t* tdata = data + 6 + 1 + 4 + 255; uint8_t* tdata = data + 6 + 1 + 4 + 255;
uint32_t len_tdata = *((uint32_t*)tdata); uint32_t len_tdata = *((uint32_t*)tdata);
tdata += 4;
uint32_t num_images = *((uint32_t*)tdata);
tdata += 4;
ssize_t blob_length = 6 + 1 + 4 + 255 + 8 + len_tdata;
// Now read all the image blobs from this target
for (int i = 0; i < num_images; i++) {
uint32_t address = *((uint32_t*)tdata);
tdata += 4; tdata += 4;
uint32_t num_images = *((uint32_t*)tdata); uint32_t len = *((uint32_t*)tdata);
tdata += 4; tdata += 4;
ssize_t blob_length = 6 + 1 + 4 + 255 + 8 + len_tdata; *out_data = tdata;
// Now read all the image blobs from this target *out_data_size = len;
for (int i = 0; i < num_images; i++) { *out_data_address = address;
uint32_t address = *((uint32_t*)tdata);
tdata += 4;
uint32_t len = *((uint32_t*)tdata);
tdata += 4;
*out_data = tdata;
*out_data_size = len;
*out_data_address = address;
return blob_length;
// tdata+=len;
}
return blob_length; return blob_length;
// tdata+=len;
}
return blob_length;
} }
static int static int probe_prefix(struct dfu_file* file) {
probe_prefix(struct dfu_file* file) { const uint8_t* prefix = file->firmware;
const uint8_t* prefix = file->firmware; file->size.prefix = 0;
file->size.prefix = 0; if (file->size.total < LMDFU_PREFIX_LENGTH)
if (file->size.total < LMDFU_PREFIX_LENGTH) return 1;
return 1; if (prefix[0] == 'D' && prefix[1] == 'f' && prefix[2] == 'u' &&
if (prefix[0] == 'D' && prefix[1] == 'f' && prefix[2] == 'u' prefix[3] == 'S' && prefix[4] == 'e') {
&& prefix[3] == 'S' && prefix[4] == 'e') { // DfuSe header
// DfuSe header // https://sourceforge.net/p/dfu-util/dfu-util/ci/master/tree/dfuse-pack.py#l110
// https://sourceforge.net/p/dfu-util/dfu-util/ci/master/tree/dfuse-pack.py#l110
file->size.prefix = 11; file->size.prefix = 11;
file->prefix_type = DFUSE_PREFIX; file->prefix_type = DFUSE_PREFIX;
uint8_t numTargets = prefix[10]; uint8_t numTargets = prefix[10];
printf("Number DFU Targets: %d\n", numTargets); printf("Number DFU Targets: %d\n", numTargets);
} }
if ((prefix[0] == 0x01) && (prefix[1] == 0x00)) { if ((prefix[0] == 0x01) && (prefix[1] == 0x00)) {
uint32_t payload_length = (prefix[7] << 24) | (prefix[6] << 16) uint32_t payload_length =
| (prefix[5] << 8) | prefix[4]; (prefix[7] << 24) | (prefix[6] << 16) | (prefix[5] << 8) | prefix[4];
uint32_t expected_payload_length = (uint32_t)file->size.total uint32_t expected_payload_length =
- LMDFU_PREFIX_LENGTH (uint32_t)file->size.total - LMDFU_PREFIX_LENGTH - file->size.suffix;
- file->size.suffix; if (payload_length != expected_payload_length)
if (payload_length != expected_payload_length) return 1;
return 1; file->prefix_type = LMDFU_PREFIX;
file->prefix_type = LMDFU_PREFIX; file->size.prefix = LMDFU_PREFIX_LENGTH;
file->size.prefix = LMDFU_PREFIX_LENGTH; file->lmdfu_address = 1024 * ((prefix[3] << 8) | prefix[2]);
file->lmdfu_address = 1024 * ((prefix[3] << 8) | prefix[2]); } else if (((prefix[0] & 0x3f) == 0x1a) && ((prefix[1] & 0x3f) == 0x3f)) {
} else if (((prefix[0] & 0x3f) == 0x1a) && ((prefix[1] & 0x3f) == 0x3f)) { file->prefix_type = LPCDFU_UNENCRYPTED_PREFIX;
file->prefix_type = LPCDFU_UNENCRYPTED_PREFIX; file->size.prefix = LPCDFU_PREFIX_LENGTH;
file->size.prefix = LPCDFU_PREFIX_LENGTH; }
}
if (file->size.prefix + file->size.suffix > file->size.total) if (file->size.prefix + file->size.suffix > file->size.total)
return 1; return 1;
return 0; return 0;
} }
struct dfu_file struct dfu_file parse_dfu_suffix(const uint8_t* file_contents,
parse_dfu_suffix(const uint8_t* file_contents, const size_t file_contents_length) {
const size_t file_contents_length) { // This is nearly 1:1 based on
// This is nearly 1:1 based on // https://sourceforge.net/p/dfu-util/dfu-util/ci/master/tree/src/dfu_file.c#l368
// https://sourceforge.net/p/dfu-util/dfu-util/ci/master/tree/src/dfu_file.c#l368 struct dfu_file output;
struct dfu_file output; memset(&output, 0, sizeof(output));
memset(&output, 0, sizeof(output)); output.firmware = file_contents;
output.firmware = file_contents; output.size.total = (off_t)file_contents_length;
output.size.total = (off_t)file_contents_length; /* Check for possible DFU file suffix by trying to parse one */
/* Check for possible DFU file suffix by trying to parse one */
uint32_t crc = 0xffffffff; uint32_t crc = 0xffffffff;
const uint8_t* dfu_suffix; const uint8_t* dfu_suffix;
int missing_suffix = 0; int missing_suffix = 0;
const char* reason; const char* reason;
if (file_contents_length < DFU_SUFFIX_LENGTH) { if (file_contents_length < DFU_SUFFIX_LENGTH) {
reason = "File too short for DFU suffix"; reason = "File too short for DFU suffix";
missing_suffix = 1; missing_suffix = 1;
goto checked; goto checked;
} }
dfu_suffix = file_contents + file_contents_length - DFU_SUFFIX_LENGTH; dfu_suffix = file_contents + file_contents_length - DFU_SUFFIX_LENGTH;
if (dfu_suffix[10] != 'D' || dfu_suffix[9] != 'F' if (dfu_suffix[10] != 'D' || dfu_suffix[9] != 'F' || dfu_suffix[8] != 'U') {
|| dfu_suffix[8] != 'U') { reason = "Invalid DFU suffix signature";
reason = "Invalid DFU suffix signature"; missing_suffix = 1;
missing_suffix = 1; goto checked;
goto checked; }
} // Calculate contents CRC32
// Calculate contents CRC32 for (int i = 0; i < file_contents_length - 4; i++) {
for (int i = 0; i < file_contents_length - 4; i++) { crc = crc32_byte(crc, file_contents[i]);
crc = crc32_byte(crc, file_contents[i]); }
}
output.dwCRC = (dfu_suffix[15] << 24) + (dfu_suffix[14] << 16) output.dwCRC = (dfu_suffix[15] << 24) + (dfu_suffix[14] << 16) +
+ (dfu_suffix[13] << 8) + dfu_suffix[12]; (dfu_suffix[13] << 8) + dfu_suffix[12];
if (output.dwCRC != crc) { if (output.dwCRC != crc) {
reason = "DFU suffix CRC does not match"; reason = "DFU suffix CRC does not match";
missing_suffix = 1; missing_suffix = 1;
goto checked; goto checked;
} }
/* At this point we believe we have a DFU suffix /* At this point we believe we have a DFU suffix
so we require further checks to succeed */ so we require further checks to succeed */
output.bcdDFU = (dfu_suffix[7] << 8) + dfu_suffix[6]; output.bcdDFU = (dfu_suffix[7] << 8) + dfu_suffix[6];
output.size.suffix = dfu_suffix[11]; output.size.suffix = dfu_suffix[11];
if (output.size.suffix < DFU_SUFFIX_LENGTH) { if (output.size.suffix < DFU_SUFFIX_LENGTH) {
fprintf(stderr, "Unsupported DFU suffix length %d", fprintf(stderr, "Unsupported DFU suffix length %d", output.size.suffix);
output.size.suffix); }
}
if (output.size.suffix > file_contents_length) { if (output.size.suffix > file_contents_length) {
fprintf(stderr, "Invalid DFU suffix length %d", output.size.suffix); fprintf(stderr, "Invalid DFU suffix length %d", output.size.suffix);
} }
output.idVendor = (dfu_suffix[5] << 8) + dfu_suffix[4]; output.idVendor = (dfu_suffix[5] << 8) + dfu_suffix[4];
output.idProduct = (dfu_suffix[3] << 8) + dfu_suffix[2]; output.idProduct = (dfu_suffix[3] << 8) + dfu_suffix[2];
output.bcdDevice = (dfu_suffix[1] << 8) + dfu_suffix[0]; output.bcdDevice = (dfu_suffix[1] << 8) + dfu_suffix[0];
checked: checked:
const int res = probe_prefix(&output); const int res = probe_prefix(&output);
if (output.size.prefix) { if (output.size.prefix) {
const uint8_t* data = file_contents; const uint8_t* data = file_contents;
if (output.prefix_type == DFUSE_PREFIX) { if (output.prefix_type == DFUSE_PREFIX) {
} else if (output.prefix_type == LMDFU_PREFIX) { } else if (output.prefix_type == LMDFU_PREFIX) {
printf("Possible TI Stellaris DFU prefix with " printf(
"the following properties\n" "Possible TI Stellaris DFU prefix with "
"Address: 0x%08x\n" "the following properties\n"
"Payload length: %d\n", "Address: 0x%08x\n"
output.lmdfu_address, "Payload length: %d\n",
data[4] | (data[5] << 8) | (data[6] << 16) output.lmdfu_address,
| (data[7] << 24)); data[4] | (data[5] << 8) | (data[6] << 16) | (data[7] << 24));
} else if (output.prefix_type == LPCDFU_UNENCRYPTED_PREFIX) { } else if (output.prefix_type == LPCDFU_UNENCRYPTED_PREFIX) {
printf("Possible unencrypted NXP LPC DFU prefix with " printf(
"the following properties\n" "Possible unencrypted NXP LPC DFU prefix with "
"Payload length: %d kiByte\n", "the following properties\n"
data[2] >> 1 | (data[3] << 7)); "Payload length: %d kiByte\n",
} else { data[2] >> 1 | (data[3] << 7));
fprintf(stderr, "Unknown DFU prefix type"); } else {
} fprintf(stderr, "Unknown DFU prefix type");
output.firmware
= output.firmware + output.size.prefix; // shift past prefix
} }
output.size.firmware output.firmware =
= output.size.total - (output.size.suffix + output.size.prefix); output.firmware + output.size.prefix; // shift past prefix
return output; }
output.size.firmware =
output.size.total - (output.size.suffix + output.size.prefix);
return output;
} }
// Returns file size _or_ negative on error
ssize_t
get_file_contents(const char* file_path_on_disk, uint8_t** file_contents) {
size_t read_count;
size_t file_size = 0;
size_t read_total = 0;
FILE* f;
if (file_contents == NULL) {
return -99;
}
f = fopen(file_path_on_disk, "rb");
if (f <= 0) {
fprintf(stderr, "Could not open file %s for reading",
file_path_on_disk);
return -1;
}
fseek(f, 0, SEEK_END);
file_size = ftell(f);
fseek(f, 0, SEEK_SET);
*file_contents = calloc(file_size, sizeof(uint8_t));
while (read_total < file_size) {
size_t to_read = file_size - read_total;
/* read() limit on Linux, slightly below MAX_INT on Windows */
if (to_read > 0x7ffff000)
to_read = 0x7ffff000;
read_count = fread((*file_contents) + read_total, 1, to_read, f);
if (read_count == 0)
break;
// If error and not end of file, break
if (read_count == -1 && !feof(f))
break;
read_total += read_count;
}
if (read_total != file_size) {
fprintf(stderr, "Could only read %lld of %lld bytes from %s",
(long long)read_total, (long long)file_size,
file_path_on_disk);
return -1;
}
fclose(f);
return (ssize_t)file_size;
}

View File

@ -0,0 +1,47 @@
#include <stdio.h>
#include "parse_file.h"
// Returns file size _or_ negative on error
ssize_t get_file_contents(const char* file_path_on_disk,
uint8_t** file_contents) {
size_t read_count;
size_t file_size = 0;
size_t read_total = 0;
FILE* f;
if (file_contents == NULL) {
return -99;
}
f = fopen(file_path_on_disk, "rb");
if (f <= 0) {
fprintf(stderr, "Could not open file %s for reading", file_path_on_disk);
return -1;
}
fseek(f, 0, SEEK_END);
file_size = ftell(f);
fseek(f, 0, SEEK_SET);
*file_contents = calloc(file_size, sizeof(uint8_t));
while (read_total < file_size) {
size_t to_read = file_size - read_total;
/* read() limit on Linux, slightly below MAX_INT on Windows */
if (to_read > 0x7ffff000)
to_read = 0x7ffff000;
read_count = fread((*file_contents) + read_total, 1, to_read, f);
if (read_count == 0)
break;
// If error and not end of file, break
if (read_count == -1 && !feof(f))
break;
read_total += read_count;
}
if (read_total != file_size) {
fprintf(stderr, "Could only read %lld of %lld bytes from %s",
(long long)read_total, (long long)file_size, file_path_on_disk);
return -1;
}
fclose(f);
return (ssize_t)file_size;
}

View File

@ -1,5 +1,6 @@
#include "parse_file.h" #include "parse_file.h"
#include <string.h> #include <string.h>
#include "bin_file.h"
#include "dfu_file.h" #include "dfu_file.h"
const char* get_filename_ext(const char* filename) { const char* get_filename_ext(const char* filename) {
@ -13,17 +14,23 @@ int parse_firmware_file(const char* file_path_on_disk,
parsed_firmware_file_t* parsed_results) { parsed_firmware_file_t* parsed_results) {
// Switchcase on the extension of the file // Switchcase on the extension of the file
const char* ext = get_filename_ext(file_path_on_disk); const char* ext = get_filename_ext(file_path_on_disk);
int res = PARSED_ERROR_INVALID_FILETYPE;
if (strncmp(ext, "dfu", 3) == 0 || strncmp(ext, "DFU", 3) == 0) { if (strncmp(ext, "dfu", 3) == 0 || strncmp(ext, "DFU", 3) == 0) {
printf("Input file identified as a .dfu file\r\n"); printf("Input file identified as a .dfu file\r\n");
// Handle as a .dfu file // Handle as a .dfu file
return dfu_file_parse("test.dfu", &parsed_results->payload, res = dfu_file_parse("test.dfu", &parsed_results->payload,
&parsed_results->payload_length, &parsed_results->payload_length,
&parsed_results->payload_address); &parsed_results->payload_address);
} else if (strncmp(ext, "bin", 3) == 0 || strncmp(ext, "BIN", 3) == 0) { } else if (strncmp(ext, "bin", 3) == 0 || strncmp(ext, "BIN", 3) == 0) {
printf("Input file identified as a .bin file\r\n"); printf("Input file identified as a .bin file\r\n");
// Raw binary file // Raw binary file
res = bin_file_parse("test.dfu", &parsed_results->payload,
&parsed_results->payload_length,
&parsed_results->payload_address);
} }
// TODO: Hex files? // If we wanted to support hex files, here would be where
return PARSED_ERROR_INVALID_FILETYPE;
parsed_results->needs_boot_struct = parsed_results->payload_address == 0;
return res;
} }

View File

@ -1,7 +1,9 @@
#pragma once #pragma once
#include <stdio.h>
#include "parsed_firmware_file.h" #include "parsed_firmware_file.h"
#define PARSED_ERROR_INVALID_FILETYPE -0x1000 #define PARSED_ERROR_INVALID_FILETYPE -0x1000
#define PARSED_ERROR_CANT_OPEN_FILE -0x1001
#define PARSED_ERROR_TOO_BIG -0x1001 /* Input expands to be too big */ #define PARSED_ERROR_TOO_BIG -0x1001 /* Input expands to be too big */
#define PARSED_ERROR_BAD_DFU -0x1002 /* DFU file provided but not valid */ #define PARSED_ERROR_BAD_DFU -0x1002 /* DFU file provided but not valid */
@ -12,3 +14,7 @@
int parse_firmware_file(const char* file_path_on_disk, int parse_firmware_file(const char* file_path_on_disk,
parsed_firmware_file_t* parsed_results); parsed_firmware_file_t* parsed_results);
// Internal util
ssize_t get_file_contents(const char* file_path_on_disk,
uint8_t** file_contents);