IP400/rpi/stm32flash/parsers/hex.c

252 lines
5.7 KiB
C
Raw Normal View History

2025-01-29 20:17:14 +02:00
/*
stm32flash - Open Source ST STM32 flash program for *nix
Copyright (C) 2010 Geoffrey McRae <geoff@spacevs.com>
This program 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 2
of the License, or (at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "hex.h"
#include "../compiler.h"
#include "../utils.h"
extern FILE *diag;
typedef struct {
size_t data_len, offset;
uint8_t *data;
uint32_t base;
} hex_t;
void* hex_init() {
return calloc(sizeof(hex_t), 1);
}
parser_err_t hex_open(void *storage, const char *filename, const char write) {
hex_t *st = storage;
if (write) {
return PARSER_ERR_RDONLY;
} else {
char mark;
int fd;
uint8_t checksum;
unsigned int c, i;
uint32_t base = 0;
unsigned int last_address = 0x0;
fd = open(filename, O_RDONLY);
if (fd < 0)
return PARSER_ERR_SYSTEM;
/* read in the file */
while(read(fd, &mark, 1) != 0) {
if (mark == '\n' || mark == '\r') continue;
if (mark != ':')
return PARSER_ERR_INVALID_FILE;
char buffer[9];
unsigned int reclen, address, type;
uint8_t *record = NULL;
/* get the reclen, address, and type */
buffer[8] = 0;
if (read(fd, &buffer, 8) != 8) return PARSER_ERR_INVALID_FILE;
if (sscanf(buffer, "%2x%4x%2x", &reclen, &address, &type) != 3) {
close(fd);
return PARSER_ERR_INVALID_FILE;
}
/* setup the checksum */
checksum =
reclen +
((address & 0xFF00) >> 8) +
((address & 0x00FF) >> 0) +
type;
switch(type) {
/* data record */
case 0:
if (st->data_len == 0) {
st->base |= address;
last_address = address;
}
c = address - last_address;
st->data = realloc(st->data, st->data_len + c + reclen);
/* if there is a gap, set it to 0xff and increment the length */
if (c > 0) {
memset(&st->data[st->data_len], 0xff, c);
st->data_len += c;
}
last_address = address + reclen;
record = &st->data[st->data_len];
st->data_len += reclen;
break;
/* extended segment address record */
case 2:
base = 0;
break;
/* extended linear address record */
case 4:
base = 0;
break;
}
buffer[2] = 0;
for(i = 0; i < reclen; ++i) {
if (read(fd, &buffer, 2) != 2 || sscanf(buffer, "%2x", &c) != 1) {
close(fd);
return PARSER_ERR_INVALID_FILE;
}
/* add the byte to the checksum */
checksum += c;
switch(type) {
case 0:
if (record != NULL) {
record[i] = c;
} else {
return PARSER_ERR_INVALID_FILE;
}
break;
case 2:
case 4:
base = (base << 8) | c;
break;
}
}
/* read, scan, and verify the checksum */
if (
read(fd, &buffer, 2 ) != 2 ||
sscanf(buffer, "%2x", &c) != 1 ||
(uint8_t)(checksum + c) != 0x00
) {
close(fd);
return PARSER_ERR_INVALID_FILE;
}
switch(type) {
/* EOF */
case 1:
close(fd);
return PARSER_ERR_OK;
/* address record */
case 4: base = base << 12;
/* fall-through */
case 2: base = base << 4;
/* Reset last_address since our base changed */
last_address = 0;
/* Only assign the program's base address once, and only
* do so if we haven't seen any data records yet.
* If there are any data records before address records,
* the program's base address must be zero.
*/
if (st->base == 0 && st->data_len == 0) {
st->base = base;
break;
}
/* we cant cope with files out of order */
if (base < st->base) {
close(fd);
return PARSER_ERR_INVALID_FILE;
}
/* if there is a gap, enlarge and fill with 0xff */
size_t len = base - st->base;
if (len > st->data_len) {
size_t gap = len - st->data_len;
if (gap > 16384) /* arbitrary limit for warning */
fprintf(diag, "Warning: Filling gap of %zu bytes at 0x%08zx\n", gap, st->base + st->data_len);
st->data = realloc(st->data, len);
if (st->data == NULL) {
fprintf(diag, "Error: Cannot reallocate memory\n");
return PARSER_ERR_SYSTEM;
}
memset(&st->data[st->data_len], 0xff, gap);
st->data_len = len;
}
break;
}
}
close(fd);
return PARSER_ERR_OK;
}
}
parser_err_t hex_close(void *storage) {
hex_t *st = storage;
if (st) free(st->data);
free(st);
return PARSER_ERR_OK;
}
unsigned int hex_base(void *storage) {
hex_t *st = storage;
return st->base;
}
unsigned int hex_size(void *storage) {
hex_t *st = storage;
return st->data_len;
}
parser_err_t hex_read(void *storage, void *data, unsigned int *len) {
hex_t *st = storage;
unsigned int left = st->data_len - st->offset;
unsigned int get = left > *len ? *len : left;
memcpy(data, &st->data[st->offset], get);
st->offset += get;
*len = get;
return PARSER_ERR_OK;
}
parser_err_t hex_write(void __unused *storage, void __unused *data, unsigned int __unused len) {
return PARSER_ERR_RDONLY;
}
parser_t PARSER_HEX = {
"Intel HEX",
hex_init,
hex_open,
hex_close,
hex_base,
hex_size,
hex_read,
hex_write
};