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