2016-02-04 22:05:17 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2017-05-04 08:52:03 +00:00
|
|
|
#define _ERR(format, ...) \
|
2016-02-04 22:05:17 +00:00
|
|
|
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)
|
2017-05-04 08:52:03 +00:00
|
|
|
_ERR("Could not open %s for reading", filename);
|
2016-02-04 22:05:17 +00:00
|
|
|
|
|
|
|
if (fstat(fd, &s) < 0)
|
2017-05-04 08:52:03 +00:00
|
|
|
_ERR("Checking filesize of input file %s", filename);
|
2016-02-04 22:05:17 +00:00
|
|
|
*bufsize = s.st_size;
|
|
|
|
|
|
|
|
if (is_rbbl) {
|
|
|
|
/* Read Rockbox header */
|
|
|
|
if (read(fd, header, sizeof(header)) != sizeof(header))
|
2017-05-04 08:52:03 +00:00
|
|
|
_ERR("Could not read file %s", filename);
|
2016-02-04 22:05:17 +00:00
|
|
|
*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)
|
2017-05-04 08:52:03 +00:00
|
|
|
_ERR("Model name \"%4.4s\" unknown. "
|
2016-02-04 22:05:17 +00:00
|
|
|
"Is this really a rockbox bootloader?", header + 4);
|
|
|
|
|
|
|
|
*model = &ipod_identity[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
buf = malloc(*bufsize);
|
|
|
|
if (buf == NULL)
|
2017-05-04 08:52:03 +00:00
|
|
|
_ERR("Could not allocate memory for %s", filename);
|
2016-02-04 22:05:17 +00:00
|
|
|
|
|
|
|
if (read(fd, buf, *bufsize) != *bufsize)
|
2017-05-04 08:52:03 +00:00
|
|
|
_ERR("Could not read file %s", filename);
|
2016-02-04 22:05:17 +00:00
|
|
|
|
|
|
|
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))
|
2017-05-04 08:52:03 +00:00
|
|
|
_ERR("Checksum mismatch in %s", filename);
|
2016-02-04 22:05:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2017-05-04 08:52:03 +00:00
|
|
|
_ERR("Platform name \"%s\" unknown", dfu_arg);
|
2016-02-04 22:05:17 +00:00
|
|
|
|
|
|
|
*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)
|
2017-05-04 08:52:03 +00:00
|
|
|
_ERR("DFU image (%d bytes) too big", *dfu_size);
|
2016-02-04 22:05:17 +00:00
|
|
|
|
|
|
|
dfu_buf = calloc(*dfu_size, 1);
|
|
|
|
if (!dfu_buf)
|
2017-05-04 08:52:03 +00:00
|
|
|
_ERR("Could not allocate %d bytes for DFU image", *dfu_size);
|
2016-02-04 22:05:17 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|