1 /*
2 * Copyright (c) 2013, Google, Inc. All rights reserved
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8 #include <lk/debug.h>
9 #include <lk/trace.h>
10 #include <assert.h>
11 #include <lk/err.h>
12 #include <string.h>
13 #include <malloc.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <lk/list.h>
17 #include <lib/bio.h>
18 #include <lib/cksum.h>
19 #include <lib/sysparam.h>
20 #include <lk/init.h>
21 #include <lk/console_cmd.h>
22
23 /* implementation of system parameter block, stored on a block device */
24 /* sysparams are simple name/value pairs, with the data unstructured */
25 #define LOCAL_TRACE 0
26
27 #define SYSPARAM_MAGIC 'SYSP'
28
29 #define SYSPARAM_FLAG_LOCK 0x1
30
31 struct sysparam_phys {
32 uint32_t magic;
33 uint32_t crc32; // crc of entire structure below crc including padding
34 uint32_t flags;
35 uint16_t namelen;
36 uint16_t datalen;
37
38 //uint8_t name[namelen];
39 // 0 padding to next multiple of 4
40 //uint8_t data[data];
41 // 0 padding to next multiple of 4
42
43 uint8_t namedata[0];
44 };
45
46 /* a copy we keep in memory */
47 struct sysparam {
48 struct list_node node;
49
50 uint32_t flags;
51
52 char *name;
53
54 size_t datalen;
55 void *data;
56
57 /* in memory size to hold this structure, the name string, and the data */
58 size_t memlen;
59 };
60
61 /* global state */
62 static struct {
63 struct list_node list;
64
65 bool dirty;
66
67 bdev_t *bdev;
68 off_t offset;
69 size_t len;
70 } params;
71
sysparam_init(uint level)72 static void sysparam_init(uint level) {
73 list_initialize(¶ms.list);
74 }
75
76 LK_INIT_HOOK(sysparam, &sysparam_init, LK_INIT_LEVEL_THREADING);
77
sysparam_is_locked(const struct sysparam * param)78 static inline bool sysparam_is_locked(const struct sysparam *param) {
79 return param->flags & SYSPARAM_FLAG_LOCK;
80 }
81
sysparam_len(const struct sysparam_phys * sp)82 static inline size_t sysparam_len(const struct sysparam_phys *sp) {
83 size_t len = sizeof(struct sysparam_phys);
84
85 len += ROUNDUP(sp->namelen, 4);
86 len += ROUNDUP(sp->datalen, 4);
87
88 return len;
89 }
90
sysparam_crc32(const struct sysparam_phys * sp)91 static inline uint32_t sysparam_crc32(const struct sysparam_phys *sp) {
92 size_t len = sysparam_len(sp);
93
94 LTRACEF("len %d\n", len);
95 uint32_t sum = crc32(0, (const void *)&sp->flags, len - 8);
96 LTRACEF("sum is 0x%x\n", sum);
97
98 return sum;
99 }
100
sysparam_create(const char * name,size_t namelen,const void * data,size_t datalen,uint32_t flags)101 static struct sysparam *sysparam_create(const char *name, size_t namelen, const void *data, size_t datalen, uint32_t flags) {
102 struct sysparam *param = malloc(sizeof(struct sysparam));
103 if (!param)
104 return NULL;
105
106 param->flags = flags;
107 param->memlen = sizeof(struct sysparam);
108
109 param->name = malloc(namelen + 1);
110 if (!param->name) {
111 free(param);
112 return NULL;
113 }
114 param->memlen += namelen + 1;
115
116 memcpy(param->name, name, namelen);
117 param->name[namelen] = '\0';
118
119 param->datalen = datalen;
120 size_t alloclen = ROUNDUP(datalen, 4); /* allocate a multiple of 4 for padding purposes */
121 param->data = malloc(alloclen);
122 if (!param->data) {
123 free(param->name);
124 free(param);
125 return NULL;
126 }
127 param->memlen += alloclen;
128
129 memcpy(param->data, data, datalen);
130 memset((char *)param->data + datalen, 0, alloclen - datalen); /* zero out the trailing space */
131
132 return param;
133 }
134
sysparam_read_phys(const struct sysparam_phys * sp)135 static struct sysparam *sysparam_read_phys(const struct sysparam_phys *sp) {
136 return sysparam_create((const char *)sp->namedata, sp->namelen, sp->namedata + ROUNDUP(sp->namelen, 4), sp->datalen, sp->flags);
137 }
138
sysparam_find(const char * name)139 static struct sysparam *sysparam_find(const char *name) {
140 struct sysparam *param;
141 list_for_every_entry(¶ms.list, param, struct sysparam, node) {
142 if (strcmp(name, param->name) == 0)
143 return param;
144 }
145
146 return NULL;
147 }
148
sysparam_scan(bdev_t * bdev,off_t offset,size_t len)149 status_t sysparam_scan(bdev_t *bdev, off_t offset, size_t len) {
150 status_t err = NO_ERROR;
151
152 LTRACEF("bdev %p (%s), offset 0x%llx, len 0x%zx\n", bdev, bdev->name, offset, len);
153
154 DEBUG_ASSERT(bdev);
155 DEBUG_ASSERT(len > 0);
156 DEBUG_ASSERT(offset + len <= bdev->total_size);
157 DEBUG_ASSERT((offset % bdev->block_size) == 0);
158
159 params.bdev = bdev;
160 params.offset = offset;
161 params.len = len;
162 params.dirty = false;
163
164 /* allocate a len sized block */
165 uint8_t *buf = malloc(len);
166 if (!buf)
167 return ERR_NO_MEMORY;
168
169 /* read in the sector at the scan offset */
170 err = bio_read(bdev, buf, offset, len);
171 if (err < (ssize_t)len) {
172 err = ERR_IO;
173 goto err;
174 }
175
176 LTRACEF("looking for sysparams in block:\n");
177 if (LOCAL_TRACE)
178 hexdump(buf, len);
179
180 size_t pos = 0;
181 while (pos < len) {
182 struct sysparam_phys *sp = (struct sysparam_phys *)(buf + pos);
183
184 /* examine the sysparam entry, making sure it's valid */
185 if (sp->magic != SYSPARAM_MAGIC) {
186 pos += 4; /* try searching in the next spot */
187 //LTRACEF("failed magic check\n");
188 continue;
189 }
190
191 /* looks valid, see if length is sane */
192 size_t splen = sysparam_len(sp);
193 if (pos + splen > offset + len) {
194 /* length exceeds the size of the area */
195 LTRACEF("param at 0x%x: bad length\n", pos);
196 break;
197 }
198
199 pos += splen;
200
201 /* calculate a checksum of it */
202 uint32_t sum = sysparam_crc32(sp);
203
204 if (sp->crc32 != sum) {
205 /* failed checksum */
206 LTRACEF("param at 0x%x: failed checksum\n", pos - splen);
207 continue;
208 }
209
210 LTRACEF("got param at offset 0x%zx\n", pos - splen);
211
212 struct sysparam *param = sysparam_read_phys(sp);
213 if (!param) {
214 LTRACEF("param at 0x%x: failed to make memory copy\n", pos - splen);
215 err = ERR_NO_MEMORY;
216 break;
217 }
218
219 list_add_tail(¶ms.list, ¶m->node);
220 }
221
222
223 err:
224 free(buf);
225
226 LTRACE_EXIT;
227 return err;
228 }
229
sysparam_reload(void)230 status_t sysparam_reload(void) {
231 if (params.bdev == NULL)
232 return ERR_INVALID_ARGS;
233 if (params.len == 0)
234 return ERR_INVALID_ARGS;
235
236 /* wipe out the existing memory entries */
237 struct sysparam *param;
238 struct sysparam *temp;
239 list_for_every_entry_safe(¶ms.list, param, temp, struct sysparam, node) {
240 list_delete(¶m->node);
241
242 free(param->name);
243 free(param->data);
244 free(param);
245 }
246
247 /* reset the list back to scratch */
248 params.dirty = false;
249
250 status_t err = sysparam_scan(params.bdev, params.offset, params.len);
251
252 return err;
253 }
254
sysparam_read(const char * name,void * data,size_t len)255 ssize_t sysparam_read(const char *name, void *data, size_t len) {
256 struct sysparam *param;
257
258 param = sysparam_find(name);
259 if (!param)
260 return ERR_NOT_FOUND;
261
262 size_t toread = MIN(len, param->datalen);
263 memcpy(data, param->data, toread);
264
265 return toread;
266 }
267
sysparam_length(const char * name)268 ssize_t sysparam_length(const char *name) {
269 struct sysparam *param;
270
271 param = sysparam_find(name);
272 if (!param)
273 return ERR_NOT_FOUND;
274
275 return param->datalen;
276 }
277
sysparam_get_ptr(const char * name,const void ** ptr,size_t * len)278 status_t sysparam_get_ptr(const char *name, const void **ptr, size_t *len) {
279 struct sysparam *param;
280
281 param = sysparam_find(name);
282 if (!param)
283 return ERR_NOT_FOUND;
284
285 if (ptr)
286 *ptr = param->data;
287 if (len)
288 *len = param->datalen;
289
290 return NO_ERROR;
291 }
292
293 #if SYSPARAM_ALLOW_WRITE
294
295 /* write all of the parameters in memory to the space reserved in flash */
sysparam_write(void)296 status_t sysparam_write(void) {
297 if (params.bdev == NULL)
298 return ERR_INVALID_ARGS;
299 if (params.len == 0)
300 return ERR_INVALID_ARGS;
301
302 if (!params.dirty)
303 return NO_ERROR;
304
305 /* preflight the length, make sure we have enough space */
306 struct sysparam *param;
307 off_t total_len = 0;
308 list_for_every_entry(¶ms.list, param, struct sysparam, node) {
309 total_len += sizeof(struct sysparam_phys);
310 total_len += ROUNDUP(strlen(param->name), 4);
311 total_len += ROUNDUP(param->datalen, 4);
312 }
313
314 if (total_len > params.len)
315 return ERR_NO_MEMORY;
316
317 /* allocate a buffer to stage it */
318 uint8_t *buf = calloc(1, params.len);
319 if (!buf) {
320 TRACEF("error allocating buffer to stage write\n");
321 return ERR_NO_MEMORY;
322 }
323
324 /* erase the block device area this covers */
325 ssize_t err = bio_erase(params.bdev, params.offset, params.len);
326 if (err < (ssize_t)params.len) {
327 TRACEF("error erasing sysparam area\n");
328 free(buf);
329 return ERR_IO;
330 }
331
332 /* serialize all of the parameters */
333 off_t pos = 0;
334 list_for_every_entry(¶ms.list, param, struct sysparam, node) {
335 struct sysparam_phys phys;
336
337 /* start filling out a struct */
338 phys.magic = SYSPARAM_MAGIC;
339 phys.crc32 = 0;
340 phys.flags = param->flags;
341 phys.namelen = strlen(param->name);
342 phys.datalen = param->datalen;
343
344 /* calculate the crc of the entire thing + padding */
345 uint32_t zero = 0;
346 uint32_t sum = crc32(0, (const void *)&phys.flags, 8);
347 sum = crc32(sum, (const void *)param->name, strlen(param->name));
348 if (strlen(param->name) % 4)
349 sum = crc32(sum, (const void *)&zero, 4 - (strlen(param->name) % 4));
350 sum = crc32(sum, (const void *)param->data, ROUNDUP(param->datalen, 4));
351 phys.crc32 = sum;
352
353 /* structure portion */
354 memcpy(buf + pos, &phys, sizeof(struct sysparam_phys));
355 pos += sizeof(struct sysparam_phys);
356
357 /* name portion */
358 memcpy(buf + pos, param->name, strlen(param->name));
359 pos += ROUNDUP(strlen(param->name), 2);
360 if (pos % 4) {
361 /* write 2 zeros to realign */
362 pos += 2;
363 }
364
365 /* data portion */
366 memcpy(buf + pos, param->data, param->datalen);
367 pos += ROUNDUP(param->datalen, 4);
368 }
369
370 /* write the block out */
371 bio_write(params.bdev, buf, params.offset, params.len);
372
373 free(buf);
374
375 params.dirty = false;
376
377 return NO_ERROR;
378 }
379
sysparam_add(const char * name,const void * value,size_t len)380 status_t sysparam_add(const char *name, const void *value, size_t len) {
381 struct sysparam *param;
382
383 param = sysparam_find(name);
384 if (param)
385 return ERR_ALREADY_EXISTS;
386
387 param = sysparam_create(name, strlen(name), value, len, 0);
388 if (!param)
389 return ERR_NO_MEMORY;
390
391 list_add_tail(¶ms.list, ¶m->node);
392
393 params.dirty = true;
394
395 return NO_ERROR;
396 }
397
sysparam_remove(const char * name)398 status_t sysparam_remove(const char *name) {
399 struct sysparam *param;
400
401 param = sysparam_find(name);
402 if (!param)
403 return ERR_NOT_FOUND;
404
405 if (sysparam_is_locked(param))
406 return ERR_NOT_ALLOWED;
407
408 list_delete(¶m->node);
409
410 free(param->name);
411 free(param->data);
412 free(param);
413
414 params.dirty = true;
415
416 return NO_ERROR;
417 }
418
sysparam_lock(const char * name)419 status_t sysparam_lock(const char *name) {
420 struct sysparam *param;
421
422 param = sysparam_find(name);
423 if (!param)
424 return ERR_NOT_FOUND;
425
426 /* set the lock bit if it isn't already */
427 if (!sysparam_is_locked(param)) {
428 param->flags |= SYSPARAM_FLAG_LOCK;
429 params.dirty = true;
430 }
431
432 return NO_ERROR;
433 }
434
435 #endif // SYSPARAM_ALLOW_WRITE
436
437 #define MAX_DUMP_LEN 16
438
sysparam_dump(bool show_all)439 void sysparam_dump(bool show_all) {
440 printf("system parameters:\n");
441
442 size_t total_memlen = 0;
443
444 struct sysparam *param;
445 list_for_every_entry(¶ms.list, param, struct sysparam, node) {
446 printf("________%c %-16s : ",
447 (param->flags & SYSPARAM_FLAG_LOCK) ? 'L' : '_',
448 param->name);
449
450 const uint8_t *dat = (const uint8_t *)param->data;
451 uint32_t pr_len = param->datalen;
452
453 if (!show_all) {
454 if (pr_len > MAX_DUMP_LEN)
455 pr_len = MAX_DUMP_LEN;
456 }
457
458 for (uint i = 0; i < pr_len; i++) {
459 printf("%02x", dat[i]);
460 }
461 if (pr_len != param->datalen)
462 printf("...\n");
463 else
464 printf("\n");
465
466 total_memlen += param->memlen;
467 }
468
469 printf("total in-memory usage: %zu bytes\n", total_memlen);
470 }
471
472 #include <ctype.h>
473
hexstr_to_val(const char * str,uint8_t ** buf)474 static ssize_t hexstr_to_val(const char *str, uint8_t **buf) {
475 /* parse the value parameter as a hex code */
476 uint8_t *hexbuffer = calloc(1, strlen(str) / 2 + 1);
477 uint pos;
478 for (pos = 0; str[pos] != 0; pos++) {
479 uint8_t c = str[pos];
480
481 if (!isxdigit(c)) {
482 free(hexbuffer);
483 return ERR_NOT_VALID;
484 }
485
486 if (c >= '0' && c <= '9')
487 c -= '0';
488 else if (c >= 'a' && c <= 'f')
489 c -= 'a' - 0xa;
490 else if (c >= 'A' && c <= 'F')
491 c -= 'A' - 0xa;
492
493 hexbuffer[pos / 2] |= (!(pos % 2)) ? (c << 4) : c;
494 }
495 pos = (pos + 1) / 2; /* round down, keeping partial bytes */
496
497 *buf = hexbuffer;
498 return pos;
499 }
500
cmd_sysparam(int argc,const console_cmd_args * argv)501 static int cmd_sysparam(int argc, const console_cmd_args *argv) {
502 status_t err;
503
504 if (argc < 2) {
505 notenoughargs:
506 printf("ERROR not enough arguments\n");
507 usage:
508 printf("usage: %s dump\n", argv[0].str);
509 printf("usage: %s list\n", argv[0].str);
510 printf("usage: %s reload\n", argv[0].str);
511 #if SYSPARAM_ALLOW_WRITE
512 printf("usage: %s add <param> <string value>\n", argv[0].str);
513 printf("usage: %s addhex <param> <hex value>\n", argv[0].str);
514 printf("usage: %s addlong <param>\n", argv[0].str);
515 printf("usage: %s remove <param>\n", argv[0].str);
516 printf("usage: %s lock <param>\n", argv[0].str);
517 printf("usage: %s write\n", argv[0].str);
518 #endif
519 printf("usage: %s length <param>\n", argv[0].str);
520 printf("usage: %s read <param>\n", argv[0].str);
521 return -1;
522 }
523
524 err = NO_ERROR;
525 if (!strcmp(argv[1].str, "dump")) {
526 sysparam_dump(true);
527 } else if (!strcmp(argv[1].str, "list")) {
528 struct sysparam *param;
529 list_for_every_entry(¶ms.list, param, struct sysparam, node) {
530 printf("%s\n", param->name);
531 }
532 } else if (!strcmp(argv[1].str, "reload")) {
533 err = sysparam_reload();
534 #if SYSPARAM_ALLOW_WRITE
535 } else if (!strcmp(argv[1].str, "add")) {
536 if (argc < 4) goto notenoughargs;
537
538 err = sysparam_add(argv[2].str, argv[3].str, strlen(argv[3].str));
539 } else if (!strcmp(argv[1].str, "addhex")) {
540 if (argc < 4) goto notenoughargs;
541
542 /* parse the value parameter as a hex code */
543 uint8_t *hexbuffer;
544
545 ssize_t len = hexstr_to_val(argv[3].str, &hexbuffer);
546 if (len < 0) {
547 err = ERR_INVALID_ARGS;
548 goto done;
549 }
550
551 err = sysparam_add(argv[2].str, hexbuffer, len);
552 free(hexbuffer);
553 } else if (!strcmp(argv[1].str, "addlong")) {
554 if (argc < 3) goto notenoughargs;
555
556 char *str;
557 ssize_t buflen = 64;
558 ssize_t len = 0;
559 str = malloc(buflen + 1);
560 if (!str) {
561 err = ERR_NO_MEMORY;
562 goto done;
563 }
564
565 for (;;) {
566 int c = getchar();
567 if (err < 0)
568 break;
569 if (c == '\r')
570 continue;
571 if (c == '\04') /* ^d */
572 break;
573 if (c == '\n')
574 break;
575
576 if (len == buflen) {
577 buflen *= 2;
578 char *origstr = str;
579 str = realloc(str, buflen + 1);
580 if (!str) {
581 err = ERR_NO_MEMORY;
582 free(origstr);
583 goto done;
584 }
585 }
586
587 str[len++] = c;
588 }
589 str[len] = '\0';
590
591 /* parse the value parameter as a hex code */
592 uint8_t *hexbuffer;
593
594 len = hexstr_to_val(str, &hexbuffer);
595
596 free(str);
597
598 if (len < 0) {
599 err = ERR_INVALID_ARGS;
600 goto done;
601 }
602
603 err = sysparam_add(argv[2].str, hexbuffer, len);
604
605 free(hexbuffer);
606 } else if (!strcmp(argv[1].str, "remove")) {
607 if (argc < 3) goto notenoughargs;
608
609 err = sysparam_remove(argv[2].str);
610 } else if (!strcmp(argv[1].str, "lock")) {
611 if (argc < 3) goto notenoughargs;
612
613 err = sysparam_lock(argv[2].str);
614 } else if (!strcmp(argv[1].str, "write")) {
615 err = sysparam_write();
616 } else if (!strcmp(argv[1].str, "nuke")) {
617 ssize_t err_len = bio_erase(params.bdev, params.offset, params.len);
618 printf("erase returns %ld\n", err_len);
619 #endif // SYSPARAM_ALLOW_WRITE
620 } else if (!strcmp(argv[1].str, "length")) {
621 if (argc < 3) goto notenoughargs;
622 ssize_t len = sysparam_length(argv[2].str);
623 if (len >= 0) {
624 printf("%zu\n", (size_t)len);
625 }
626 err = (len >= 0) ? NO_ERROR : len;
627 } else if (!strcmp(argv[1].str, "read")) {
628 if (argc < 3) goto notenoughargs;
629 ssize_t len = sysparam_length(argv[2].str);
630 if (len < 0) {
631 err = len;
632 goto done;
633 }
634 uint8_t *buf = malloc(len);
635 if (!buf) {
636 err = ERR_NO_MEMORY;
637 goto done;
638 }
639 len = sysparam_read(argv[2].str, buf, len);
640 if (len < 0) {
641 err = len;
642 goto done;
643 }
644 err = NO_ERROR;
645
646 for (int i = 0; i < len; i++)
647 printf("%02x", buf[i]);
648 printf("\n");
649 free(buf);
650 } else {
651 printf("ERROR unknown command\n");
652 goto usage;
653 }
654
655 done:
656 /* shared error reporting */
657 if (err >= NO_ERROR) {
658 printf("OK\n");
659 } else if (err == ERR_NO_MEMORY) {
660 printf("ERROR out of memory\n");
661 } else if (err == ERR_ALREADY_EXISTS) {
662 printf("ERROR already exists\n");
663 } else if (err == ERR_INVALID_ARGS) {
664 printf("ERROR invalid argument\n");
665 } else if (err == ERR_NOT_FOUND) {
666 printf("ERROR not found\n");
667 } else if (err == ERR_NOT_ALLOWED) {
668 printf("ERROR not allowed (locked)\n");
669 } else {
670 printf("ERROR generic error %d\n", err);
671 }
672
673 return 0;
674 }
675
676 STATIC_COMMAND_START
677 STATIC_COMMAND("sysparam", "commands for manipulating system parameters", &cmd_sysparam)
678 STATIC_COMMAND_END(sysparam);
679