1 /*
2 * crunchfc.c
3 *
4 * $Id: crunchfc.c,v 1.3 2008/03/29 12:20:55 andrzej Exp $
5 *
6 * Redboot Flash Configuration parser.
7 * Configuration parsing routines.
8 *
9 * Copyright (C) 2006 Ekiert sp z o.o.
10 * Author: Andrzej Ekiert <a.ekiert@ekiert.com>
11 *
12 * Changes:
13 * 2007/10/21 - 'list' option added by Hamish Moffatt
14 * 2008/03/29 - 'offset' option added by wimpunk
15 *
16 * This program is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU General Public License
18 * as published by the Free Software Foundation; either version
19 * 2 of the License, or (at your option) any later version.
20 */
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdint.h>
24 #include <stddef.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <unistd.h>
28
29 #include "crunchfc.h"
30 #include "ftypes.h"
31 #include "crc.h"
32 #include "debug.h"
33
34 /*
35 * RedBoot configuration layout is the following:
36 * 0 to 3 : len
37 * 4 to 7 : CONFIG_KEY1
38 * 8 to len-9 : data
39 * len-8 to len-5 : CONFIG_KEY2
40 * len-4 to len-1 : checksum
41 *
42 * Tested with RedBoot v. 2.02
43 */
44 #define CONFIG_KEY1 0x0BADFACE
45 #define CONFIG_KEY2 0xDEADDEAD
46
47 /*
48 * Each data item is variable length, with the name, type and dependencies
49 * encoded into the object.
50 * offset contents
51 * 0 data type
52 * 1 length of name (N)
53 * 2 enable sense
54 * 3 length of enable key (M)
55 * 4 key name
56 * N+4 enable key
57 * M+N+4 data value
58 */
59
60 /*
61 * Fix 32-bit number endianness.
62 */
fix_endian32(void * vptr,uint8_t swab)63 static inline void fix_endian32(void *vptr, uint8_t swab)
64 {
65 uint8_t *ptr = (uint8_t*)vptr;
66 uint8_t tmp;
67 if (!swab) {
68 return;
69 }
70 tmp = *ptr;
71 *ptr = *(ptr+3);
72 *(ptr+3) = tmp;
73 tmp = *(ptr+1);
74 *(ptr+1) = *(ptr+2);
75 *(ptr+2) = tmp;
76 }
77
78 /*
79 * Configuration key description.
80 */
81 struct fconfig_key {
82 uint8_t type;
83 uint8_t namelen, ensense, enlen;
84 uint8_t *keyname;
85 uint8_t *enkey;
86 uint8_t *dataval;
87 };
88
89 /*
90 * Fill-in the key description structure.
91 *
92 * 'ptr' should point to the start of key data. There MUST BE at least 4 more
93 * bytes in the buffer (and there should be more, if start address is valid).
94 */
get_key(uint8_t * ptr,struct fconfig_key * key)95 static uint8_t *get_key(uint8_t *ptr, struct fconfig_key *key)
96 {
97 key->type = *ptr++;
98 key->namelen = *ptr++;
99 key->ensense = *ptr++;
100 key->enlen = *ptr++;
101 key->keyname = ptr;
102 ptr += key->namelen;
103 key->enkey = ptr;
104 ptr += key->enlen;
105
106 /* Be warned: 'dataval' may be an odd pointer and may contain
107 * an uint32_t. If the pointer is odd, then uint32_t will be unaligned.
108 * Never try to cast it: *(uint32_t*)key->dataval. On many
109 * architectures this will not work.
110 */
111 key->dataval = ptr;
112
113 if (!verify_ftype(key->type)) {
114 MESSAGE(VERB_LOW, "Unsupported type: %d\n", key->type);
115 return NULL;
116 }
117
118 ptr += TYPE_SIZE(key->type);
119
120 return ptr;
121 }
122
123 /*
124 * Print key data to the screen.
125 * It is assumed that key has been filled-in by get_key().
126 */
print_key(struct fconfig_key * key,uint8_t verb,uint8_t swab)127 static void print_key(struct fconfig_key *key, uint8_t verb, uint8_t swab)
128 {
129 uint8_t buf[MAX_TYPE_SIZE];
130 printer_t printer;
131
132 MESSAGE(verb, "\n");
133 MESSAGE(verb, "Name length: %d\n", key->namelen);
134 MESSAGE(verb, "Enable sense: %d\n", key->ensense);
135 if (key->ensense==0) {
136 MESSAGE(verb, "Enable key length: %d\n", key->enlen);
137 MESSAGE(verb, "Enable key: %s\n", key->enkey);
138 }
139 MESSAGE(verb, "Key name: %s\n", key->keyname);
140 MESSAGE(verb, "Value: ");
141 if (verb <= verbosity) {
142 memcpy(buf, key->dataval, TYPE_SIZE(key->type));
143 switch (key->type) {
144 case CONFIG_BOOL :
145 case CONFIG_INT :
146 fix_endian32(buf, swab);
147 break;
148 default :
149 break;
150 }
151 printer = TYPE_PRINTER(key->type);
152 if (printer) {
153 printer(buf);
154 }
155 }
156 MESSAGE(verb, "\n");
157 }
158
159 /*
160 * Find the address, where a key with given 'nickname' starts.
161 * 'data' should have been previously validated with verify_fconfig().
162 */
locate_key(struct config_data * data,uint8_t * nickname)163 static uint8_t *locate_key(struct config_data *data, uint8_t *nickname)
164 {
165 struct fconfig_key key;
166 uint32_t len = data->reallen;
167 uint8_t *keyptr = NULL;
168 uint8_t *ptr = data->buf+8;
169 uint8_t *ptrend = data->buf+len-9;
170
171 while (ptr < ptrend-4) {
172 keyptr = ptr;
173 ptr = get_key(ptr, &key);
174 if (ptr == NULL) {
175 MESSAGE(VERB_LOW, "Error in structure\n");
176 return NULL;
177 }
178 if (ptr > ptrend) {
179 MESSAGE(VERB_LOW, "Parser went out of struct!\n");
180 return NULL;
181 }
182
183 if ((key.type == 0) && (key.namelen==0)) {
184 MESSAGE(VERB_NORMAL, "EOF reached - key not found\n");
185 return NULL;
186 }
187
188 if (strncmp(nickname, key.keyname, key.namelen) == 0) {
189 break;
190 }
191 }
192 return keyptr;
193 }
194
195 /*
196 * Verify the correctness of the configuration structure.
197 */
buf_check(struct config_data * data)198 static int8_t buf_check(struct config_data *data)
199 {
200 struct fconfig_key key;
201 uint32_t len = data->reallen;
202 uint8_t *ptr = data->buf+8;
203 uint8_t *ptrend = data->buf+len-9;
204
205 while (ptr < ptrend-4) {
206 ptr = get_key(ptr, &key);
207 if (ptr == NULL) {
208 MESSAGE(VERB_LOW, "Error in structure\n");
209 return -1;
210 }
211 if (ptr > ptrend) {
212 MESSAGE(VERB_LOW, "Parser went out of struct!\n");
213 return -1;
214 }
215
216 if ((key.type == 0) && (key.namelen==0)) {
217 MESSAGE(VERB_HIGH, "EOF reached - structure OK\n");
218 return 0;
219 }
220 print_key(&key, VERB_HIGH, data->swab);
221 }
222 return 0;
223 }
224
225 /*
226 * Check whether given buffer contains something that looks mostly like
227 * a valid configuration. Try to automatically tell what the endianness is.
228 *
229 * You must call this function before doing anything else to the 'data' buffer.
230 */
verify_fconfig(struct config_data * data)231 int8_t verify_fconfig(struct config_data *data)
232 {
233 uint32_t len;
234 uint32_t key;
235 uint32_t crc;
236 uint32_t maxlen;
237 uint8_t *buf;
238 uint8_t swab;
239
240 buf = data->buf;
241 maxlen = data->maxlen;
242
243 for (swab = 0; swab < 2; swab++) {
244 memcpy(&key, buf+4, sizeof(key));
245 fix_endian32(&key, swab);
246 if (key == CONFIG_KEY1) {
247 break;
248 }
249 }
250 if (swab == 0) {
251 MESSAGE(VERB_HIGH, "Using native endianness\n");
252 } else if (swab == 1) {
253 MESSAGE(VERB_HIGH, "Using non-native endianness\n");
254 } else {
255 MESSAGE(VERB_NORMAL, "Key1 is not valid, terminating\n");
256 return -1;
257 }
258
259 memcpy(&len, buf, sizeof(len));
260 fix_endian32(&len, swab);
261
262 MESSAGE(VERB_NORMAL, "Data length is %d, maxlen is %d\n",
263 len, maxlen);
264 if (len > maxlen) {
265 MESSAGE(VERB_NORMAL, "This is too long.\n");
266 return -1;
267 }
268
269 memcpy(&key, buf+len-8, sizeof(key));
270 fix_endian32(&key, swab);
271 if (key != CONFIG_KEY2) {
272 MESSAGE(VERB_NORMAL, "Key2 is not valid, terminating\n");
273 return -1;
274 }
275
276 /* verify crc... */
277 memcpy(&crc, buf+len-4, sizeof(crc));
278 fix_endian32(&crc, swab);
279 if (crc != crc32(buf, len-4)) {
280 MESSAGE(VERB_NORMAL, "CRC verification failed.\n");
281 return -1;
282 }
283 MESSAGE(VERB_NORMAL, "CRC is valid.\n");
284
285 data->swab = swab;
286 data->reallen = len;
287
288 if (buf_check(data)) {
289 MESSAGE(VERB_NORMAL, "Configuration structure is broken.\n");
290 return -1;
291 }
292
293 return 0;
294 }
295
296 /*
297 * Find a key with given nickname, check its type and print value
298 * Assumes that verify_fconfig() has been called on 'data' before.
299 */
get_key_value(struct config_data * data,uint8_t * nickname)300 int8_t get_key_value(struct config_data *data, uint8_t *nickname)
301 {
302 printer_t printer;
303 struct fconfig_key key;
304 uint8_t *ptr;
305
306 ptr = locate_key(data, nickname);
307 if (ptr == NULL) {
308 MESSAGE(VERB_LOW, "Unknown key.\n");
309 return -1;
310 }
311 if (get_key(ptr, &key) == NULL) {
312 MESSAGE(VERB_LOW, "Erroneous key.\n");
313 return -1;
314 }
315 print_key(&key, VERB_HIGH, data->swab);
316
317 printer = TYPE_PRINTER(key.type);
318 if (printer == NULL) {
319 MESSAGE(VERB_LOW, "Printer missing for type %d\n", key.type);
320 return -1;
321 }
322 printer(key.dataval);
323 return 0;
324 }
325
326 /*
327 * List known keys.
328 */
list_keys(struct config_data * data)329 int8_t list_keys(struct config_data *data)
330 {
331 printer_t printer;
332 struct fconfig_key key;
333 uint32_t len = data->reallen;
334 uint8_t *keyptr = NULL;
335 uint8_t *ptr = data->buf+8;
336 uint8_t *ptrend = data->buf+len-9;
337
338 while (ptr < ptrend-4) {
339 keyptr = ptr;
340 ptr = get_key(ptr, &key);
341 if (ptr == NULL) {
342 MESSAGE(VERB_LOW, "Error in structure\n");
343 return -1;
344 }
345 if (ptr > ptrend) {
346 MESSAGE(VERB_LOW, "Parser went out of struct!\n");
347 return -1;
348 }
349
350 if ((key.type == 0) && (key.namelen==0)) {
351 MESSAGE(VERB_NORMAL, "EOF reached\n");
352 return -1;
353 }
354
355 print_key(&key, VERB_HIGH, data->swab);
356
357 printf("%s: ", key.keyname);
358 printer = TYPE_PRINTER(key.type);
359 if (printer == NULL) {
360 MESSAGE(VERB_LOW,
361 "Printer missing for type %d\n", key.type);
362 return -1;
363 }
364 printer(key.dataval);
365 printf("\n");
366 }
367 }
368
369 /*
370 * Find a key with given nickname, check its type and set value
371 * Assumes that verify_fconfig() has been called on 'data' before.
372 */
set_key_value(struct config_data * data,uint8_t * nickname,void * value)373 int8_t set_key_value(struct config_data *data, uint8_t *nickname, void *value)
374 {
375 uint32_t offset;
376 uint8_t buf[MAX_TYPE_SIZE];
377 parser_t parser;
378 struct fconfig_key key;
379 uint8_t *ptr;
380
381 ptr = locate_key(data, nickname);
382 if (ptr == NULL) {
383 MESSAGE(VERB_LOW, "Unknown key.\n");
384 return -1;
385 }
386 if (get_key(ptr, &key) == NULL) {
387 MESSAGE(VERB_LOW, "Erroneous key.\n");
388 return -1;
389 }
390
391 MESSAGE(VERB_NORMAL, "\nBefore change:");
392 print_key(&key, VERB_NORMAL, data->swab);
393
394 parser = TYPE_PARSER(key.type);
395 if (parser == NULL) {
396 MESSAGE(VERB_LOW, "Parser missing for type %d\n", key.type);
397 return -1;
398 }
399
400 memset(buf, 0, MAX_TYPE_SIZE);
401 if (parser(value, buf)) {
402 MESSAGE(VERB_LOW, "Bad value.\n");
403 return -1;
404 }
405
406 offset = (uint32_t)(key.dataval - data->buf);
407 MESSAGE(VERB_HIGH, "Writing %d bytes at offset %d\n",
408 TYPE_SIZE(key.type), offset);
409
410 #if !defined(__FreeBSD__)
411 /* do an actual write to the device or file */
412 if (lseek(data->fd, data->offset+offset, SEEK_SET) == -1) {
413 MESSAGE(VERB_LOW, "lseek() failed\n");
414 return -1;
415 }
416 if (write(data->fd, buf, TYPE_SIZE(key.type)) == -1) {
417 MESSAGE(VERB_LOW, "write() failed\n");
418 return -1;
419 }
420 #endif
421 /* keep our buffer in sync with the device or file */
422 memcpy(key.dataval, buf, TYPE_SIZE(key.type));
423
424 MESSAGE(VERB_NORMAL, "\nAfter change:");
425 print_key(&key, VERB_NORMAL, data->swab);
426
427 return 0;
428 }
429
430 /*
431 * Recalculate the checksum of a configuration buffer.
432 * Assumes that verify_fconfig() has been called on 'data' before.
433 */
recalculate_crc(struct config_data * data)434 void recalculate_crc(struct config_data *data)
435 {
436 uint32_t len;
437 uint32_t crc;
438 uint8_t *buf;
439 uint8_t swab;
440
441 len = data->reallen;
442 buf = data->buf;
443 swab = data->swab;
444
445 /* Show old */
446 memcpy(&crc, buf+len-4, sizeof(crc));
447 fix_endian32(&crc, swab);
448 MESSAGE(VERB_NORMAL, "Old CRC: %04x\n", crc);
449
450 /* Set new */
451 crc = crc32(buf, len-4);
452 fix_endian32(&crc, swab);
453
454 MESSAGE(VERB_HIGH, "Writing CRC at offset %d\n", len-4);
455
456 #if !defined(__FreeBSD__)
457 /* do an actual write to the device or file */
458 if (lseek(data->fd, data->offset+len-4, SEEK_SET) == -1) {
459 MESSAGE(VERB_LOW, "CRC: lseek() failed\n");
460 return;
461 }
462 if (write(data->fd, &crc, sizeof(crc)) == -1) {
463 MESSAGE(VERB_LOW, "CRC: write() failed\n");
464 return;
465 }
466 #endif
467 /* keep our buffer in sync with the device or file */
468 memcpy(buf+len-4, &crc, sizeof(crc));
469
470 /* Show new */
471 memcpy(&crc, buf+len-4, sizeof(crc));
472 fix_endian32(&crc, swab);
473 MESSAGE(VERB_NORMAL, "New CRC: %04x\n", crc);
474 }
475
476