From c03e01879e4dc13d8ecc999b97350e683fa5cf06 Mon Sep 17 00:00:00 2001 From: Pali Rohár Date: Sat, 24 Nov 2012 00:54:01 +0100 Subject: cal: Added CAL parser from Calvaria --- src/cal.c | 263 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 src/cal.c (limited to 'src/cal.c') diff --git a/src/cal.c b/src/cal.c new file mode 100644 index 0000000..3a46abd --- /dev/null +++ b/src/cal.c @@ -0,0 +1,263 @@ +/* + 0xFFFF - Open Free Fiasco Firmware Flasher + Copyright (c) 2011 Michael Buesch + Copyright (C) 2012 Pali Rohár + + 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 3 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, see . + +*/ + +/* This is simple CAL parser form Calvaria */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "cal.h" + +#define MAX_SIZE 393216 +#define INDEX_LAST (0xFF + 1) +#define HDR_MAGIC "ConF" + +struct cal { + int fd; + ssize_t size; + void * mem; +}; + +struct header { + char magic[4]; /* Magic sequence */ + uint8_t type; /* Type number */ + uint8_t index; /* Index number */ + uint16_t flags; /* Flags */ + char name[16]; /* Human readable section name */ + uint32_t length; /* Payload length */ + uint32_t datasum; /* Data CRC32 checksum */ + uint32_t hdrsum; /* Header CRC32 checksum */ +} __attribute__((__packed__)); + + +int cal_init_file(const char * file, struct cal ** cal_out) { + + int fd = -1; + uint64_t blksize = 0; + ssize_t size = 0; + void * mem = NULL; + struct cal * cal = NULL; + struct stat st; + mtd_info_t mtd_info; + + if ( stat(file, &st) != 0 ) + return -1; + + fd = open(file, O_RDONLY); + + if ( fd < 0 ) + return -1; + + if ( S_ISREG(st.st_mode) ) + size = st.st_size; + else if ( S_ISBLK(st.st_mode) ) { + if ( ioctl(fd, BLKGETSIZE64, &blksize) != 0 ) + goto err; + if ( blksize > SSIZE_MAX ) + goto err; + size = blksize; + } else if ( S_ISCHR(st.st_mode) && major(st.st_rdev) == 90 ) { + if ( ioctl(fd, MEMGETINFO, &mtd_info) != 0 ) + goto err; + size = mtd_info.size; + } else { + goto err; + } + + if ( size == 0 || size > MAX_SIZE ) + goto err; + + mem = malloc(size); + + if ( ! mem ) + goto err; + + if ( read(fd, mem, size) != size ) + goto err; + + cal = malloc(sizeof(struct cal)); + + if ( ! cal ) + goto err; + + cal->fd = fd; + cal->mem = mem; + cal->size = size; + + *cal_out = cal; + return 0; + +err: + close(fd); + free(mem); + return -1; + +} + +int cal_init(struct cal ** cal_out) { + + return cal_init_file("/dev/mtd1ro", cal_out); + +} + +void cal_finish(struct cal * cal) { + + if ( cal ) { + free(cal->mem); + free(cal); + } + +} + +static uint32_t crc32(uint32_t crc, const void * _data, size_t size) { + + const uint8_t * data = _data; + uint8_t value; + unsigned int bit; + size_t i; + const uint32_t poly = 0xEDB88320; + + for ( i = 0; i < size; i++ ) { + value = data[i]; + for ( bit = 8; bit; bit-- ) { + if ( (crc & 1) != (value & 1) ) + crc = (crc >> 1) ^ poly; + else + crc >>= 1; + value >>= 1; + } + } + + return crc; + +} + +static int is_header(void *data, size_t size) { + + struct header * hdr = data; + + if ( size < sizeof(struct header) ) + return 0; + + if ( memcmp(hdr->magic, HDR_MAGIC, sizeof(hdr->magic)) != 0 ) + + return 0; + return 1; + +} + +static int64_t find_section(void *start, uint64_t count, int want_index, const char *want_name) { + + int64_t offset = 0, found_offset = -1; + uint8_t * data = start; + struct header *hdr; + char sectname[sizeof(hdr->name) + 1] = { 0, }; + uint32_t payload_len; + int previous_index = -1; + + while ( 1 ) { + + /* Find header start */ + if ( count < sizeof(struct header) ) + break; + + if ( ! is_header(data + offset, count) ) { + count--; + offset++; + continue; + } + + hdr = (struct header *)(data + offset); + payload_len = hdr->length; + + if ( count - sizeof(struct header) < payload_len ) + return -1; + + memcpy(sectname, hdr->name, sizeof(hdr->name)); + + if ( want_index == INDEX_LAST ) { + if ((int)hdr->index <= previous_index) + goto next; + } else { + if (want_index >= 0 && want_index != hdr->index) + goto next; + } + + if ( want_name && strcmp(sectname, want_name) != 0 ) + goto next; + + /* Found it */ + found_offset = offset; + if ( want_index == INDEX_LAST ) + previous_index = hdr->index; + else + break; + +next: + count -= sizeof(struct header) + payload_len; + offset += sizeof(struct header) + payload_len; + + } + + return found_offset; + +} + +int cal_read_block(struct cal * cal, const char * name, void ** ptr, unsigned long * len, unsigned long flags) { + + int64_t find_offset; + uint64_t filelen = cal->size; + uint8_t * data = cal->mem; + struct header * hdr; + + find_offset = find_section(data, filelen, INDEX_LAST, name); + if ( find_offset < 0 ) + return -1; + + hdr = (struct header *)(data + find_offset); + + if ( flags && hdr->flags != flags ) + return -1; + + if ( crc32(0, hdr, sizeof(*hdr) - 4) != hdr->hdrsum ) + return -1; + + *ptr = data + find_offset + sizeof(struct header); + *len = hdr->length; + + if ( crc32(0, *ptr, *len) != hdr->datasum ) + return -1; + + return 0; + +} -- cgit v1.2.3