brintos

brintos / linux-shallow public Read only

0
0
Text · 6.7 KiB · 2ebed47 Raw
290 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Parser/loader for IHEX formatted data.4 *5 * Copyright © 2008 David Woodhouse <dwmw2@infradead.org>6 * Copyright © 2005 Jan Harkes <jaharkes@cs.cmu.edu>7 */8 9#include <stdint.h>10#include <arpa/inet.h>11#include <stdio.h>12#include <errno.h>13#include <sys/types.h>14#include <sys/stat.h>15#include <sys/mman.h>16#include <fcntl.h>17#include <string.h>18#include <unistd.h>19#include <stdlib.h>20#define _GNU_SOURCE21#include <getopt.h>22 23 24#define __ALIGN_KERNEL_MASK(x, mask)	(((x) + (mask)) & ~(mask))25#define __ALIGN_KERNEL(x, a)		__ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)26#define ALIGN(x, a)			__ALIGN_KERNEL((x), (a))27 28struct ihex_binrec {29	struct ihex_binrec *next; /* not part of the real data structure */30        uint32_t addr;31        uint16_t len;32        uint8_t data[];33};34 35/**36 * nybble/hex are little helpers to parse hexadecimal numbers to a byte value37 **/38static uint8_t nybble(const uint8_t n)39{40	if      (n >= '0' && n <= '9') return n - '0';41	else if (n >= 'A' && n <= 'F') return n - ('A' - 10);42	else if (n >= 'a' && n <= 'f') return n - ('a' - 10);43	return 0;44}45 46static uint8_t hex(const uint8_t *data, uint8_t *crc)47{48	uint8_t val = (nybble(data[0]) << 4) | nybble(data[1]);49	*crc += val;50	return val;51}52 53static int process_ihex(uint8_t *data, ssize_t size);54static void file_record(struct ihex_binrec *record);55static int output_records(int outfd);56 57static int sort_records = 0;58static int wide_records = 0;59static int include_jump = 0;60 61static int usage(void)62{63	fprintf(stderr, "ihex2fw: Convert ihex files into binary "64		"representation for use by Linux kernel\n");65	fprintf(stderr, "usage: ihex2fw [<options>] <src.HEX> <dst.fw>\n");66	fprintf(stderr, "       -w: wide records (16-bit length)\n");67	fprintf(stderr, "       -s: sort records by address\n");68	fprintf(stderr, "       -j: include records for CS:IP/EIP address\n");69	return 1;70}71 72int main(int argc, char **argv)73{74	int infd, outfd;75	struct stat st;76	uint8_t *data;77	int opt;78 79	while ((opt = getopt(argc, argv, "wsj")) != -1) {80		switch (opt) {81		case 'w':82			wide_records = 1;83			break;84		case 's':85			sort_records = 1;86			break;87		case 'j':88			include_jump = 1;89			break;90		default:91			return usage();92		}93	}94 95	if (optind + 2 != argc)96		return usage();97 98	if (!strcmp(argv[optind], "-"))99		infd = 0;100	else101		infd = open(argv[optind], O_RDONLY);102	if (infd == -1) {103		fprintf(stderr, "Failed to open source file: %s",104			strerror(errno));105		return usage();106	}107	if (fstat(infd, &st)) {108		perror("stat");109		return 1;110	}111	data = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, infd, 0);112	if (data == MAP_FAILED) {113		perror("mmap");114		return 1;115	}116 117	if (!strcmp(argv[optind+1], "-"))118		outfd = 1;119	else120		outfd = open(argv[optind+1], O_TRUNC|O_CREAT|O_WRONLY, 0644);121	if (outfd == -1) {122		fprintf(stderr, "Failed to open destination file: %s",123			strerror(errno));124		return usage();125	}126	if (process_ihex(data, st.st_size))127		return 1;128 129	return output_records(outfd);130}131 132static int process_ihex(uint8_t *data, ssize_t size)133{134	struct ihex_binrec *record;135	size_t record_size;136	uint32_t offset = 0;137	uint32_t data32;138	uint8_t type, crc = 0, crcbyte = 0;139	int i, j;140	int line = 1;141	int len;142 143	i = 0;144next_record:145	/* search for the start of record character */146	while (i < size) {147		if (data[i] == '\n') line++;148		if (data[i++] == ':') break;149	}150 151	/* Minimum record length would be about 10 characters */152	if (i + 10 > size) {153		fprintf(stderr, "Can't find valid record at line %d\n", line);154		return -EINVAL;155	}156 157	len = hex(data + i, &crc); i += 2;158	if (wide_records) {159		len <<= 8;160		len += hex(data + i, &crc); i += 2;161	}162	record_size = ALIGN(sizeof(*record) + len, 4);163	record = malloc(record_size);164	if (!record) {165		fprintf(stderr, "out of memory for records\n");166		return -ENOMEM;167	}168	memset(record, 0, record_size);169	record->len = len;170 171	/* now check if we have enough data to read everything */172	if (i + 8 + (record->len * 2) > size) {173		fprintf(stderr, "Not enough data to read complete record at line %d\n",174			line);175		return -EINVAL;176	}177 178	record->addr  = hex(data + i, &crc) << 8; i += 2;179	record->addr |= hex(data + i, &crc); i += 2;180	type = hex(data + i, &crc); i += 2;181 182	for (j = 0; j < record->len; j++, i += 2)183		record->data[j] = hex(data + i, &crc);184 185	/* check CRC */186	crcbyte = hex(data + i, &crc); i += 2;187	if (crc != 0) {188		fprintf(stderr, "CRC failure at line %d: got 0x%X, expected 0x%X\n",189			line, crcbyte, (unsigned char)(crcbyte-crc));190		return -EINVAL;191	}192 193	/* Done reading the record */194	switch (type) {195	case 0:196		/* old style EOF record? */197		if (!record->len)198			break;199 200		record->addr += offset;201		file_record(record);202		goto next_record;203 204	case 1: /* End-Of-File Record */205		if (record->addr || record->len) {206			fprintf(stderr, "Bad EOF record (type 01) format at line %d",207				line);208			return -EINVAL;209		}210		break;211 212	case 2: /* Extended Segment Address Record (HEX86) */213	case 4: /* Extended Linear Address Record (HEX386) */214		if (record->addr || record->len != 2) {215			fprintf(stderr, "Bad HEX86/HEX386 record (type %02X) at line %d\n",216				type, line);217			return -EINVAL;218		}219 220		/* We shouldn't really be using the offset for HEX86 because221		 * the wraparound case is specified quite differently. */222		offset = record->data[0] << 8 | record->data[1];223		offset <<= (type == 2 ? 4 : 16);224		goto next_record;225 226	case 3: /* Start Segment Address Record */227	case 5: /* Start Linear Address Record */228		if (record->addr || record->len != 4) {229			fprintf(stderr, "Bad Start Address record (type %02X) at line %d\n",230				type, line);231			return -EINVAL;232		}233 234		memcpy(&data32, &record->data[0], sizeof(data32));235		data32 = htonl(data32);236		memcpy(&record->data[0], &data32, sizeof(data32));237 238		/* These records contain the CS/IP or EIP where execution239		 * starts. If requested output this as a record. */240		if (include_jump)241			file_record(record);242		goto next_record;243 244	default:245		fprintf(stderr, "Unknown record (type %02X)\n", type);246		return -EINVAL;247	}248 249	return 0;250}251 252static struct ihex_binrec *records;253 254static void file_record(struct ihex_binrec *record)255{256	struct ihex_binrec **p = &records;257 258	while ((*p) && (!sort_records || (*p)->addr < record->addr))259		p = &((*p)->next);260 261	record->next = *p;262	*p = record;263}264 265static uint16_t ihex_binrec_size(struct ihex_binrec *p)266{267	return p->len + sizeof(p->addr) + sizeof(p->len);268}269 270static int output_records(int outfd)271{272	unsigned char zeroes[6] = {0, 0, 0, 0, 0, 0};273	struct ihex_binrec *p = records;274 275	while (p) {276		uint16_t writelen = ALIGN(ihex_binrec_size(p), 4);277 278		p->addr = htonl(p->addr);279		p->len = htons(p->len);280		if (write(outfd, &p->addr, writelen) != writelen)281			return 1;282		p = p->next;283	}284	/* EOF record is zero length, since we don't bother to represent285	   the type field in the binary version */286	if (write(outfd, zeroes, 6) != 6)287		return 1;288	return 0;289}290