summaryrefslogtreecommitdiffstats
path: root/rbutil/mks5lboot/mkdfu.c
diff options
context:
space:
mode:
authorCástor Muñoz <cmvidal@gmail.com>2016-02-04 23:05:17 +0100
committerCástor Muñoz <cmvidal@gmail.com>2017-04-14 00:03:42 +0200
commit346423c040fe4ac31dae7c1afcb1d853cc80635c (patch)
treebd8dd4c55f083a1162f7019188977213a20dc41c /rbutil/mks5lboot/mkdfu.c
parenteefc7c73e2495decdc6f242515696fe0e3f85609 (diff)
downloadrockbox-346423c040fe4ac31dae7c1afcb1d853cc80635c.tar.gz
rockbox-346423c040fe4ac31dae7c1afcb1d853cc80635c.zip
mks5lboot v1.0 - dualboot installer for s5l8702 targetsmks5lboot_1.0
A tool to install/uninstall a bootloader into a s5l8702 based device: - iPod Classic 6G - iPod Nano 3G (TODO) See mks5lboot/README for detailed info. Change-Id: I451d2aaff34509ebd356e4660647e5222c5d3409
Diffstat (limited to 'rbutil/mks5lboot/mkdfu.c')
-rw-r--r--rbutil/mks5lboot/mkdfu.c318
1 files changed, 318 insertions, 0 deletions
diff --git a/rbutil/mks5lboot/mkdfu.c b/rbutil/mks5lboot/mkdfu.c
new file mode 100644
index 0000000000..6ac0daf1ac
--- /dev/null
+++ b/rbutil/mks5lboot/mkdfu.c
@@ -0,0 +1,318 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2015 by Cástor Muñoz
+ *
+ * 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 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "mks5lboot.h"
+
+/* Header for ARM code binaries */
+#include "dualboot.h"
+
+/* Win32 compatibility */
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/*
+ * This code is based on emCORE, adds a couple of improvements thanks to
+ * some extra features in Apple's ROM:
+ * - Generic Im3Info header valid for all payloads. It is done by moving
+ * the certificate to a fixed position (before data), and using a 'magic'
+ * value (0x300) for signature offset.
+ * - Some integer overflows in ROM make it possible to use the free space
+ * available in Im3Hdr, resulting a maximum payload size of:
+ * 128 KiB - 0x50 bytes (IM3INFO_SZ) - 700 bytes (CERT_SIZE).
+ */
+
+const struct ipod_models ipod_identity[] =
+{
+ [MODEL_IPOD6G] = {
+ "Classic 6G", "ipod6g", "ip6g", 71,
+ dualboot_install_ipod6g, sizeof(dualboot_install_ipod6g),
+ dualboot_uninstall_ipod6g, sizeof(dualboot_uninstall_ipod6g) },
+};
+
+struct Im3Info s5l8702hdr =
+{
+ .ident = IM3_IDENT,
+ .version = IM3_VERSION,
+ .enc_type = 3,
+ .u.enc34 = {
+ .sign_off = "\x00\x03\x00\x00",
+ .cert_off = "\x50\xF8\xFF\xFF", /* -0x800 + CERT_OFFSET */
+ .cert_sz = "\xBA\x02\x00\x00", /* CERT_SIZE */
+ },
+ .info_sign = "\xC2\x54\x51\x31\xDC\xC0\x84\xA4"
+ "\x7F\xD1\x45\x08\xE8\xFF\xE8\x1D",
+};
+
+unsigned char s5l8702pwnage[/*CERT_SIZE*/] =
+{
+ "\x30\x82\x00\x7A\x30\x66\x02\x00\x30\x0D\x06\x09\x2A\x86\x48\x86"
+ "\xF7\x0D\x01\x01\x05\x05\x00\x30\x0B\x31\x09\x30\x07\x06\x03\x55"
+ "\x04\x03\x13\x00\x30\x1E\x17\x0D\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x17\x0D\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x30\x0B\x31\x09\x30\x07\x06\x03\x55\x04\x03\x13"
+ "\x00\x30\x19\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01"
+ "\x05\x00\x03\x08\x00\x30\x05\x02\x01\x00\x02\x00\x30\x0D\x06\x09"
+ "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x05\x05\x00\x03\x01\x00\x30\x82"
+ "\x00\x7A\x30\x66\x02\x00\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D"
+ "\x01\x01\x05\x05\x00\x30\x0B\x31\x09\x30\x07\x06\x03\x55\x04\x03"
+ "\x13\x00\x30\x1E\x17\x0D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x17\x0D\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x30\x0B\x31\x09\x30\x07\x06\x03\x55\x04\x03\x13\x00\x30"
+ "\x19\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01\x05\x00"
+ "\x03\x08\x00\x30\x05\x02\x01\x00\x02\x00\x30\x0D\x06\x09\x2A\x86"
+ "\x48\x86\xF7\x0D\x01\x01\x05\x05\x00\x03\x01\x00\x30\x82\x01\xBA"
+ "\x30\x50\x02\x00\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01"
+ "\x05\x05\x00\x30\x00\x30\x1E\x17\x0D\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x17\x0D\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x30\x00\x30\x19\x30\x0D\x06\x09\x2A\x86\x48"
+ "\x86\xF7\x0D\x01\x01\x01\x05\x00\x03\x08\x00\x30\x05\x02\x01\x00"
+ "\x02\x00\x30\x0D\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x01\x05\x05"
+ "\x00\x03\x82\x01\x55"
+};
+
+static uint32_t get_uint32le(unsigned char* p)
+{
+ return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+}
+
+static uint32_t get_uint32be(unsigned char* p)
+{
+ return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+}
+
+static void put_uint32le(unsigned char* p, uint32_t x)
+{
+ p[0] = x & 0xff;
+ p[1] = (x >> 8) & 0xff;
+ p[2] = (x >> 16) & 0xff;
+ p[3] = (x >> 24) & 0xff;
+}
+
+#define ERROR(format, ...) \
+ do { \
+ snprintf(errstr, errstrsize, "[ERR] "format, __VA_ARGS__); \
+ goto error; \
+ } while(0)
+
+static unsigned char *load_file(char *filename, int *bufsize,
+ const struct ipod_models** model,
+ char* errstr, int errstrsize)
+{
+ int fd, i;
+ struct stat s;
+ unsigned char header[8];
+ unsigned char *buf = NULL;
+ bool is_rbbl = (model);
+
+ fd = open(filename, O_RDONLY|O_BINARY);
+ if (fd < 0)
+ ERROR("Could not open %s for reading", filename);
+
+ if (fstat(fd, &s) < 0)
+ ERROR("Checking filesize of input file %s", filename);
+ *bufsize = s.st_size;
+
+ if (is_rbbl) {
+ /* Read Rockbox header */
+ if (read(fd, header, sizeof(header)) != sizeof(header))
+ ERROR("Could not read file %s", filename);
+ *bufsize -= sizeof(header);
+
+ for (i = 0; i < NUM_MODELS; i++)
+ if (memcmp(ipod_identity[i].rb_model_name, header + 4, 4) == 0)
+ break;
+
+ if (i == NUM_MODELS)
+ ERROR("Model name \"%4.4s\" unknown. "
+ "Is this really a rockbox bootloader?", header + 4);
+
+ *model = &ipod_identity[i];
+ }
+
+ buf = malloc(*bufsize);
+ if (buf == NULL)
+ ERROR("Could not allocate memory for %s", filename);
+
+ if (read(fd, buf, *bufsize) != *bufsize)
+ ERROR("Could not read file %s", filename);
+
+ if (is_rbbl) {
+ /* Check checksum */
+ uint32_t sum = (*model)->rb_model_num;
+ for (i = 0; i < *bufsize; i++) {
+ /* add 8 unsigned bits but keep a 32 bit sum */
+ sum += buf[i];
+ }
+ if (sum != get_uint32be(header))
+ ERROR("Checksum mismatch in %s", filename);
+ }
+
+ close(fd);
+ return buf;
+
+error:
+ if (fd >= 0)
+ close(fd);
+ if (buf)
+ free(buf);
+ return NULL;
+}
+
+unsigned char *mkdfu(int dfu_type, char *dfu_arg, int* dfu_size,
+ char* errstr, int errstrsize)
+{
+ const struct ipod_models *model = NULL;
+ unsigned char *dfu_buf = NULL;
+ unsigned char *f_buf;
+ int f_size;
+ uint32_t padded_bl_size;
+ uint32_t cert_off, cert_sz;
+ off_t cur_off;
+ char *dfu_desc;
+ int i;
+
+ switch (dfu_type)
+ {
+ case DFU_INST:
+ case DFU_INST_SINGLE:
+ f_buf = load_file(dfu_arg, &f_size, &model, errstr, errstrsize);
+ if (f_buf == NULL)
+ goto error;
+ /* IM3 data size should be padded to 16 */
+ padded_bl_size = ((f_size + 0xf) & ~0xf);
+ *dfu_size = BIN_OFFSET + (model->dualboot_install_size +
+ (IM3HDR_SZ - (int)IM3INFO_SZ) + (int)padded_bl_size);
+ dfu_desc = (dfu_type == DFU_INST_SINGLE) ? "BL installer (single)"
+ : "BL installer";
+ break;
+
+ case DFU_UNINST:
+ for (i = 0; i < NUM_MODELS; i++) {
+ if (!strcmp(ipod_identity[i].platform_name, dfu_arg)) {
+ model = &ipod_identity[i];
+ break;
+ }
+ }
+ if (!model)
+ ERROR("Platform name \"%s\" unknown", dfu_arg);
+
+ *dfu_size = BIN_OFFSET + model->dualboot_uninstall_size;
+ dfu_desc = "BL uninstaller";
+ break;
+
+ case DFU_RAW:
+ default:
+ f_buf = load_file(dfu_arg, &f_size, NULL, errstr, errstrsize);
+ if (f_buf == NULL)
+ goto error;
+ /* IM3 data size should be padded to 16 */
+ *dfu_size = BIN_OFFSET + ((f_size + 0xf) & ~0xf);
+ dfu_desc = "raw binary";
+ break;
+ }
+
+ printf("[INFO] Building DFU:\n");
+ printf("[INFO] type: %s\n", dfu_desc);
+ if ((dfu_type == DFU_INST) || (dfu_type == DFU_INST_SINGLE))
+ printf("[INFO] BL size: %d\n", f_size);
+ if (dfu_type == DFU_RAW)
+ printf("[INFO] BIN size: %d\n", f_size);
+ printf("[INFO] DFU size: %d\n", *dfu_size);
+ if (model) {
+ printf("[INFO] model name: %s\n", model->model_name);
+ printf("[INFO] platform: %s\n", model->platform_name);
+ printf("[INFO] RB name: %s\n", model->rb_model_name);
+ printf("[INFO] RB num: %d\n", model->rb_model_num);
+ }
+
+ if (*dfu_size > DFU_MAXSIZE)
+ ERROR("DFU image (%d bytes) too big", *dfu_size);
+
+ dfu_buf = calloc(*dfu_size, 1);
+ if (!dfu_buf)
+ ERROR("Could not allocate %d bytes for DFU image", *dfu_size);
+
+ cert_off = get_uint32le(s5l8702hdr.u.enc34.cert_off);
+ cert_sz = get_uint32le(s5l8702hdr.u.enc34.cert_sz);
+
+ memcpy(dfu_buf, &s5l8702hdr, sizeof(s5l8702hdr));
+
+ cur_off = IM3HDR_SZ + cert_off;
+
+ memcpy(dfu_buf + cur_off, s5l8702pwnage, sizeof(s5l8702pwnage));
+
+ /* set entry point */
+ cur_off += cert_sz - 4;
+ put_uint32le(dfu_buf + cur_off, DFU_LOADADDR + BIN_OFFSET);
+
+ cur_off = BIN_OFFSET;
+
+ switch (dfu_type)
+ {
+ case DFU_INST:
+ case DFU_INST_SINGLE:
+ /* copy the dualboot installer binary */
+ memcpy(dfu_buf + cur_off, model->dualboot_install,
+ model->dualboot_install_size);
+ /* point to the start of the included IM3 header info */
+ cur_off += model->dualboot_install_size - IM3INFO_SZ;
+ /* set bootloader data size */
+ struct Im3Info *bl_info = (struct Im3Info*)(dfu_buf + cur_off);
+ put_uint32le(bl_info->data_sz, padded_bl_size);
+ /* use info_sign to pass params to the dualboot installer */
+ if (dfu_type == DFU_INST_SINGLE)
+ bl_info->info_sign[0] = 0x1;
+ /* add bootloader binary */
+ cur_off += IM3HDR_SZ;
+ memcpy(dfu_buf + cur_off, f_buf, f_size);
+ break;
+
+ case DFU_UNINST:
+ /* copy the dualboot uninstaller binary */
+ memcpy(dfu_buf + cur_off, model->dualboot_uninstall,
+ model->dualboot_uninstall_size);
+ break;
+
+ case DFU_RAW:
+ default:
+ /* add raw binary */
+ memcpy(dfu_buf + cur_off, f_buf, f_size);
+ break;
+ }
+
+ return dfu_buf;
+
+error:
+ if (dfu_buf)
+ free(dfu_buf);
+ return NULL;
+}