summaryrefslogtreecommitdiffstats
path: root/utils/mks5lboot/dualboot/dualboot.c
blob: b4446769ca591c61df3c25966bbec1cd3cbe70ef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
/***************************************************************************
 *             __________               __   ___.
 *   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 <stdint.h>
#include <string.h>

#include "config.h"
#include "system.h"
#include "button.h"

#include "s5l8702.h"
#include "clocking-s5l8702.h"
#include "spi-s5l8702.h"
#include "nor-target.h"
#include "piezo.h"

/* How it works:
 *
 * - dualboot-installer: installs or updates a RB bootloader, the bootloader
 *   to install/update is already included into dualboot-installer.dfu file,
 *   once it is executed by the iPod device:
 *
 *   1) locates an original NORBOOT (ONB): first it looks at offset=32KB, if
 *      a NORBOOT is found but it is not an ONB then it is supposed it is a
 *      RB bootloader (that should be updated), then the ONB is loaded from
 *      offset=32KB+old_BLSIZE).
 *   2) write ONB at 32KB+new_BLSIZE, if it fails then:
 *      2a) try to restore ONB to its 'pristine' place (offset=32KB), if it
 *          also fails then the NOR got corrupted (ONB probably destroyed)
 *          and iTunes should be used to restore the iPod.
 *   3) write new (included) RB bootloader at offset=32KB, it it fails then
 *      goto 2a)
 *
 * - dualboot-uninstaller: uninstall RB bootloader from NOR, leaving it at
 *   it's previous (pristine) state.
 *
 * See bootloader/ipod-s5l87xx.c for notes on how the RB bootloader works.
 *
 *
 *               Pristine NOR                    Rockboxed NOR
 *         1MB  ______________
 *             |              |
 *             |   flsh DIR   |
 *   1MB-0x200 |______________|
 *             |              |
 *             |    File 1    |
 *             |..............|
 *             |              |
 *             .              .
 *             .              .
 *             .              .
 *             |              |
 *             |..............|
 *             |              |                 .              .
 *             |    File N    |                 .              .
 *             |______________|                 |______________|
 *             |              |                 |              |
 *             |              |                 |              |
 *             |              |                 |    Unused    |
 *             |              |                 |              |
 *             |    Unused    |      160KB+BLSZ |______________|
 *             |              |                 |              |
 *             |              |                 |   Original   |
 *             |              |                 |   NOR boot   |
 *       160KB |______________|                 |  (decrypted) |
 *             |              |                 |              |
 *             |              |       32KB+BLSZ |______________|
 *             |   Original   |                 |              |
 *             |   NOR boot   |                 |  Decrypted   |
 *             | (encrypted)  |                 |   Rockbox    |
 *             |              |                 |  Bootloader  |
 *        32KB |______________|            32KB |______________|
 *             |              |                 |              |
 *             |              |                 .              .
 *             |              |                 .              .
 *             |______________|
 *             |              |
 *             |    SysCfg    |
 *           0 |______________|
 *
 */

#define OF_LOADADDR     IRAM1_ORIG

/* tone sequences: period (uS), duration (ms), silence (ms) */
static uint16_t alive[] = { 500,100,0, 0 };
static uint16_t happy[] = { 1000,100,0, 500,150,0, 0 };
static uint16_t fatal[] = { 3000,500,500, 3000,500,500, 3000,500,0, 0 };
#define sad2 (&fatal[3])
#define sad  (&fatal[6])

/* iPod Classic: decrypted hashes for known OFs */
static unsigned char of_sha[][SIGN_SZ] = {
    "\x66\x66\x76\xDC\x1D\x32\xB2\x46\xA6\xC9\x7D\x5A\x61\xD3\x49\x4C", /* v1.1.2 */
    "\x1E\xF0\xD9\xDE\xC2\x7E\xEC\x02\x7C\x15\x76\xBB\x5C\x4F\x2D\x95", /* v2.0.1 */
    "\x06\x85\xDF\x28\xE4\xD7\xF4\x82\xC0\x73\xB0\x53\x26\xFC\xB0\xFE", /* v2.0.4 */
    "\x60\x80\x7D\x33\xA8\xDE\xF8\x49\xBB\xBE\x01\x45\xFF\x62\x40\x19"  /* v2.0.5 */
};
#define N_OF (int)(sizeof(of_sha)/SIGN_SZ)

/* we can assume that unknown FW is a RB bootloader */
#define FW_RB   N_OF

static int identify_fw(struct Im3Info *hinfo)
{
    unsigned char hash[SIGN_SZ];
    int of;

    /* decrypt hash to identify OF */
    memcpy(hash, hinfo->u.enc12.data_sign, SIGN_SZ);
    hwkeyaes(HWKEYAES_DECRYPT, HWKEYAES_UKEY, hash, SIGN_SZ);

    for (of = 0; of < N_OF; of++)
        if (memcmp(hash, of_sha[of], SIGN_SZ) == 0)
            break;

    return of;
}

