summaryrefslogtreecommitdiffstats
path: root/src/fiasco.c
diff options
context:
space:
mode:
authorPali Rohár <pali.rohar@gmail.com>2012-08-09 19:50:14 +0200
committerPali Rohár <pali.rohar@gmail.com>2012-08-09 19:50:14 +0200
commitb2883167d5db98d279687ea009950569568316a3 (patch)
tree5c823503ac6ac382a4515b5b51243b6653607a4d /src/fiasco.c
parent1b99ad115af567e386432fb73b51eb490aba6a55 (diff)
download0xFFFF-b2883167d5db98d279687ea009950569568316a3.tar.bz2
Rename new fiasco2 to fiasco
Diffstat (limited to 'src/fiasco.c')
-rw-r--r--src/fiasco.c595
1 files changed, 595 insertions, 0 deletions
diff --git a/src/fiasco.c b/src/fiasco.c
new file mode 100644
index 0000000..72d245b
--- /dev/null
+++ b/src/fiasco.c
@@ -0,0 +1,595 @@
+/*
+ 0xFFFF - Open Free Fiasco Firmware Flasher
+ Copyright (C) 2007-2011 pancake <pancake@youterm.com>
+ Copyright (C) 2011-2012 Pali Rohár <pali.rohar@gmail.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 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 <http://www.gnu.org/licenses/>.
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "global.h"
+#include "device.h"
+#include "image.h"
+#include "fiasco.h"
+
+#define FIASCO_READ_ERROR(fiasco, format, ...) do { ERROR(errno, format, ##__VA_ARGS__); fiasco_free(fiasco); return NULL; } while (0)
+#define FIASCO_WRITE_ERROR(file, fd, format, ...) do { ERROR(errno, "%s: " format, file, ##__VA_ARGS__); if ( fd >= 0 ) close(fd); return -1; } while (0)
+#define READ_OR_FAIL(fiasco, buf, size) do { if ( read(fiasco->fd, buf, size) != size ) { FIASCO_READ_ERROR(fiasco, "Cannot read %d bytes", size); } } while (0)
+#define READ_OR_RETURN(fiasco, buf, size) do { if ( read(fiasco->fd, buf, size) != size ) return fiasco; } while (0)
+#define WRITE_OR_FAIL(file, fd, buf, size) do { if ( ! simulate ) { if ( write(fd, buf, size) != size ) { FIASCO_WRITE_ERROR(file, fd, "Cannot write %d bytes", size); } } } while (0)
+
+struct fiasco * fiasco_alloc_empty(void) {
+
+ struct fiasco * fiasco = calloc(1, sizeof(struct fiasco));
+ if ( ! fiasco )
+ ALLOC_ERROR_RETURN(NULL);
+
+ fiasco->fd = -1;
+ return fiasco;
+
+}
+
+struct fiasco * fiasco_alloc_from_file(const char * file) {
+
+ uint8_t byte;
+ uint32_t length;
+ uint32_t count;
+ uint8_t length8;
+ uint8_t count8;
+
+ char type[13];
+ char device[17];
+ char hwrevs[1024];
+ char version[257];
+ char layout[257];
+ uint16_t hash;
+ off_t offset;
+ struct image * image;
+
+ char hwrev[9];
+ unsigned char buf[512];
+ unsigned char *pbuf;
+
+ struct fiasco * fiasco = fiasco_alloc_empty();
+ if ( ! fiasco )
+ return NULL;
+
+ fiasco->fd = open(file, O_RDONLY);
+ if ( fiasco->fd < 0 ) {
+ ERROR(errno, "Cannot open file");
+ fiasco_free(fiasco);
+ return NULL;
+ }
+
+ fiasco->orig_filename = strdup(file);
+
+ READ_OR_FAIL(fiasco, &byte, 1);
+ if ( byte != 0xb4 )
+ FIASCO_READ_ERROR(fiasco, "Invalid fiasco signature");
+
+ READ_OR_FAIL(fiasco, &length, 4);
+ length = ntohl(length);
+
+ READ_OR_FAIL(fiasco, &count, 4);
+ count = ntohl(count);
+
+ VERBOSE("Number of header blocks: %d\n", count);
+
+ while ( count > 0 ) {
+ READ_OR_FAIL(fiasco, &byte, 1);
+ READ_OR_FAIL(fiasco, &length8, 1);
+ READ_OR_FAIL(fiasco, buf, length8);
+ if ( byte == 0xe8 ) {
+ memset(fiasco->name, 0, sizeof(fiasco->name));
+ strncpy(fiasco->name, (char *)buf, length8);
+ VERBOSE("Fiasco name: %s\n", fiasco->name);
+ } else if ( byte == 0x31 ) {
+ memset(fiasco->swver, 0, sizeof(fiasco->swver));
+ strncpy(fiasco->swver, (char *)buf, length8);
+ VERBOSE("SW version: %s\n", fiasco->swver);
+ } else {
+ VERBOSE("Unknown header %#x\n", byte);
+ }
+ --count;
+ }
+
+ /* walk the tree */
+ while ( 1 ) {
+
+ /* If end of file, return fiasco image */
+ READ_OR_RETURN(fiasco, buf, 7);
+
+ /* Header of next image */
+ if ( ! buf[0] == 0x54 && buf[2] == 0x2E && buf[3] == 0x19 && buf[4] == 0x01 && buf[5] == 0x01 && buf[6] == 0x00 ) {
+ ERROR(0, "Invalid next image header");
+ return fiasco;
+ }
+
+ count8 = buf[1];
+ if ( count8 > 0 )
+ --count8;
+
+ READ_OR_RETURN(fiasco, &hash, 2);
+ hash = ntohs(hash);
+
+ memset(type, 0, sizeof(type));
+ READ_OR_RETURN(fiasco, type, 12);
+
+ byte = type[0];
+ if ( byte == 0xFF )
+ return fiasco;
+
+ VERBOSE(" %s\n", type);
+
+ READ_OR_RETURN(fiasco, &length, 4);
+ length = ntohl(length);
+
+ /* unknown */
+ READ_OR_RETURN(fiasco, buf, 4);
+
+ VERBOSE(" size: %d bytes\n", length);
+ VERBOSE(" hash: %#04x\n", hash);
+ VERBOSE(" subsections: %d\n", count8);
+
+ memset(device, 0, sizeof(device));
+ memset(hwrevs, 0, sizeof(hwrevs));
+ memset(version, 0, sizeof(version));
+ memset(layout, 0, sizeof(layout));
+
+ while ( count8 > 0 ) {
+
+ READ_OR_RETURN(fiasco, &byte, 1);
+ READ_OR_RETURN(fiasco, &length8, 1);
+ READ_OR_RETURN(fiasco, buf, length8);
+
+ VERBOSE(" subinfo\n");
+ VERBOSE(" length: %d\n", length8);
+ VERBOSE(" type: ");
+
+ if ( byte == '1' ) {
+ memset(version, 0, sizeof(version));
+ strncpy(version, (char *)buf, length8);
+ VERBOSE("version string\n");
+ VERBOSE(" version: %s\n", version);
+ } else if ( byte == '2' ) {
+ int tmp = length8;
+ if ( tmp > 16 ) tmp = 16;
+ memset(device, 0, sizeof(device));
+ strncpy(device, (char *)buf, tmp);
+ VERBOSE("hw revision\n");
+ VERBOSE(" device: %s\n", device);
+ pbuf = buf + strlen(device) + 1;
+ while ( pbuf < buf + length8 ) {
+ while ( pbuf < buf + length8 && *pbuf < 32 )
+ ++pbuf;
+ if ( pbuf >= buf + length8 ) break;
+ tmp = buf + length8 - pbuf;
+ if ( tmp > 8 ) tmp = 8;
+ memset(hwrev, 0, sizeof(hwrev));
+ strncpy(hwrev, (char *)pbuf, tmp);
+ if ( ! hwrevs[0] )
+ strcpy(hwrevs, hwrev);
+ else {
+ /* TODO: check if hwrevs has enought size */
+ strcat(hwrevs, ",");
+ strcat(hwrevs, hwrev);
+ }
+ VERBOSE(" hw revision: %s\n", hwrev);
+ pbuf += strlen(hwrev) + 1;
+ }
+ } else if ( byte == '3' ) {
+ memset(layout, 0, sizeof(layout));
+ strncpy(layout, (char *)buf, length8);
+ VERBOSE("layout\n");
+ } else {
+ VERBOSE("unknown ('%c':%#x)\n", byte, byte);
+ }
+
+ --count8;
+ }
+
+ /* unknown */
+ READ_OR_RETURN(fiasco, buf, 1);
+
+ offset = lseek(fiasco->fd, 0, SEEK_CUR);
+
+ VERBOSE(" version: %s\n", version);
+ VERBOSE(" device: %s\n", device);
+ VERBOSE(" hwrevs: %s\n", hwrevs);
+ VERBOSE(" data at: %#08x\n", (unsigned int)offset);
+
+ image = image_alloc_from_shared_fd(fiasco->fd, length, offset, hash, type, device, hwrevs, version, layout);
+
+ if ( ! image )
+ FIASCO_READ_ERROR(fiasco, "Cannot allocate image");
+
+ fiasco_add_image(fiasco, image);
+
+ lseek(fiasco->fd, offset+length, SEEK_SET);
+
+ }
+
+ return fiasco;
+
+}
+
+void fiasco_free(struct fiasco * fiasco) {
+
+ struct image_list * list = fiasco->first;
+
+ while ( list ) {
+ struct image_list * next = list->next;
+ image_list_del(list);
+ list = next;
+ }
+
+ if ( fiasco->fd >= 0 )
+ close(fiasco->fd);
+
+ free(fiasco->orig_filename);
+
+ free(fiasco);
+
+}
+
+void fiasco_add_image(struct fiasco * fiasco, struct image * image) {
+
+ image_list_add(&fiasco->first, image);
+
+}
+
+int fiasco_write_to_file(struct fiasco * fiasco, const char * file) {
+
+ int fd = -1;
+ uint32_t size;
+ uint32_t length;
+ uint16_t hash;
+ uint8_t length8;
+ const char * str;
+ const char * type;
+ const char * device;
+ struct image_list * image_list;
+ struct image * image;
+ unsigned char buf[4096];
+
+ if ( ! fiasco )
+ return -1;
+
+ printf("Generating Fiasco image %s...\n", file);
+
+ if ( ! fiasco->first )
+ FIASCO_WRITE_ERROR(file, fd, "Nothing to write");
+
+ if ( fiasco->name && strlen(fiasco->name)+1 > UINT8_MAX )
+ FIASCO_WRITE_ERROR(file, fd, "Fiasco name string is too long");
+
+ if ( fiasco->swver && strlen(fiasco->swver)+1 > UINT8_MAX )
+ FIASCO_WRITE_ERROR(file, fd, "SW version string is too long");
+
+ if ( ! simulate ) {
+ fd = open(file, O_RDWR|O_CREAT|O_TRUNC, 0644);
+ if ( fd < 0 ) {
+ ERROR(errno, "Cannot create file");
+ return -1;
+ }
+ }
+
+ printf("Writing Fiasco header...\n");
+
+ WRITE_OR_FAIL(file, fd, "\xb4", 1); /* signature */
+
+ if ( fiasco->name[0] )
+ str = fiasco->name;
+ else
+ str = "OSSO UART+USB";
+
+ length = 4 + strlen(str) + 3;
+ if ( fiasco->swver[0] )
+ length += strlen(fiasco->swver) + 3;
+ length = htonl(length);
+ WRITE_OR_FAIL(file, fd, &length, 4); /* FW header length */
+
+ if ( fiasco->swver[0] )
+ length = htonl(2);
+ else
+ length = htonl(1);
+ WRITE_OR_FAIL(file, fd, &length, 4); /* FW header blocks count */
+
+ /* Fiasco name */
+ length8 = strlen(str)+1;
+ WRITE_OR_FAIL(file, fd, "\xe8", 1);
+ WRITE_OR_FAIL(file, fd, &length8, 1);
+ WRITE_OR_FAIL(file, fd, str, length8);
+
+ /* SW version */
+ if ( fiasco->swver[0] ) {
+ printf("Writing SW version: %s\n", fiasco->swver);
+ length8 = strlen(fiasco->swver)+1;
+ WRITE_OR_FAIL(file, fd, "\x31", 1);
+ WRITE_OR_FAIL(file, fd, &length8, 1);
+ WRITE_OR_FAIL(file, fd, fiasco->swver, length8);
+ };
+
+ printf("\n");
+
+ image_list = fiasco->first;
+
+ while ( image_list ) {
+
+ image = image_list->image;
+
+ if ( ! image )
+ FIASCO_WRITE_ERROR(file, fd, "Empty image");
+
+ printf("Writing image...\n");
+ image_print_info(image);
+
+ type = image_type_to_string(image->type);
+ device = device_to_string(image->device);
+
+ if ( ! type )
+ FIASCO_WRITE_ERROR(file, fd, "Unknown image type");
+
+ if ( image->version && strlen(image->version) > UINT8_MAX )
+ FIASCO_WRITE_ERROR(file, fd, "Image version string is too long");
+
+ if ( image->layout && strlen(image->layout) > UINT8_MAX )
+ FIASCO_WRITE_ERROR(file, fd, "Image layout is too long");
+
+ printf("Writing image header...\n");
+
+ /* signature */
+ WRITE_OR_FAIL(file, fd, "T", 1);
+
+ /* number of subsections */
+ length8 = 1;
+ if ( image->version )
+ ++length8;
+ if ( device )
+ ++length8;
+ if ( image->layout )
+ ++length8;
+ WRITE_OR_FAIL(file, fd, &length8, 1);
+
+ /* unknown */
+ WRITE_OR_FAIL(file, fd, "\x2e\x19\x01\x01\x00", 5);
+
+ /* checksum */
+ hash = htons(image->hash);
+ WRITE_OR_FAIL(file, fd, &hash, 2);
+
+ /* image type name */
+ memset(buf, 0, 12);
+ strncpy((char *)buf, type, 12);
+ WRITE_OR_FAIL(file, fd, buf, 12);
+
+ /* image size */
+ size = htonl(image->size);
+ WRITE_OR_FAIL(file, fd, &size, 4);
+
+ /* unknown */
+ WRITE_OR_FAIL(file, fd, "\x00\x00\x00\x00", 4);
+
+ /* append version subsection */
+ if ( image->version ) {
+ WRITE_OR_FAIL(file, fd, "1", 1); /* 1 - version */
+ length8 = strlen(image->version)+1;
+ WRITE_OR_FAIL(file, fd, &length8, 1);
+ WRITE_OR_FAIL(file, fd, image->version, length8);
+ }
+
+ /* append device & hwrevs subsection */
+ if ( device ) {
+ const char *ptr = image->hwrevs;
+ const char *oldptr = ptr;
+ int i;
+ WRITE_OR_FAIL(file, fd, "2", 1); /* 2 - device & hwrevs */
+ length8 = 16;
+ if ( image->hwrevs ) {
+ i = 1;
+ while ( (ptr = strchr(ptr, ',')) ) { i++; ptr++; }
+ if ( (int)length8 + i*8 > 255 ) {
+ FIASCO_WRITE_ERROR(file, fd, "Device string and HW revisions are too long");
+ }
+ length8 += i*8;
+ }
+ WRITE_OR_FAIL(file, fd, &length8, 1);
+ length8 = strlen(device);
+ if ( length8 > 15 ) length8 = 15;
+ WRITE_OR_FAIL(file, fd, device, length8);
+ for ( i = 0; i < 16 - length8; ++i )
+ WRITE_OR_FAIL(file, fd, "\x00", 1);
+ if ( image->hwrevs ) {
+ ptr = image->hwrevs;
+ oldptr = ptr;
+ while ( (ptr = strchr(ptr, ',')) ) {
+ length8 = ptr-oldptr;
+ if ( length8 > 8 ) length8 = 8;
+ WRITE_OR_FAIL(file, fd, oldptr, length8);
+ for ( i=0; i < 8 - length8; ++i )
+ WRITE_OR_FAIL(file, fd, "\x00", 1);
+ ++ptr;
+ oldptr = ptr;
+ }
+ length8 = strlen(oldptr);
+ if ( length8 > 8 ) length8 = 8;
+ WRITE_OR_FAIL(file, fd, oldptr, length8);
+ for ( i = 0; i < 8 - length8; ++i )
+ WRITE_OR_FAIL(file, fd, "\x00", 1);
+ }
+ }
+
+ /* append layout subsection */
+ if ( image->layout ) {
+ length8 = strlen(image->layout);
+ WRITE_OR_FAIL(file, fd, "3", 1); /* 3 - layout */
+ WRITE_OR_FAIL(file, fd, &length8, 1);
+ WRITE_OR_FAIL(file, fd, image->layout, length8);
+ }
+
+ /* dummy byte - end of all subsections */
+ WRITE_OR_FAIL(file, fd, "\x00", 1);
+
+ printf("Writing image data...\n");
+
+ image_seek(image, 0);
+ while ( 1 ) {
+ size = image_read(image, buf, sizeof(buf));
+ if ( size < 1 )
+ break;
+ WRITE_OR_FAIL(file, fd, buf, size);
+ }
+
+ image_list = image_list->next;
+
+ if ( image_list )
+ printf("\n");
+
+ }
+
+ close(fd);
+ printf("\nDone\n\n");
+ return 0;
+
+}
+
+int fiasco_unpack(struct fiasco * fiasco, const char * dir) {
+
+ int fd;
+ char * name;
+ char * layout_name;
+ struct image * image;
+ struct image_list * image_list;
+ uint32_t size;
+ char cwd[256];
+ unsigned char buf[4096];
+
+ if ( dir ) {
+
+ memset(cwd, 0, sizeof(cwd));
+
+ if ( ! getcwd(cwd, sizeof(cwd)) ) {
+ ERROR(errno, "Cannot store current directory");
+ return -1;
+ }
+
+ if ( chdir(dir) < 0 ) {
+ ERROR(errno, "Cannot change current directory to %s", dir);
+ return -1;
+ }
+
+ }
+
+ fiasco_print_info(fiasco);
+
+ image_list = fiasco->first;
+
+ while ( image_list ) {
+
+ image = image_list->image;
+
+ name = image_name_alloc_from_values(image);
+ if ( ! name )
+ return -1;
+
+ printf("\n");
+ printf("Unpacking image...\n");
+ image_print_info(image);
+
+ if ( image->layout ) {
+
+ layout_name = calloc(1, strlen(name) + strlen(".layout") + 1);
+ if ( ! layout_name )
+ ALLOC_ERROR_RETURN(-1);
+
+ sprintf(layout_name, "%s.layout", name);
+
+ printf(" Layout file: %s\n", layout_name);
+
+ }
+
+ printf(" Output file: %s\n", name);
+
+ fd = open(name, O_RDWR|O_CREAT|O_TRUNC, 0644);
+ if ( fd < 0 ) {
+ ERROR(errno, "Cannot create output file %s", name);
+ return -1;
+ }
+
+ free(name);
+
+ image_seek(image, 0);
+ while ( 1 ) {
+ size = image_read(image, buf, sizeof(buf));
+ if ( size < 1 )
+ break;
+ WRITE_OR_FAIL(name, fd, buf, size);
+ }
+
+ close(fd);
+
+ if ( image->layout ) {
+
+ fd = open(layout_name, O_RDWR|O_CREAT|O_TRUNC, 0644);
+ if ( fd < 0 ) {
+ ERROR(errno, "Cannot create layout file %s", layout_name);
+ return -1;
+ }
+
+ free(layout_name);
+
+ WRITE_OR_FAIL(layout_name, fd, image->layout, (int)strlen(image->layout));
+
+ close(fd);
+
+ }
+
+ image_list = image_list->next;
+
+ }
+
+ if ( dir ) {
+ if ( chdir(cwd) < 0 ) {
+ ERROR(errno, "Cannot change current directory back to %s", cwd);
+ return -1;
+ }
+ }
+
+ printf("\nDone\n\n");
+ return 0;
+
+}
+
+void fiasco_print_info(struct fiasco * fiasco) {
+
+ if ( fiasco->orig_filename )
+ printf("File: %s\n", fiasco->orig_filename);
+
+ if ( fiasco->name[0] )
+ printf(" Fiasco Name: %s\n", fiasco->name);
+
+ if ( fiasco->swver[0] )
+ printf(" Fiasco Software release version: %s\n", fiasco->swver);
+
+}