cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ihex2fw.c (6880B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      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_SOURCE
     21#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 value
     37 **/
     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	else
    101		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	else
    120		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 because
    221		 * 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 execution
    239		 * 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 represent
    285	   the type field in the binary version */
    286	if (write(outfd, zeroes, 6) != 6)
    287		return 1;
    288	return 0;
    289}