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(&params.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(&params.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(&params.list, &param->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(&params.list, param, temp, struct sysparam, node) {
240         list_delete(&param->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(&params.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(&params.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(&params.list, &param->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(&param->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(&params.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(&params.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