Initial import master
authorAntonio Ospite <ao2@ao2.it>
Sun, 1 Jun 2014 20:13:48 +0000 (22:13 +0200)
committerAntonio Ospite <ao2@ao2.it>
Sun, 1 Jun 2014 20:13:48 +0000 (22:13 +0200)
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
actions-firmware-extract.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..d891fd7
--- /dev/null
@@ -0,0 +1,2 @@
+actions-firmware-extract.o
+actions-firmware-extract
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..f2e65c8
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,54 @@
+CFLAGS ?= -std=c99 -pedantic -Wall -Wextra -O2
+
+CFLAGS += -pedantic-errors
+
+# GCC >= 4.6
+CFLAGS += -Wunused-but-set-variable
+
+CFLAGS += -fno-common \
+  -Wall \
+  -Wextra \
+  -Wformat=2 \
+  -Winit-self \
+  -Winline \
+  -Wpacked \
+  -Wp,-D_FORTIFY_SOURCE=2 \
+  -Wpointer-arith \
+  -Wlarger-than-65500 \
+  -Wmissing-declarations \
+  -Wmissing-format-attribute \
+  -Wmissing-noreturn \
+  -Wmissing-prototypes \
+  -Wnested-externs \
+  -Wold-style-definition \
+  -Wredundant-decls \
+  -Wsign-compare \
+  -Wstrict-aliasing=2 \
+  -Wstrict-prototypes \
+  -Wswitch-enum \
+  -Wundef \
+  -Wunreachable-code \
+  -Wunsafe-loop-optimizations \
+  -Wwrite-strings
+
+# for some functions in endian.h
+CFLAGS += -D_BSD_SOURCE
+
+# for getopt()
+CFLAGS += -D_POSIX_C_SOURCE=2
+
+PREFIX ?= /usr/local
+bindir := $(PREFIX)/sbin
+
+actions-firmware-extract: actions-firmware-extract.o
+
+test: actions-firmware-extract
+       rm -rf PPX2230 && \
+       ./actions-firmware-extract -d PPX2230 ppx2230_eu_fus_aen.bin
+
+install: actions-firmware-extract
+       install -d $(DESTDIR)$(bindir)
+       install -m 755 actions-firmware-extract $(DESTDIR)$(bindir)
+
+clean:
+       rm -rf *~ *.o actions-firmware-extract endian endian.h
diff --git a/actions-firmware-extract.c b/actions-firmware-extract.c
new file mode 100644 (file)
index 0000000..2deeb8f
--- /dev/null
@@ -0,0 +1,348 @@
+/* actions-firmware-extract - extract firmwares like the one for Acer K330
+ *
+ * Copyright (C) 2014  Antonio Ospite <ao2@ao2.it>
+ *
+ * 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 <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include <endian.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+
+/*
+ * Based on cdt_parse.c from OpenEZX:
+ * http://cgit.openezx.org/moto-boot-usb/tree/src/cdt_parse.c
+ */
+
+#define ACTIONS_HEADER_DISK_SIZE 64
+struct actions_firmware_header {
+       uint8_t magic_string[16];
+       uint8_t version_string[16];
+       uint8_t checksum_string[16];
+       uint32_t base_address;
+       uint32_t length;
+       uint32_t unknown0;
+       uint32_t unknown1;
+};
+
+#define ACTIONS_SECTION_DISK_SIZE 32
+struct actions_firmware_section {
+       uint8_t name[16];
+       uint32_t start_address;
+       uint32_t length;
+       uint32_t unknown0;
+       uint32_t unknown1;
+};
+
+#define FIRM_TABLE_OFFSET 0x200
+#define FIRM_DATA_OFFSET 0x2000
+
+static void get_string(uint8_t **srcp, uint8_t *dest, unsigned int len)
+{
+       memcpy(dest, *srcp, len);
+       dest[len - 1] = '\0';
+       *srcp += len;
+}
+
+static inline uint16_t get_le16(uint8_t **bufferp)
+{
+       uint16_t tmp;
+
+       memcpy(&tmp, *bufferp, sizeof (tmp));
+       *bufferp += sizeof (tmp);
+
+       return le16toh(tmp);
+}
+
+static inline uint32_t get_le32(uint8_t **bufferp)
+{
+       uint32_t tmp;
+
+       memcpy(&tmp, *bufferp, sizeof (tmp));
+       *bufferp += sizeof (tmp);
+
+       return le32toh(tmp);
+}
+
+static inline uint64_t get_le64(uint8_t **bufferp)
+{
+       uint64_t tmp;
+
+       memcpy(&tmp, *bufferp, sizeof (tmp));
+       *bufferp += sizeof (tmp);
+
+       return le64toh(tmp);
+}
+
+static int parse_header(uint8_t **bufferp, struct actions_firmware_header *header)
+{
+       get_string(bufferp, header->magic_string, 16);
+
+       if (strncmp((char *)header->magic_string, "ActionsFirmware", 16) != 0) {
+               fprintf(stderr, "Not an Actions firmware.\n");
+               return -EINVAL;
+       }
+
+       get_string(bufferp, header->version_string, 16);
+       get_string(bufferp, header->checksum_string, 16);
+
+       header->base_address = get_le32(bufferp);
+       header->length = get_le32(bufferp);
+       header->unknown0 = get_le32(bufferp);
+       header->unknown1 = get_le32(bufferp);
+
+       return 0;
+}
+
+static int parse_section(uint8_t **bufferp, struct actions_firmware_section *section)
+{
+       get_string(bufferp, section->name, 16);
+
+       if (section->name[0] == '\0')
+               return -EINVAL;
+
+       section->start_address = get_le32(bufferp);
+       section->length = get_le32(bufferp);
+       section->unknown0 = get_le32(bufferp);
+       section->unknown1 = get_le32(bufferp);
+
+       return 0;
+}
+
+static void print_header(struct actions_firmware_header *header)
+{
+       printf("Magic: %s\n"
+              "Version: %s\n"
+              "Checksum: %s\n"
+              "Base address: %d\n"
+              "Content length: %d\n"
+              "unknown0: 0x%08x (%u)\n"
+              "unknown1: 0x%08x (%u)\n"
+              "Complete file size: %d\n",
+              header->magic_string,
+              header->version_string,
+              header->checksum_string,
+              header->base_address,
+              header->length,
+              header->unknown0, header->unknown0,
+              header->unknown1, header->unknown1,
+              header->base_address + header->length);
+}
+
+static void print_section(struct actions_firmware_section *section)
+{
+       printf("| %-16s | 0x%08x | 0x%08x | 0x%08x | 0x%08x |\n",
+              section->name,
+              section->start_address,
+              section->length,
+              section->unknown0,
+              section->unknown1);
+}
+
+static int dump_section_data(const char *path_prefix,
+                            const char *name_prefix,
+                            uint8_t *buffer,
+                            struct actions_firmware_section *section)
+{
+       int fd;
+       int ret;
+       char path[PATH_MAX] = { 0 };
+       ssize_t written;
+
+       snprintf(path, PATH_MAX, "%s/%s%s",
+                path_prefix, name_prefix ? name_prefix : "", (char *)section->name);
+
+       fd = creat(path, 0644);
+       if (fd < 0) {
+               perror("creat");
+               ret = -errno;
+               goto out;
+       }
+
+       written = write(fd, buffer + section->start_address, section->length);
+       if (written < 0) {
+               perror("write");
+               ret = -errno;
+               goto out_close_fd;
+       }
+
+       ret = 0;
+
+out_close_fd:
+       close(fd);
+out:
+       return ret;
+}
+
+static void usage(const char *name)
+{
+       printf("usage: %s [OPTIONS] <fimrware.bin>\n", name);
+       printf("OPTIONS:\n");
+       printf("\t-d <dest_dir>\t\tdump the firmware sections into the <dest_dir> directory\n");
+       printf("\t-h \t\t\tthis help message\n");
+       printf("\nEXAMPLE OF USE:\n");
+       printf("\t%s -d PPX2230 ppx2230_eu_fus_aen.bin\n", name);
+}
+
+int main(int argc, char *argv[])
+{
+       int ret;
+       int opt;
+
+       int fd;
+       struct stat st;
+       size_t size;
+       uint8_t *buffer;
+       uint8_t *buffer_iterator;
+       struct actions_firmware_header header;
+       struct actions_firmware_section section;
+       unsigned int i;
+       const char *path_prefix = NULL;
+
+       while ((opt = getopt(argc, argv, "d:h")) != -1) {
+               switch (opt) {
+               case 'd':
+                       path_prefix = optarg;
+                       ret = mkdir(path_prefix, 0755);
+                       if (ret < 0) {
+                               fprintf(stderr, "Cannot create \"%s\", if it exists remove it first.\n", path_prefix);
+                               goto out;
+                       }
+                       break;
+               case 'h':
+                       usage(argv[0]);
+                       ret = 0;
+                       goto out;
+               default: /* '?' */
+                       usage(argv[0]);
+                       ret = -EINVAL;
+                       goto out;
+               }
+       }
+
+       if (argv[optind] == NULL) {
+               usage(argv[0]);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       fd = open(argv[optind], O_RDONLY);
+       if (fd < 0) {
+               perror("open");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       ret = fstat(fd, &st);
+       if (ret < 0) {
+               perror("fstat");
+               goto out_close_fd;
+       }
+       size = (size_t)st.st_size;
+
+       buffer = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
+       if (buffer == NULL) {
+               perror("mmap");
+               ret = -errno;
+               goto out_close_fd;
+       }
+
+       if (size < ACTIONS_HEADER_DISK_SIZE) {
+               fprintf(stderr, "File cannot contain an Actions firmware header.\n");
+               ret = -EINVAL;
+               goto out_munmap;
+       }
+
+       /* iterate over a copy of the pointer */
+       buffer_iterator = buffer;
+
+       ret = parse_header(&buffer_iterator, &header);
+       if (ret < 0) {
+               fprintf(stderr, "Cannot parse the header.\n");
+               goto out_munmap;
+       }
+
+       print_header(&header);
+
+       i = 0;
+       while (parse_section(&buffer_iterator, &section) == 0) {
+               print_section(&section);
+               i++;
+
+               if (strncmp((char *)section.name, "FIRM", 16) == 0) {
+                       uint8_t *firm_buffer_iterator = buffer + section.start_address + FIRM_TABLE_OFFSET;
+                       struct actions_firmware_section firm_section;
+                       struct actions_firmware_section prev_section = {
+                               .start_address = section.start_address + FIRM_DATA_OFFSET,
+                               .length = 0,
+                       };
+                       while (parse_section(&firm_buffer_iterator, &firm_section) == 0) {
+                               /*
+                                * unknown1 seems to be some form of checksum for
+                                * firm sections, if a sections have the same
+                                * checksum of the previous one they are not
+                                * duplicated but refer to the same memory
+                                * region, so do not increase the start
+                                * address.
+                                */
+                               if (firm_section.unknown1 == prev_section.unknown1) {
+                                       firm_section.start_address = prev_section.start_address;
+                               } else {
+                                       firm_section.start_address = prev_section.start_address + prev_section.length;
+                               }
+
+                               printf("\t");
+                               print_section(&firm_section);
+
+                               if (path_prefix)
+                                       dump_section_data(path_prefix,
+                                                         "FIRM_",
+                                                         buffer,
+                                                         &firm_section);
+
+                               prev_section = firm_section;
+                       }
+               } else if (strncmp((char *)section.name, "LINUX", 16) == 0) {
+                       continue;
+               }
+
+               if (path_prefix)
+                       dump_section_data(path_prefix, NULL, buffer, &section);
+
+       }
+       printf("Found %d sections.\n", i);
+
+       ret = 0;
+
+out_munmap:
+       ret = munmap(buffer, size);
+       if (ret < 0)
+               perror("munmap");
+
+out_close_fd:
+       ret = close(fd);
+       if (ret < 0)
+               perror("close");
+
+out:
+       return ret;
+}