#ifdef DUALBOOT_UNINSTALL
/* Uninstall RB bootloader */
void main(void)
{
    struct Im3Info *hinfo;
    void *fw_addr;
    uint16_t *status;
    unsigned bl_nor_sz;

    usec_timer_init();
    piezo_seq(alive);
    spi_clkdiv(SPI_PORT, 4); /* SPI clock = 27/5 MHz. */

    hinfo = (struct Im3Info*)OF_LOADADDR;
    fw_addr = (void*)hinfo + IM3HDR_SZ;

    if (im3_read(NORBOOT_OFF, hinfo, NULL) != 0) {
        status = sad;
        goto bye; /* no FW found */
    }

    if (identify_fw(hinfo) != FW_RB) {
        status = happy;
        goto bye; /* RB bootloader not installed, nothing to do */
    }

    /* if found FW is a RB bootloader, OF should start just behind it */
    bl_nor_sz = im3_nor_sz(hinfo);
    if ((im3_read(NORBOOT_OFF + bl_nor_sz, hinfo, fw_addr) != 0)
                            || (identify_fw(hinfo) == FW_RB)) {
        status = sad;
        goto bye; /* OF not found */
    }

    /* decrypted OF correctly loaded, encrypt it before restoration */
    im3_crypt(HWKEYAES_ENCRYPT, hinfo, fw_addr);

    /* restore OF to it's original place */
    if (!im3_write(NORBOOT_OFF, hinfo)) {
        status = fatal;
        goto bye; /* corrupted NOR, use iTunes to restore */
    }

    /* erase freed NOR blocks */
    bootflash_init(SPI_PORT);
    bootflash_erase_blocks(SPI_PORT,
            (NORBOOT_OFF + im3_nor_sz(hinfo)) >> 12, bl_nor_sz >> 12);
    bootflash_close(SPI_PORT);

    status = happy;

bye:
    /* minimum time between the initial and the final beeps */
    while (USEC_TIMER < 2000000);
    piezo_seq(status);
    WDTCON = 0x100000; /* WDT reset */
    while (1);
}

#else
/* Install RB bootloader */
struct Im3Info bl_hinfo __attribute__((section(".im3info.data"))) =
{
    .ident = IM3_IDENT,
    .version = IM3_VERSION,
    .enc_type = 2,
};

static uint32_t get_uint32le(unsigned char *p)
{
    return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
}

void main(void)
{
    uint16_t *status = happy;
    int single_boot;
    struct Im3Info *hinfo;
    void *fw_addr;
    unsigned bl_nor_sz;

    usec_timer_init();
    piezo_seq(alive);
    spi_clkdiv(SPI_PORT, 4); /* SPI clock = 27/5 MHz. */

    /* check for single boot installation, is is configured when
       mks5lboot.exe builds the .dfu image */
    single_boot = bl_hinfo.info_sign[0];

    /* sign RB bootloader (data and header), but don't encrypt it,
       use current decrypted image for faster load */
    im3_sign(HWKEYAES_UKEY, (void*)&bl_hinfo + IM3HDR_SZ,
                get_uint32le(bl_hinfo.data_sz), bl_hinfo.u.enc12.data_sign);
    im3_sign(HWKEYAES_UKEY, &bl_hinfo, IM3INFOSIGN_SZ, bl_hinfo.info_sign);

    if (single_boot) {
        if (!im3_write(NORBOOT_OFF, &bl_hinfo))
            status = sad;
        goto bye;
    }

    hinfo = (struct Im3Info*)OF_LOADADDR;
    fw_addr = (void*)hinfo + IM3HDR_SZ;

    if (im3_read(NORBOOT_OFF, hinfo, fw_addr) != 0) {
        status = sad;
        goto bye; /* no FW found */
    }

    if (identify_fw(hinfo) == FW_RB) {
        /* FW found, but not OF, assume it is a RB bootloader,
           already decrypted OF should be located just behind */
        int nor_offset = NORBOOT_OFF + im3_nor_sz(hinfo);
        if ((im3_read(nor_offset, hinfo, fw_addr) != 0)
                                || (identify_fw(hinfo) == FW_RB)) {
            status = sad;
            goto bye; /* OF not found, use iTunes to restore */
        }
    }

    bl_nor_sz = im3_nor_sz(&bl_hinfo);
    /* safety check - verify we are not going to overwrite useful data */
    if (flsh_get_unused() < bl_nor_sz) {
        status = sad2;
        goto bye; /* no space if flash, use iTunes to restore */
    }

    /* write decrypted OF and RB bootloader, if any of these fails we
       will try to retore OF to its original place */
    if (!im3_write(NORBOOT_OFF + bl_nor_sz, hinfo)
                                || !im3_write(NORBOOT_OFF, &bl_hinfo)) {
        im3_crypt(HWKEYAES_ENCRYPT, hinfo, fw_addr);
        if (!im3_write(NORBOOT_OFF, hinfo)) {
            /* corrupted NOR, use iTunes to restore */
            status = fatal;
        }
        else {
            /* RB bootloader not succesfully intalled, but device
               was restored and should be working as before */
            status = sad;
        }
    }

bye:
    /* minimum time between the initial and the final beeps */
    while (USEC_TIMER < 2000000);
    piezo_seq(status);
    WDTCON = 0x100000; /* WDT reset */
    while (1);
}
#endif  /* DUALBOOT_UNINSTALL */