-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
yaml --- r: 100980 b: refs/heads/master c: 8bd6b22 h: refs/heads/master v: v3
- Loading branch information
David Woodhouse
authored and
David Woodhouse
committed
Jul 10, 2008
1 parent
6c8030c
commit b6e6123
Showing
4 changed files
with
263 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
--- | ||
refs/heads/master: f1485f3deb89e6ae10c4d34662ec9e692855ab5d | ||
refs/heads/master: 8bd6b2229bf98761465020467ec33547d05bff46 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,247 @@ | ||
/* | ||
* Parser/loader for IHEX formatted data. | ||
* | ||
* Copyright © 2008 David Woodhouse <dwmw2@infradead.org> | ||
* Copyright © 2005 Jan Harkes <jaharkes@cs.cmu.edu> | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License version 2 as | ||
* published by the Free Software Foundation. | ||
*/ | ||
|
||
#include <stdint.h> | ||
#include <arpa/inet.h> | ||
#include <stdio.h> | ||
#include <errno.h> | ||
#include <sys/types.h> | ||
#include <sys/stat.h> | ||
#include <sys/mman.h> | ||
#include <fcntl.h> | ||
#include <string.h> | ||
#include <unistd.h> | ||
#include <stdlib.h> | ||
|
||
struct ihex_binrec { | ||
struct ihex_binrec *next; /* not part of the real data structure */ | ||
uint32_t addr; | ||
uint16_t len; | ||
uint8_t data[]; | ||
}; | ||
|
||
/** | ||
* nybble/hex are little helpers to parse hexadecimal numbers to a byte value | ||
**/ | ||
static uint8_t nybble(const uint8_t n) | ||
{ | ||
if (n >= '0' && n <= '9') return n - '0'; | ||
else if (n >= 'A' && n <= 'F') return n - ('A' - 10); | ||
else if (n >= 'a' && n <= 'f') return n - ('a' - 10); | ||
return 0; | ||
} | ||
|
||
static uint8_t hex(const uint8_t *data, uint8_t *crc) | ||
{ | ||
uint8_t val = (nybble(data[0]) << 4) | nybble(data[1]); | ||
*crc += val; | ||
return val; | ||
} | ||
|
||
static int process_ihex(uint8_t *data, ssize_t size); | ||
static void file_record(struct ihex_binrec *record); | ||
static int output_records(int outfd); | ||
|
||
static int sort_records = 0; | ||
|
||
int main(int argc, char **argv) | ||
{ | ||
int infd, outfd; | ||
struct stat st; | ||
uint8_t *data; | ||
|
||
if (argc == 4 && !strcmp(argv[1], "-s")) { | ||
sort_records = 1; | ||
argc--; | ||
argv++; | ||
} | ||
if (argc != 3) { | ||
usage: | ||
fprintf(stderr, "ihex2fw: Convert ihex files into binary " | ||
"representation for use by Linux kernel\n"); | ||
fprintf(stderr, "usage: ihex2fw [-s] <src.HEX> <dst.fw>\n"); | ||
fprintf(stderr, " -s: sort records by address\n"); | ||
return 1; | ||
} | ||
if (!strcmp(argv[1], "-")) | ||
infd = 0; | ||
else | ||
infd = open(argv[1], O_RDONLY); | ||
if (infd == -1) { | ||
fprintf(stderr, "Failed to open source file: %s", | ||
strerror(errno)); | ||
goto usage; | ||
} | ||
if (fstat(infd, &st)) { | ||
perror("stat"); | ||
return 1; | ||
} | ||
data = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, infd, 0); | ||
if (data == MAP_FAILED) { | ||
perror("mmap"); | ||
return 1; | ||
} | ||
|
||
if (!strcmp(argv[2], "-")) | ||
outfd = 1; | ||
else | ||
outfd = open(argv[2], O_TRUNC|O_CREAT|O_WRONLY, 0644); | ||
if (outfd == -1) { | ||
fprintf(stderr, "Failed to open destination file: %s", | ||
strerror(errno)); | ||
goto usage; | ||
} | ||
if (process_ihex(data, st.st_size)) | ||
return 1; | ||
|
||
output_records(outfd); | ||
return 0; | ||
} | ||
|
||
static int process_ihex(uint8_t *data, ssize_t size) | ||
{ | ||
struct ihex_binrec *record; | ||
uint32_t offset = 0; | ||
uint8_t type, crc = 0, crcbyte = 0; | ||
int i, j; | ||
int line = 1; | ||
int len; | ||
|
||
i = 0; | ||
next_record: | ||
/* search for the start of record character */ | ||
while (i < size) { | ||
if (data[i] == '\n') line++; | ||
if (data[i++] == ':') break; | ||
} | ||
|
||
/* Minimum record length would be about 10 characters */ | ||
if (i + 10 > size) { | ||
fprintf(stderr, "Can't find valid record at line %d\n", line); | ||
return -EINVAL; | ||
} | ||
|
||
len = hex(data + i, &crc); i += 2; | ||
|
||
record = malloc((sizeof (*record) + len + 3) & ~3); | ||
if (!record) { | ||
fprintf(stderr, "out of memory for records\n"); | ||
return -ENOMEM; | ||
} | ||
memset(record, 0, (sizeof(*record) + len + 3) & ~3); | ||
record->len = len; | ||
|
||
/* now check if we have enough data to read everything */ | ||
if (i + 8 + (record->len * 2) > size) { | ||
fprintf(stderr, "Not enough data to read complete record at line %d\n", | ||
line); | ||
return -EINVAL; | ||
} | ||
|
||
record->addr = hex(data + i, &crc) << 8; i += 2; | ||
record->addr |= hex(data + i, &crc); i += 2; | ||
type = hex(data + i, &crc); i += 2; | ||
|
||
for (j = 0; j < record->len; j++, i += 2) | ||
record->data[j] = hex(data + i, &crc); | ||
|
||
/* check CRC */ | ||
crcbyte = hex(data + i, &crc); i += 2; | ||
if (crc != 0) { | ||
fprintf(stderr, "CRC failure at line %d: got 0x%X, expected 0x%X\n", | ||
line, crcbyte, (unsigned char)(crcbyte-crc)); | ||
return -EINVAL; | ||
} | ||
|
||
/* Done reading the record */ | ||
switch (type) { | ||
case 0: | ||
/* old style EOF record? */ | ||
if (!record->len) | ||
break; | ||
|
||
record->addr += offset; | ||
file_record(record); | ||
goto next_record; | ||
|
||
case 1: /* End-Of-File Record */ | ||
if (record->addr || record->len) { | ||
fprintf(stderr, "Bad EOF record (type 01) format at line %d", | ||
line); | ||
return -EINVAL; | ||
} | ||
break; | ||
|
||
case 2: /* Extended Segment Address Record (HEX86) */ | ||
case 4: /* Extended Linear Address Record (HEX386) */ | ||
if (record->addr || record->len != 2) { | ||
fprintf(stderr, "Bad HEX86/HEX386 record (type %02X) at line %d\n", | ||
type, line); | ||
return -EINVAL; | ||
} | ||
|
||
/* We shouldn't really be using the offset for HEX86 because | ||
* the wraparound case is specified quite differently. */ | ||
offset = record->data[0] << 8 | record->data[1]; | ||
offset <<= (type == 2 ? 4 : 16); | ||
goto next_record; | ||
|
||
case 3: /* Start Segment Address Record */ | ||
case 5: /* Start Linear Address Record */ | ||
if (record->addr || record->len != 4) { | ||
fprintf(stderr, "Bad Start Address record (type %02X) at line %d\n", | ||
type, line); | ||
return -EINVAL; | ||
} | ||
|
||
/* These records contain the CS/IP or EIP where execution | ||
* starts. Don't really know what to do with them. */ | ||
goto next_record; | ||
|
||
default: | ||
fprintf(stderr, "Unknown record (type %02X)\n", type); | ||
return -EINVAL; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
static struct ihex_binrec *records; | ||
|
||
static void file_record(struct ihex_binrec *record) | ||
{ | ||
struct ihex_binrec **p = &records; | ||
|
||
while ((*p) && (!sort_records || (*p)->addr < record->addr)) | ||
p = &((*p)->next); | ||
|
||
record->next = *p; | ||
*p = record; | ||
} | ||
|
||
static int output_records(int outfd) | ||
{ | ||
unsigned char zeroes[5] = {0, 0, 0, 0, 0}; | ||
struct ihex_binrec *p = records; | ||
|
||
while (p) { | ||
uint16_t writelen = (p->len + 9) & ~3; | ||
|
||
p->addr = htonl(p->addr); | ||
p->len = htonl(p->len); | ||
write(outfd, &p->addr, writelen); | ||
p = p->next; | ||
} | ||
/* EOF record is zero length, since we don't bother to represent | ||
the type field in the binary version */ | ||
write(outfd, zeroes, 5); | ||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters