/*************************************************************************** * __________ __ ___. * 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 #include #include #include #include #include #include #include #include #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 _ERR(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) _ERR("Could not open %s for reading", filename); if (fstat(fd, &s) < 0) _ERR("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)) _ERR("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) _ERR("Model name \"%4.4s\" unknown. " "Is this really a rockbox bootloader?", header + 4); *model = &ipod_identity[i]; } buf = malloc(*bufsize); if (buf == NULL) _ERR("Could not allocate memory for %s", filename); if (read(fd, buf, *bufsize) != *bufsize) _ERR("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)) _ERR("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) _ERR("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) _ERR("DFU image (%d bytes) too big", *dfu_size); dfu_buf = calloc(*dfu_size, 1); if (!dfu_buf) _ERR("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; }