summaryrefslogtreecommitdiff
path: root/firmware/target/arm
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/arm')
-rw-r--r--firmware/target/arm/pp/mi4-loader.c237
-rw-r--r--firmware/target/arm/rk27xx/rkw-loader.c72
2 files changed, 273 insertions, 36 deletions
diff --git a/firmware/target/arm/pp/mi4-loader.c b/firmware/target/arm/pp/mi4-loader.c
new file mode 100644
index 0000000000..c465b898b4
--- /dev/null
+++ b/firmware/target/arm/pp/mi4-loader.c
@@ -0,0 +1,237 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 *
9 * Copyright (C) 2006 by Barry Wardell
10 *
11 * Based on Rockbox iriver bootloader by Linus Nielsen Feltzing
12 * and the ipodlinux bootloader by Daniel Palffy and Bernard Leach
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 ****************************************************************************/
23
24#include <stdbool.h>
25#include <stdio.h>
26#include "config.h"
27#include "mi4-loader.h"
28#include "loader_strerror.h"
29#include "crc32-mi4.h"
30#include "file.h"
31
32static inline unsigned int le2int(unsigned char* buf)
33{
34 int32_t res = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
35
36 return res;
37}
38
39static inline void int2le(unsigned int val, unsigned char* addr)
40{
41 addr[0] = val & 0xFF;
42 addr[1] = (val >> 8) & 0xff;
43 addr[2] = (val >> 16) & 0xff;
44 addr[3] = (val >> 24) & 0xff;
45}
46
47static struct tea_key tea_keytable[] = {
48 { "default" , { 0x20d36cc0, 0x10e8c07d, 0xc0e7dcaa, 0x107eb080 } },
49 { "sansa", { 0xe494e96e, 0x3ee32966, 0x6f48512b, 0xa93fbb42 } },
50 { "sansa_gh", { 0xd7b10538, 0xc662945b, 0x1b3fce68, 0xf389c0e6 } },
51 { "sansa_103", { 0x1d29ddc0, 0x2579c2cd, 0xce339e1a, 0x75465dfe } },
52 { "rhapsody", { 0x7aa9c8dc, 0xbed0a82a, 0x16204cc7, 0x5904ef38 } },
53 { "p610", { 0x950e83dc, 0xec4907f9, 0x023734b9, 0x10cfb7c7 } },
54 { "p640", { 0x220c5f23, 0xd04df68e, 0x431b5e25, 0x4dcc1fa1 } },
55 { "virgin", { 0xe83c29a1, 0x04862973, 0xa9b3f0d4, 0x38be2a9c } },
56 { "20gc_eng", { 0x0240772c, 0x6f3329b5, 0x3ec9a6c5, 0xb0c9e493 } },
57 { "20gc_fre", { 0xbede8817, 0xb23bfe4f, 0x80aa682d, 0xd13f598c } },
58 { "elio_p722", { 0x6af3b9f8, 0x777483f5, 0xae8181cc, 0xfa6d8a84 } },
59 { "c200", { 0xbf2d06fa, 0xf0e23d59, 0x29738132, 0xe2d04ca7 } },
60 { "c200_103", { 0x2a7968de, 0x15127979, 0x142e60a7, 0xe49c1893 } },
61 { "c200_106", { 0xa913d139, 0xf842f398, 0x3e03f1a6, 0x060ee012 } },
62 { "view", { 0x70e19bda, 0x0c69ea7d, 0x2b8b1ad1, 0xe9767ced } },
63 { "sa9200", { 0x33ea0236, 0x9247bdc5, 0xdfaedf9f, 0xd67c9d30 } },
64 { "hdd1630/hdd63x0", { 0x04543ced, 0xcebfdbad, 0xf7477872, 0x0d12342e } },
65 { "vibe500", { 0xe3a66156, 0x77c6b67a, 0xe821dca5, 0xca8ca37c } },
66};
67
68/*
69
70tea_decrypt() from http://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm
71
72"Following is an adaptation of the reference encryption and decryption
73routines in C, released into the public domain by David Wheeler and
74Roger Needham:"
75
76*/
77
78/* NOTE: The mi4 version of TEA uses a different initial value to sum compared
79 to the reference implementation and the main loop is 8 iterations, not
80 32.
81*/
82
83static void tea_decrypt(uint32_t* v0, uint32_t* v1, uint32_t* k) {
84 uint32_t sum=0xF1BBCDC8, i; /* set up */
85 uint32_t delta=0x9E3779B9; /* a key schedule constant */
86 uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
87 for(i=0; i<8; i++) { /* basic cycle start */
88 *v1 -= ((*v0<<4) + k2) ^ (*v0 + sum) ^ ((*v0>>5) + k3);
89 *v0 -= ((*v1<<4) + k0) ^ (*v1 + sum) ^ ((*v1>>5) + k1);
90 sum -= delta; /* end cycle */
91 }
92}
93
94/* mi4 files are encrypted in 64-bit blocks (two little-endian 32-bit
95 integers) and the key is incremented after each block
96 */
97
98static void tea_decrypt_buf(unsigned char* src,
99 unsigned char* dest,
100 size_t n, uint32_t * key)
101{
102 uint32_t v0, v1;
103 unsigned int i;
104
105 for (i = 0; i < (n / 8); i++) {
106 v0 = le2int(src);
107 v1 = le2int(src+4);
108
109 tea_decrypt(&v0, &v1, key);
110
111 int2le(v0, dest);
112 int2le(v1, dest+4);
113
114 src += 8;
115 dest += 8;
116
117 /* Now increment the key */
118 key[0]++;
119 if (key[0]==0) {
120 key[1]++;
121 if (key[1]==0) {
122 key[2]++;
123 if (key[2]==0) {
124 key[3]++;
125 }
126 }
127 }
128 }
129}
130
131static int tea_find_key(struct mi4header_t *mi4header, unsigned char* buf)
132{
133 unsigned int i;
134 uint32_t key[4];
135 uint32_t keyinc;
136 unsigned char magic_dec[8];
137 int key_found = -1;
138 unsigned int magic_location = mi4header->length-4;
139 int unaligned = 0;
140
141 if ( (magic_location % 8) != 0 )
142 {
143 unaligned = 1;
144 magic_location -= 4;
145 }
146
147 for (i=0; i < NUM_KEYS && (key_found<0) ; i++) {
148 key[0] = tea_keytable[i].key[0];
149 key[1] = tea_keytable[i].key[1];
150 key[2] = tea_keytable[i].key[2];
151 key[3] = tea_keytable[i].key[3];
152
153 /* Now increment the key */
154 keyinc = (magic_location-mi4header->plaintext)/8;
155 if ((key[0]+keyinc) < key[0]) key[1]++;
156 key[0] += keyinc;
157 if (key[1]==0) key[2]++;
158 if (key[2]==0) key[3]++;
159
160 /* Decrypt putative magic */
161 tea_decrypt_buf(&buf[magic_location], magic_dec, 8, key);
162
163 if (le2int(&magic_dec[4*unaligned]) == 0xaa55aa55)
164 {
165 key_found = i;
166 }
167 }
168
169 return key_found;
170}
171
172/* Load mi4 format firmware image */
173int load_mi4(unsigned char* buf,
174 const char* firmware,
175 unsigned int buffer_size)
176{
177 int fd;
178 struct mi4header_t mi4header;
179 int rc;
180 unsigned long sum;
181 char filename[MAX_PATH];
182
183 snprintf(filename,sizeof(filename), BOOTDIR "/%s",firmware);
184 fd = open(filename, O_RDONLY);
185 if(fd < 0)
186 {
187 snprintf(filename,sizeof(filename),"/%s",firmware);
188 fd = open(filename, O_RDONLY);
189 if(fd < 0)
190 return EFILE_NOT_FOUND;
191 }
192
193 read(fd, &mi4header, MI4_HEADER_SIZE);
194
195 /* MI4 file size */
196 if ((mi4header.mi4size-MI4_HEADER_SIZE) > buffer_size)
197 {
198 close(fd);
199 return EFILE_TOO_BIG;
200 }
201
202 /* Load firmware file */
203 lseek(fd, MI4_HEADER_SIZE, SEEK_SET);
204 rc = read(fd, buf, mi4header.mi4size-MI4_HEADER_SIZE);
205 close(fd);
206 if(rc < (int)mi4header.mi4size-MI4_HEADER_SIZE)
207 return EREAD_IMAGE_FAILED;
208
209 /* Check CRC32 to see if we have a valid file */
210 sum = chksum_crc32 (buf, mi4header.mi4size - MI4_HEADER_SIZE);
211
212 if(sum != mi4header.crc32)
213 return EBAD_CHKSUM;
214
215 if( (mi4header.plaintext + MI4_HEADER_SIZE) != mi4header.mi4size)
216 {
217 /* Load encrypted firmware */
218 int key_index = tea_find_key(&mi4header, buf);
219
220 if (key_index < 0)
221 return EINVALID_FORMAT;
222
223 /* Plaintext part is already loaded */
224 buf += mi4header.plaintext;
225
226 /* Decrypt in-place */
227 tea_decrypt_buf(buf, buf,
228 mi4header.mi4size-(mi4header.plaintext+MI4_HEADER_SIZE),
229 tea_keytable[key_index].key);
230
231 /* Check decryption was successfull */
232 if(le2int(&buf[mi4header.length-mi4header.plaintext-4]) != 0xaa55aa55)
233 return EREAD_IMAGE_FAILED;
234 }
235
236 return mi4header.mi4size - MI4_HEADER_SIZE;
237}
diff --git a/firmware/target/arm/rk27xx/rkw-loader.c b/firmware/target/arm/rk27xx/rkw-loader.c
index ed7e253f14..9b50e1b187 100644
--- a/firmware/target/arm/rk27xx/rkw-loader.c
+++ b/firmware/target/arm/rk27xx/rkw-loader.c
@@ -18,42 +18,22 @@
18 * 18 *
19 ****************************************************************************/ 19 ****************************************************************************/
20 20
21#include <stdio.h>
21#include "config.h" 22#include "config.h"
23#include "loader_strerror.h"
22#include "rkw-loader.h" 24#include "rkw-loader.h"
23#include "crc32-rkw.h" 25#include "crc32-rkw.h"
24#include "file.h" 26#include "file.h"
25#include "panic.h" 27#include "panic.h"
26 28
27/* error strings sorted by number */
28/* error 0 is empty */
29static const char *err_str[] = {
30 "Loading OK",
31 "Can't open RKW file",
32 "Can't read RKW header",
33 "Invalid RKW magic number",
34 "RKW header CRC error",
35 "RKW file too big",
36 "RKW Load address mismatch",
37 "Bad model number",
38 "Error Reading File",
39 "RKW firmware CRC error"
40};
41
42const char *rkw_strerror(int8_t errno)
43{
44 if ((uint8_t)-errno >= sizeof(err_str)/sizeof(err_str[0]) || errno > 0)
45 return "Unknown error";
46
47 return err_str[-errno];
48}
49
50/* loosely based on load_firmware() 29/* loosely based on load_firmware()
51 * on success we return size loaded image 30 * on success we return size of loaded image
52 * on error we return negative value which can be deciphered by means 31 * on error we return negative value which can be deciphered by means
53 * of rkw_strerror() function 32 * of rkw_strerror() function
54 */ 33 */
55int load_rkw(unsigned char* buf, const char* filename, int buffer_size) 34int load_rkw(unsigned char* buf, const char* firmware, int buffer_size)
56{ 35{
36 char filename[MAX_PATH];
57 int fd; 37 int fd;
58 int rc; 38 int rc;
59 int len; 39 int len;
@@ -61,22 +41,42 @@ int load_rkw(unsigned char* buf, const char* filename, int buffer_size)
61 uint32_t crc, fw_crc; 41 uint32_t crc, fw_crc;
62 struct rkw_header_t rkw_info; 42 struct rkw_header_t rkw_info;
63 43
64 fd = open(filename, O_RDONLY); 44 /* only filename passed */
45 if (firmware[0] != '/')
46 {
47 /* First check in BOOTDIR */
48 snprintf(filename, sizeof(filename), BOOTDIR "/%s",firmware);
65 49
66 if(fd < 0) 50 fd = open(filename, O_RDONLY);
67 return -1; 51 if(fd < 0)
52 {
53 /* Check in root dir */
54 snprintf(filename, sizeof(filename),"/%s",firmware);
55 fd = open(filename, O_RDONLY);
56
57 if (fd < 0)
58 return EFILE_NOT_FOUND;
59 }
60 }
61 else
62 {
63 /* full path passed */
64 fd = open(firmware, O_RDONLY);
65 if (fd < 0)
66 return EFILE_NOT_FOUND;
67 }
68 68
69 rc = read(fd, &rkw_info, sizeof(rkw_info)); 69 rc = read(fd, &rkw_info, sizeof(rkw_info));
70 if (rc < (int)sizeof(rkw_info)) 70 if (rc < (int)sizeof(rkw_info))
71 { 71 {
72 ret = -2; 72 ret = EREAD_HEADER_FAILED;
73 goto end; 73 goto end;
74 } 74 }
75 75
76 /* check if RKW is valid */ 76 /* check if RKW is valid */
77 if (rkw_info.magic_number != RKLD_MAGIC) 77 if (rkw_info.magic_number != RKLD_MAGIC)
78 { 78 {
79 ret = -3; 79 ret = EINVALID_FORMAT;
80 goto end; 80 goto end;
81 } 81 }
82 82
@@ -86,7 +86,7 @@ int load_rkw(unsigned char* buf, const char* filename, int buffer_size)
86 crc = crc32_rkw((uint8_t *)&rkw_info, sizeof(rkw_info)-sizeof(uint32_t)); 86 crc = crc32_rkw((uint8_t *)&rkw_info, sizeof(rkw_info)-sizeof(uint32_t));
87 if (rkw_info.crc != crc) 87 if (rkw_info.crc != crc)
88 { 88 {
89 ret = -4; 89 ret = EBAD_HEADER_CHKSUM;
90 goto end; 90 goto end;
91 } 91 }
92 } 92 }
@@ -95,14 +95,14 @@ int load_rkw(unsigned char* buf, const char* filename, int buffer_size)
95 len = rkw_info.load_limit - rkw_info.load_address; 95 len = rkw_info.load_limit - rkw_info.load_address;
96 if (len > buffer_size) 96 if (len > buffer_size)
97 { 97 {
98 ret = -5; 98 ret = EFILE_TOO_BIG;
99 goto end; 99 goto end;
100 } 100 }
101 101
102 /* check load address - we support loading only at 0x60000000 */ 102 /* check load address - we support loading only at 0x60000000 */
103 if (rkw_info.load_address != 0x60000000) 103 if (rkw_info.load_address != 0x60000000)
104 { 104 {
105 ret = -6; 105 ret = EINVALID_LOAD_ADDR;
106 goto end; 106 goto end;
107 } 107 }
108 108
@@ -112,7 +112,7 @@ int load_rkw(unsigned char* buf, const char* filename, int buffer_size)
112 */ 112 */
113 if (rkw_info.reserved0 != 0 && rkw_info.reserved0 != MODEL_NUMBER) 113 if (rkw_info.reserved0 != 0 && rkw_info.reserved0 != MODEL_NUMBER)
114 { 114 {
115 ret = -7; 115 ret = EBAD_MODEL;
116 goto end; 116 goto end;
117 } 117 }
118 118
@@ -124,7 +124,7 @@ int load_rkw(unsigned char* buf, const char* filename, int buffer_size)
124 124
125 if(rc < len) 125 if(rc < len)
126 { 126 {
127 ret = -8; 127 ret = EREAD_IMAGE_FAILED;
128 goto end; 128 goto end;
129 } 129 }
130 130
@@ -136,7 +136,7 @@ int load_rkw(unsigned char* buf, const char* filename, int buffer_size)
136 136
137 if (fw_crc != crc) 137 if (fw_crc != crc)
138 { 138 {
139 ret = -9; 139 ret = EBAD_CHKSUM;
140 goto end; 140 goto end;
141 } 141 }
142 } 142 }