1 /* actions-firmware-extract - extract firmwares like the one for Acer K330
3 * Copyright (C) 2014 Antonio Ospite <ao2@ao2.it>
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
32 * Based on cdt_parse.c from OpenEZX:
33 * http://cgit.openezx.org/moto-boot-usb/tree/src/cdt_parse.c
36 #define ACTIONS_HEADER_DISK_SIZE 64
37 struct actions_firmware_header {
38 uint8_t magic_string[16];
39 uint8_t version_string[16];
40 uint8_t checksum_string[16];
41 uint32_t base_address;
47 #define ACTIONS_SECTION_DISK_SIZE 32
48 struct actions_firmware_section {
50 uint32_t start_address;
56 #define FIRM_TABLE_OFFSET 0x200
57 #define FIRM_DATA_OFFSET 0x2000
59 static void get_string(uint8_t **srcp, uint8_t *dest, unsigned int len)
61 memcpy(dest, *srcp, len);
66 static inline uint16_t get_le16(uint8_t **bufferp)
70 memcpy(&tmp, *bufferp, sizeof (tmp));
71 *bufferp += sizeof (tmp);
76 static inline uint32_t get_le32(uint8_t **bufferp)
80 memcpy(&tmp, *bufferp, sizeof (tmp));
81 *bufferp += sizeof (tmp);
86 static inline uint64_t get_le64(uint8_t **bufferp)
90 memcpy(&tmp, *bufferp, sizeof (tmp));
91 *bufferp += sizeof (tmp);
96 static int parse_header(uint8_t **bufferp, struct actions_firmware_header *header)
98 get_string(bufferp, header->magic_string, 16);
100 if (strncmp((char *)header->magic_string, "ActionsFirmware", 16) != 0) {
101 fprintf(stderr, "Not an Actions firmware.\n");
105 get_string(bufferp, header->version_string, 16);
106 get_string(bufferp, header->checksum_string, 16);
108 header->base_address = get_le32(bufferp);
109 header->length = get_le32(bufferp);
110 header->unknown0 = get_le32(bufferp);
111 header->unknown1 = get_le32(bufferp);
116 static int parse_section(uint8_t **bufferp, struct actions_firmware_section *section)
118 get_string(bufferp, section->name, 16);
120 if (section->name[0] == '\0')
123 section->start_address = get_le32(bufferp);
124 section->length = get_le32(bufferp);
125 section->unknown0 = get_le32(bufferp);
126 section->unknown1 = get_le32(bufferp);
131 static void print_header(struct actions_firmware_header *header)
137 "Content length: %d\n"
138 "unknown0: 0x%08x (%u)\n"
139 "unknown1: 0x%08x (%u)\n"
140 "Complete file size: %d\n",
141 header->magic_string,
142 header->version_string,
143 header->checksum_string,
144 header->base_address,
146 header->unknown0, header->unknown0,
147 header->unknown1, header->unknown1,
148 header->base_address + header->length);
151 static void print_section(struct actions_firmware_section *section)
153 printf("| %-16s | 0x%08x | 0x%08x | 0x%08x | 0x%08x |\n",
155 section->start_address,
161 static int dump_section_data(const char *path_prefix,
162 const char *name_prefix,
164 struct actions_firmware_section *section)
168 char path[PATH_MAX] = { 0 };
171 snprintf(path, PATH_MAX, "%s/%s%s",
172 path_prefix, name_prefix ? name_prefix : "", (char *)section->name);
174 fd = creat(path, 0644);
181 written = write(fd, buffer + section->start_address, section->length);
196 static void usage(const char *name)
198 printf("usage: %s [OPTIONS] <fimrware.bin>\n", name);
199 printf("OPTIONS:\n");
200 printf("\t-d <dest_dir>\t\tdump the firmware sections into the <dest_dir> directory\n");
201 printf("\t-h \t\t\tthis help message\n");
202 printf("\nEXAMPLE OF USE:\n");
203 printf("\t%s -d PPX2230 ppx2230_eu_fus_aen.bin\n", name);
206 int main(int argc, char *argv[])
215 uint8_t *buffer_iterator;
216 struct actions_firmware_header header;
217 struct actions_firmware_section section;
219 const char *path_prefix = NULL;
221 while ((opt = getopt(argc, argv, "d:h")) != -1) {
224 path_prefix = optarg;
225 ret = mkdir(path_prefix, 0755);
227 fprintf(stderr, "Cannot create \"%s\", if it exists remove it first.\n", path_prefix);
242 if (argv[optind] == NULL) {
248 fd = open(argv[optind], O_RDONLY);
255 ret = fstat(fd, &st);
260 size = (size_t)st.st_size;
262 buffer = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
263 if (buffer == NULL) {
269 if (size < ACTIONS_HEADER_DISK_SIZE) {
270 fprintf(stderr, "File cannot contain an Actions firmware header.\n");
275 /* iterate over a copy of the pointer */
276 buffer_iterator = buffer;
278 ret = parse_header(&buffer_iterator, &header);
280 fprintf(stderr, "Cannot parse the header.\n");
284 print_header(&header);
287 while (parse_section(&buffer_iterator, §ion) == 0) {
288 print_section(§ion);
291 if (strncmp((char *)section.name, "FIRM", 16) == 0) {
292 uint8_t *firm_buffer_iterator = buffer + section.start_address + FIRM_TABLE_OFFSET;
293 struct actions_firmware_section firm_section;
294 struct actions_firmware_section prev_section = {
295 .start_address = section.start_address + FIRM_DATA_OFFSET,
298 while (parse_section(&firm_buffer_iterator, &firm_section) == 0) {
300 * unknown1 seems to be some form of checksum for
301 * firm sections, if a sections have the same
302 * checksum of the previous one they are not
303 * duplicated but refer to the same memory
304 * region, so do not increase the start
307 if (firm_section.unknown1 == prev_section.unknown1) {
308 firm_section.start_address = prev_section.start_address;
310 firm_section.start_address = prev_section.start_address + prev_section.length;
314 print_section(&firm_section);
317 dump_section_data(path_prefix,
322 prev_section = firm_section;
324 } else if (strncmp((char *)section.name, "LINUX", 16) == 0) {
329 dump_section_data(path_prefix, NULL, buffer, §ion);
332 printf("Found %d sections.\n", i);
337 ret = munmap(buffer, size);