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