398 lines
11 KiB
C
398 lines
11 KiB
C
/*
|
|
* Copyright (C) 2018 Massdrop Inc.
|
|
*
|
|
* This file is part of Massdrop Loader.
|
|
*
|
|
* Massdrop Loader is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Massdrop Loader is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Massdrop Loader. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "mdloader_common.h"
|
|
#include "mdloader_parser.h"
|
|
|
|
void free_data(data_t *data)
|
|
{
|
|
if (!data)
|
|
{
|
|
printf("Error: Parser: Attempt to free NULL data!\n");
|
|
return;
|
|
}
|
|
|
|
if (data->data)
|
|
{
|
|
free(data->data);
|
|
data->data = NULL;
|
|
}
|
|
|
|
free(data);
|
|
data = NULL;
|
|
}
|
|
|
|
data_t *create_data(uint32_t data_length)
|
|
{
|
|
data_t *data = (data_t *)malloc(sizeof(data_t));
|
|
if (!data)
|
|
{
|
|
printf("ERROR: Parser: Could not allocate parser memory!\n");
|
|
return NULL;
|
|
}
|
|
|
|
data->data = (char *)malloc(data_length);
|
|
if (!data->data)
|
|
{
|
|
printf("ERROR: Parser: Could not allocate parser data memory!\n");
|
|
free_data(data);
|
|
return NULL;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
//Assuming validity checks on incoming variables have been completed prior to call
|
|
data_t *parse_bin(char *rawdata, uint32_t rawlength)
|
|
{
|
|
if (!rawdata)
|
|
{
|
|
printf("ERROR: Parser: Bin: Raw data null!\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (!rawlength)
|
|
{
|
|
printf("ERROR: Parser: Bin: Raw data length zero!\n");
|
|
return NULL;
|
|
}
|
|
|
|
data_t *data = create_data(rawlength);
|
|
|
|
if (data)
|
|
{
|
|
memcpy(data->data, rawdata, rawlength);
|
|
data->size = rawlength;
|
|
data->addr = mcu->flash_addr + bootloader_length;
|
|
}
|
|
else
|
|
printf("Error: Parser: Bin: Error creating parser storage!\n");
|
|
|
|
return data;
|
|
}
|
|
|
|
char hex_conv_error; //Cleared on conversion attempt and set to 1 if read fails
|
|
|
|
//Must check hex_conv_error != 0 for conversion error after call
|
|
char ascii_to_hex(char bh, char bl)
|
|
{
|
|
hex_conv_error = 0;
|
|
|
|
if (bh >= '0' && bh <= '9') bh -= '0';
|
|
else if (bh >= 'A' && bh <= 'F') bh -= 'A' - 0xA;
|
|
else if (bh >= 'a' && bh <= 'a') bh -= 'a' - 0xA;
|
|
else
|
|
{
|
|
hex_conv_error = 1;
|
|
return 0;
|
|
}
|
|
|
|
if (bl >= '0' && bl <= '9') bl -= '0';
|
|
else if (bl >= 'A' && bl <= 'F') bl -= 'A' - 0xA;
|
|
else if (bl >= 'a' && bl <= 'a') bl -= 'a' - 0xA;
|
|
else
|
|
{
|
|
hex_conv_error = 1;
|
|
return 0;
|
|
}
|
|
|
|
return (bh << 4) | bl;
|
|
}
|
|
|
|
//Assuming validity checks on incoming variables have been completed prior to call
|
|
data_t *parse_hex(char *rawdata, uint32_t rawlength)
|
|
{
|
|
if (!rawdata)
|
|
{
|
|
printf("ERROR: Parser: Hex: Raw data null!\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (!rawlength)
|
|
{
|
|
printf("ERROR: Parser: Hex: Raw data length zero!\n");
|
|
return NULL;
|
|
}
|
|
|
|
uint8_t first_address_set = 0;
|
|
uint32_t base_address = 0;
|
|
uint32_t start_offset;
|
|
uint8_t *rds = (uint8_t *)rawdata;
|
|
uint8_t *rde = (uint8_t *)rawdata + rawlength;
|
|
uint8_t *bindata = (uint8_t *)rawdata;
|
|
hex_record_t *hex;
|
|
uint8_t *hex_data;
|
|
uint8_t checksum;
|
|
uint8_t checksum_given;
|
|
uint16_t line = 0;
|
|
uint32_t binlength = 0;
|
|
uint8_t byte_count;
|
|
uint32_t next_address = 0;
|
|
uint32_t start_segment_address = 0;
|
|
uint8_t start_segment_address_set = 0;
|
|
while (rds < rde)
|
|
{
|
|
line++;
|
|
if (rde - rds < sizeof(hex_record_t))
|
|
{
|
|
printf("Error: Parser: Hex: Unexpected end of header! (Line %i)\n",line);
|
|
return NULL;
|
|
}
|
|
|
|
hex = (hex_record_t *)rds;
|
|
if (hex->start_code != ':')
|
|
{
|
|
printf("Error: Parser: Hex: Invalid start code! (Line %i)\n",line);
|
|
return NULL;
|
|
}
|
|
|
|
checksum = 0;
|
|
|
|
//Convert byte count to safe storage
|
|
byte_count = ascii_to_hex(*hex->byte_count, *(hex->byte_count+1));
|
|
if (hex_conv_error)
|
|
{
|
|
printf("Error: Parser: Hex: Unexpected ASCII in byte count! (Line %i)\n",line);
|
|
return NULL;
|
|
}
|
|
checksum += byte_count;
|
|
|
|
//Convert record type in place
|
|
*hex->record_type = ascii_to_hex(*hex->record_type, *(hex->record_type+1));
|
|
if (hex_conv_error)
|
|
{
|
|
printf("Error: Parser: Hex: Unexpected ASCII in record type! (Line %i)\n",line);
|
|
return NULL;
|
|
}
|
|
checksum += *hex->record_type;
|
|
|
|
//Convert address in place
|
|
*hex->address.c = ascii_to_hex(*(hex->address.c+0), *(hex->address.c+1));
|
|
if (hex_conv_error)
|
|
{
|
|
printf("Error: Parser: Hex: Unexpected ASCII in address byte 1! (Line %i)\n",line);
|
|
return NULL;
|
|
}
|
|
checksum += *hex->address.c;
|
|
*(hex->address.c+1) = ascii_to_hex(*(hex->address.c+2), *(hex->address.c+3));
|
|
if (hex_conv_error)
|
|
{
|
|
printf("Error: Parser: Hex: Unexpected ASCII in address byte 2! (Line %i)\n",line);
|
|
return NULL;
|
|
}
|
|
checksum += *(hex->address.c+1);
|
|
hex->address.i = (*hex->address.c << 8) + *(hex->address.c+1);
|
|
|
|
//Check enough data remains to parse
|
|
if (rde - rds < sizeof(hex_record_t) + (byte_count * 2) + 2)
|
|
{
|
|
printf("Error: Parser: Hex: Malformed data! (Line %i)\n",line);
|
|
return NULL;
|
|
}
|
|
|
|
hex_data = rds + sizeof(hex_record_t);
|
|
|
|
//Convert data in place
|
|
for (start_offset = 0; start_offset < byte_count * 2; start_offset += 2)
|
|
{
|
|
*(hex_data + start_offset / 2) = ascii_to_hex(*(hex_data + start_offset), *(hex_data + start_offset + 1));
|
|
if (hex_conv_error)
|
|
{
|
|
printf("Error: Parser: Hex: Unexpected ASCII in data byte! (Line %i)\n",line);
|
|
return NULL;
|
|
}
|
|
checksum += *(hex_data + start_offset / 2);
|
|
}
|
|
|
|
checksum *= -1;
|
|
|
|
//Convert checksum
|
|
checksum_given = ascii_to_hex(*(hex_data + start_offset), *(hex_data + start_offset + 1));
|
|
if (hex_conv_error)
|
|
{
|
|
printf("Error: Parser: Hex: Unexpected ASCII in checksum byte! (Line %i)\n",line);
|
|
return NULL;
|
|
}
|
|
if (checksum != checksum_given)
|
|
{
|
|
printf("Error: Parser: Hex: Checksum mismatch! (Line %i)\n",line);
|
|
return NULL;
|
|
}
|
|
|
|
//Print hex line
|
|
//printf("%c ",hex->start_code);
|
|
//printf("%02X ",byte_count);
|
|
//printf("%04X ",hex->address.i);
|
|
//for (start_offset = 0; start_offset < byte_count; start_offset++)
|
|
// printf("%02X",*(hex_data + start_offset));
|
|
//if (byte_count) printf(" ");
|
|
//printf("%02X\n",checksum);
|
|
|
|
if (*hex->record_type == 0) //Data
|
|
{
|
|
if (!first_address_set)
|
|
{
|
|
first_address_set = 1;
|
|
next_address = base_address + hex->address.i;
|
|
}
|
|
|
|
if (hex->address.i + base_address != next_address)
|
|
{
|
|
printf("Error: Parser: Hex: Address not contiguous! (Line %i)\n",line);
|
|
return NULL;
|
|
}
|
|
|
|
//Write binary data into rawdata buffer (write position will never reach read position)
|
|
binlength += byte_count;
|
|
for (start_offset = 0; start_offset < byte_count; start_offset++)
|
|
{
|
|
*bindata = *(hex_data + start_offset);
|
|
bindata++;
|
|
}
|
|
|
|
next_address = base_address + ((next_address + byte_count) & 0xFFFF);
|
|
}
|
|
else if (*hex->record_type == 1) //EOF
|
|
{
|
|
break; //No need to go further
|
|
}
|
|
else if (*hex->record_type == 2) //Extended Segment Address
|
|
{
|
|
base_address = ((*hex_data << 8) + *(hex_data+1)) << 16;
|
|
next_address += base_address;
|
|
}
|
|
else if (*hex->record_type == 3) //Start Segment Address
|
|
{
|
|
start_segment_address = ((*hex_data << 24) + (*(hex_data+1) << 16) + (*(hex_data+2) << 8) + (*(hex_data+3)));
|
|
start_segment_address_set = 1;
|
|
}
|
|
else if (*hex->record_type == 4) //Extended Linear Address
|
|
{
|
|
printf("Error: Parser: Hex: 32-bit addressing is not supported! (Line %i)\n",line);
|
|
return NULL;
|
|
}
|
|
else if (*hex->record_type == 5) //Start Linear Address
|
|
{
|
|
printf("Error: Parser: Hex: Start linear address is not supported! (Line %i)\n",line);
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
printf("Error: Parser: Hex: Unknown record type! (Line %i)\n",line);
|
|
return NULL;
|
|
}
|
|
|
|
rds += sizeof(hex_record_t) + (byte_count * 2) + 2; //Bypass header, data, checksum
|
|
|
|
while (rds < rde && (*rds == '\r' || *rds == '\n')) rds++; //Bypass EOL characters
|
|
}
|
|
|
|
if (!start_segment_address_set)
|
|
{
|
|
printf("Error: Parser: Hex: Missing start segment address!\n");
|
|
return NULL;
|
|
}
|
|
|
|
data_t *data = create_data(binlength);
|
|
|
|
if (data)
|
|
{
|
|
memcpy(data->data, rawdata, binlength);
|
|
data->size = binlength;
|
|
data->addr = start_segment_address;
|
|
}
|
|
else
|
|
printf("Error: Parser: Hex: Error creating parser storage!\n");
|
|
|
|
return data;
|
|
}
|
|
|
|
char get_type_by_ext(char *fname)
|
|
{
|
|
char *pext = fname + (strlen(fname) - 4);
|
|
|
|
if (!strcmp(pext,EXT_HEX)) return FTYPE_HEX;
|
|
else if (!strcmp(pext,EXT_BIN)) return FTYPE_BIN;
|
|
else return FTYPE_NONE;
|
|
}
|
|
|
|
data_t *load_file(char *fname)
|
|
{
|
|
if (!fname)
|
|
{
|
|
printf("ERROR: Parser: No file given!\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (strlen(fname) < 5)
|
|
{
|
|
printf("ERROR: Parser: File name must end in %s or %s!\n", EXT_HEX, EXT_BIN);
|
|
return NULL;
|
|
}
|
|
|
|
char ftype = get_type_by_ext(fname);
|
|
if (ftype == FTYPE_NONE)
|
|
{
|
|
printf("ERROR: Parser: File name must end in %s or %s!\n", EXT_HEX, EXT_BIN);
|
|
return NULL;
|
|
}
|
|
|
|
uint32_t fsize = filesize(fname);
|
|
if (fsize == 0)
|
|
{
|
|
printf("ERROR: Parser: File is empty!\n");
|
|
return NULL;
|
|
}
|
|
|
|
FILE *fIn = fopen(fname, "rb");
|
|
if (!fIn)
|
|
{
|
|
printf("ERROR: Parser: Could not open file for read!\n");
|
|
return NULL;
|
|
}
|
|
|
|
char *rawdata = (char *)malloc(fsize);
|
|
if (!rawdata)
|
|
{
|
|
printf("ERROR: Parser: Could no allocated file memory buffer!\n");
|
|
fclose(fIn);
|
|
return NULL;
|
|
}
|
|
|
|
uint32_t readbytes = (uint32_t)fread(rawdata, 1, fsize, fIn);
|
|
|
|
fclose(fIn);
|
|
|
|
if (readbytes != fsize)
|
|
{
|
|
printf("ERROR: Parser: File read size mismatch!\n");
|
|
free(rawdata);
|
|
return NULL;
|
|
}
|
|
|
|
if (ftype == FTYPE_HEX)
|
|
return parse_hex(rawdata, readbytes);
|
|
else if (ftype == FTYPE_BIN)
|
|
return parse_bin(rawdata, readbytes);
|
|
else
|
|
printf("ERROR: Parser: Unknown file type!\n");
|
|
|
|
return NULL;
|
|
}
|