1/*
2Copyright (c) 2016 Raspberry Pi (Trading) Ltd.
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <unistd.h>
32#include <stdarg.h>
33#include <sys/stat.h>
34#include <dirent.h>
35#include <errno.h>
36
37#include <libfdt.h>
38
39#include "dtoverlay.h"
40#include "utils.h"
41
42
43#define CFG_DIR_1 "/sys/kernel/config"
44#define CFG_DIR_2 "/config"
45#define DT_SUBDIR "/device-tree"
46#define WORK_DIR "/tmp/.dtoverlays"
47#define OVERLAY_SRC_SUBDIR "overlays"
48#define README_FILE "README"
49#define DT_OVERLAYS_SUBDIR "overlays"
50#define DTOVERLAY_PATH_MAX 128
51#define DIR_MODE 0755
52
53
54enum {
55 OPT_ADD,
56 OPT_REMOVE,
57 OPT_REMOVE_FROM,
58 OPT_LIST,
59 OPT_LIST_ALL,
60 OPT_HELP
61};
62
63static const char *boot_dirs[] =
64{
65#ifdef FORCE_BOOT_DIR
66 FORCE_BOOT_DIR,
67#else
68 "/boot",
69 "/flash",
70#ifdef OTHER_BOOT_DIR
71 OTHER_BOOT_DIR,
72#endif
73#endif
74 NULL /* Terminator */
75};
76
77typedef struct state_struct
78{
79 int count;
80 struct dirent **namelist;
81} STATE_T;
82
83static int dtoverlay_add(STATE_T *state, const char *overlay,
84 int argc, const char **argv);
85static int dtoverlay_remove(STATE_T *state, const char *overlay, int and_later);
86static int dtoverlay_list(STATE_T *state);
87static int dtoverlay_list_all(STATE_T *state);
88static void usage(void);
89static void root_check(void);
90
91static void overlay_help(const char *overlay, const char **params);
92
93static int apply_overlay(const char *overlay_file, const char *overlay);
94static int overlay_applied(const char *overlay_dir);
95
96static STATE_T *read_state(const char *dir);
97static void free_state(STATE_T *state);
98
99const char *cmd_name;
100
101const char *work_dir = WORK_DIR;
102const char *overlay_src_dir;
103const char *dt_overlays_dir;
104const char *error_file = NULL;
105int dry_run = 0;
106
107int main(int argc, const char **argv)
108{
109 int argn = 1;
110 int opt = OPT_ADD;
111 int is_dtparam;
112 const char *overlay = NULL;
113 const char **params = NULL;
114 int ret = 0;
115 STATE_T *state = NULL;
116 const char *cfg_dir;
117
118 cmd_name = argv[0];
119 if (strrchr(cmd_name, '/'))
120 cmd_name = strrchr(cmd_name, '/') + 1;
121 is_dtparam = (strcmp(cmd_name, "dtparam") == 0);
122
123 while ((argn < argc) && (argv[argn][0] == '-'))
124 {
125 const char *arg = argv[argn++];
126 if (strcmp(arg, "-r") == 0)
127 {
128 if (opt != OPT_ADD)
129 usage();
130 opt = OPT_REMOVE;
131 }
132 else if (strcmp(arg, "-R") == 0)
133 {
134 if (opt != OPT_ADD)
135 usage();
136 opt = OPT_REMOVE_FROM;
137 }
138 else if (strcmp(arg, "-D") == 0)
139 {
140 if (opt != OPT_ADD)
141 usage();
142 dry_run = 1;
143 work_dir = ".";
144 }
145 else if ((strcmp(arg, "-l") == 0) ||
146 (strcmp(arg, "--list") == 0))
147 {
148 if (opt != OPT_ADD)
149 usage();
150 opt = OPT_LIST;
151 }
152 else if ((strcmp(arg, "-a") == 0) ||
153 (strcmp(arg, "--listall") == 0) ||
154 (strcmp(arg, "--all") == 0))
155 {
156 if (opt != OPT_ADD)
157 usage();
158 opt = OPT_LIST_ALL;
159 }
160 else if (strcmp(arg, "-d") == 0)
161 {
162 if (argn == argc)
163 usage();
164 overlay_src_dir = argv[argn++];
165 }
166 else if (strcmp(arg, "-v") == 0)
167 {
168 opt_verbose = 1;
169 }
170 else if (strcmp(arg, "-h") == 0)
171 {
172 opt = OPT_HELP;
173 }
174 else
175 {
176 fprintf(stderr, "* unknown option '%s'\n", arg);
177 usage();
178 }
179 }
180
181 if ((opt == OPT_ADD) || (opt == OPT_REMOVE) ||
182 (opt == OPT_REMOVE_FROM) || (opt == OPT_HELP))
183 {
184 if ((argn == argc) &&
185 ((!is_dtparam &&
186 ((opt == OPT_ADD) || (opt == OPT_HELP))) ||
187 (is_dtparam && (opt == OPT_HELP))))
188 usage();
189 if (is_dtparam && (opt == OPT_ADD) && (argn == argc))
190 opt = OPT_HELP;
191 if (is_dtparam &&
192 ((opt == OPT_ADD) || (opt == OPT_HELP)))
193 overlay = "dtparam";
194 else if (argn < argc)
195 overlay = argv[argn++];
196 }
197
198 if ((opt == OPT_HELP) && (argn < argc))
199 {
200 params = &argv[argn];
201 argn = argc;
202 }
203
204 if ((opt != OPT_ADD) && (argn != argc))
205 usage();
206
207 dtoverlay_enable_debug(opt_verbose);
208
209 if (!overlay_src_dir)
210 {
211 /* Find the overlays and README */
212 int i;
213
214 for (i = 0; boot_dirs[i]; i++)
215 {
216 overlay_src_dir = sprintf_dup("%s/" OVERLAY_SRC_SUBDIR,
217 boot_dirs[i]);
218 if (dir_exists(overlay_src_dir))
219 break;
220 free_string(overlay_src_dir);
221 overlay_src_dir = NULL;
222 }
223
224 if (!overlay_src_dir)
225 fatal_error("Failed to find overlays directory");
226 }
227
228 if (opt == OPT_HELP)
229 {
230 overlay_help(overlay, params);
231 goto orderly_exit;
232 }
233
234 if (!dir_exists(work_dir))
235 {
236 if (mkdir(work_dir, DIR_MODE) != 0)
237 fatal_error("Failed to create '%s' - %d", work_dir, errno);
238 }
239
240 error_file = sprintf_dup("%s/%s", work_dir, "error.dtb");
241
242 cfg_dir = CFG_DIR_1 DT_SUBDIR;
243 if (!dry_run && !dir_exists(cfg_dir))
244 {
245 root_check();
246
247 cfg_dir = CFG_DIR_2;
248 if (!dir_exists(cfg_dir))
249 {
250 if (mkdir(cfg_dir, DIR_MODE) != 0)
251 fatal_error("Failed to create '%s' - %d", cfg_dir, errno);
252 }
253
254 cfg_dir = CFG_DIR_2 DT_SUBDIR;
255 if (!dir_exists(cfg_dir) &&
256 (run_cmd("mount -t configfs none '%s'", cfg_dir) != 0))
257 fatal_error("Failed to mount configfs - %d", errno);
258 }
259
260 dt_overlays_dir = sprintf_dup("%s/%s", cfg_dir, DT_OVERLAYS_SUBDIR);
261 if (!dir_exists(dt_overlays_dir))
262 fatal_error("configfs overlays folder not found - incompatible kernel");
263
264 if (!dry_run)
265 {
266 state = read_state(work_dir);
267 if (!state)
268 fatal_error("Failed to read state");
269 }
270
271 switch (opt)
272 {
273 case OPT_ADD:
274 case OPT_REMOVE:
275 case OPT_REMOVE_FROM:
276 if (!dry_run)
277 {
278 root_check();
279 run_cmd("which dtoverlay-pre >/dev/null 2>&1 && dtoverlay-pre");
280 }
281 break;
282 default:
283 break;
284 }
285
286 switch (opt)
287 {
288 case OPT_ADD:
289 ret = dtoverlay_add(state, overlay, argc - argn, argv + argn);
290 break;
291 case OPT_REMOVE:
292 ret = dtoverlay_remove(state, overlay, 0);
293 break;
294 case OPT_REMOVE_FROM:
295 ret = dtoverlay_remove(state, overlay, 1);
296 break;
297 case OPT_LIST:
298 ret = dtoverlay_list(state);
299 break;
300 case OPT_LIST_ALL:
301 ret = dtoverlay_list_all(state);
302 break;
303 default:
304 ret = 1;
305 break;
306 }
307
308 switch (opt)
309 {
310 case OPT_ADD:
311 case OPT_REMOVE:
312 case OPT_REMOVE_FROM:
313 if (!dry_run)
314 run_cmd("which dtoverlay-post >/dev/null 2>&1 && dtoverlay-post");
315 break;
316 default:
317 break;
318 }
319
320orderly_exit:
321 if (state)
322 free_state(state);
323 free_strings();
324
325 if ((ret == 0) && error_file)
326 unlink(error_file);
327
328 return ret;
329}
330
331int dtparam_callback(int override_type, const char *override_value,
332 DTBLOB_T *dtb, int node_off,
333 const char *prop_name, int target_phandle,
334 int target_off, int target_size,
335 void *callback_state)
336{
337 STRING_VEC_T *used_props = callback_state;
338 char prop_id[80];
339 int err;
340
341 err = dtoverlay_override_one_target(override_type,
342 override_value,
343 dtb, node_off,
344 prop_name, target_phandle,
345 target_off, target_size,
346 callback_state);
347
348 if ((err == 0) && (target_phandle != 0))
349 {
350 if (snprintf(prop_id, sizeof(prop_id), "%08x%s", target_phandle,
351 prop_name) < 0)
352 err = FDT_ERR_INTERNAL;
353 else if (string_vec_find(used_props, prop_id, 0) < 0)
354 string_vec_add(used_props, prop_id, 0);
355 }
356
357 return err;
358}
359
360// Returns 0 on success, -ve for fatal errors and +ve for non-fatal errors
361int dtparam_apply(DTBLOB_T *dtb, const char *override_name,
362 const char *override_data, int data_len,
363 const char *override_value, STRING_VEC_T *used_props)
364{
365 void *data;
366 int err;
367
368 /* Copy the override data in case it moves */
369 data = malloc(data_len);
370 if (data)
371 {
372 memcpy(data, override_data, data_len);
373 err = dtoverlay_foreach_override_target(dtb, override_name,
374 data, data_len,
375 override_value,
376 dtparam_callback,
377 used_props);
378 free(data);
379 }
380 else
381 {
382 dtoverlay_error("out of memory");
383 err = NON_FATAL(FDT_ERR_NOSPACE);
384 }
385
386 return err;
387}
388
389static int dtoverlay_add(STATE_T *state, const char *overlay,
390 int argc, const char **argv)
391{
392 const char *overlay_name;
393 const char *overlay_file;
394 char *param_string = NULL;
395 int is_dtparam;
396 DTBLOB_T *base_dtb = NULL;
397 DTBLOB_T *overlay_dtb;
398 STRING_VEC_T used_props;
399 int err;
400 int len;
401 int i;
402
403 len = strlen(overlay) - 5;
404 is_dtparam = (strcmp(overlay, "dtparam") == 0);
405 if (is_dtparam)
406 {
407 /* Convert /proc/device-tree to a .dtb and load it */
408 overlay_file = sprintf_dup("%s/%s", work_dir, "base.dtb");
409 if (run_cmd("dtc -I fs -O dtb -o '%s' /proc/device-tree 1>/dev/null 2>&1",
410 overlay_file) != 0)
411 return error("Failed to read active DTB");
412 }
413 else if ((len > 0) && (strcmp(overlay + len, ".dtbo") == 0))
414 {
415 const char *p;
416 overlay_file = overlay;
417 p = strrchr(overlay, '/');
418 if (p)
419 {
420 overlay = p + 1;
421 len = strlen(overlay) - 5;
422 }
423
424 overlay = sprintf_dup("%.*s", len, overlay);
425 }
426 else
427 {
428 overlay_file = sprintf_dup("%s/%s.dtbo", overlay_src_dir, overlay);
429 }
430
431 if (dry_run)
432 overlay_name = "dry_run";
433 else
434 overlay_name = sprintf_dup("%d_%s", state->count, overlay);
435 overlay_dtb = dtoverlay_load_dtb(overlay_file, DTOVERLAY_PADDING(4096));
436 if (!overlay_dtb)
437 return error("Failed to read '%s'", overlay_file);
438
439 if (is_dtparam)
440 {
441 base_dtb = overlay_dtb;
442 string_vec_init(&used_props);
443 }
444
445 /* Apply any parameters next */
446 for (i = 0; i < argc; i++)
447 {
448 const char *arg = argv[i];
449 const char *param_val = strchr(arg, '=');
450 const char *param, *override;
451 char *p = NULL;
452 int override_len;
453 if (param_val)
454 {
455 int len = (param_val - arg);
456 p = sprintf_dup("%.*s", len, arg);
457 param = p;
458 param_val++;
459 }
460 else
461 {
462 /* Use the default parameter value - true */
463 param = arg;
464 param_val = "true";
465 }
466
467 override = dtoverlay_find_override(overlay_dtb, param, &override_len);
468
469 if (!override)
470 return error("Unknown parameter '%s'", param);
471
472 if (is_dtparam)
473 err = dtparam_apply(overlay_dtb, param,
474 override, override_len,
475 param_val, &used_props);
476 else
477 err = dtoverlay_apply_override(overlay_dtb, param,
478 override, override_len,
479 param_val);
480 if (err != 0)
481 return error("Failed to set %s=%s", param, param_val);
482
483 param_string = sprintf_dup("%s %s=%s",
484 param_string ? param_string : "",
485 param, param_val);
486
487 free_string(p);
488 }
489
490 if (is_dtparam)
491 {
492 /* Build an overlay DTB */
493 overlay_dtb = dtoverlay_create_dtb(2048 + 256 * used_props.num_strings);
494
495 for (i = 0; i < used_props.num_strings; i++)
496 {
497 int phandle, node_off, prop_len;
498 const char *str, *prop_name;
499 const void *prop_data;
500
501 str = used_props.strings[i];
502 sscanf(str, "%8x", &phandle);
503 prop_name = str + 8;
504 node_off = dtoverlay_find_phandle(base_dtb, phandle);
505
506 prop_data = dtoverlay_get_property(base_dtb, node_off,
507 prop_name, &prop_len);
508 err = dtoverlay_create_prop_fragment(overlay_dtb, i, phandle,
509 prop_name, prop_data, prop_len);
510 }
511
512 dtoverlay_free_dtb(base_dtb);
513 }
514
515 /* Prevent symbol clash by keeping them all private.
516 * In future we could choose to expose some - perhaps using
517 * a naming convention, or an "__exports__" node, at which
518 * point it will no longer be necessary to explictly target
519 * the /__symbols__ node with a fragment.
520 */
521 dtoverlay_delete_node(overlay_dtb, "/__symbols__", 0);
522
523 if (param_string)
524 dtoverlay_dtb_set_trailer(overlay_dtb, param_string,
525 strlen(param_string) + 1);
526
527 /* Create a filename with the sequence number */
528 overlay_file = sprintf_dup("%s/%s.dtbo", work_dir, overlay_name);
529
530 /* then write the overlay to the file */
531 dtoverlay_pack_dtb(overlay_dtb);
532 dtoverlay_save_dtb(overlay_dtb, overlay_file);
533 dtoverlay_free_dtb(overlay_dtb);
534
535 if (!dry_run && !apply_overlay(overlay_file, overlay_name))
536 {
537 if (error_file)
538 {
539 rename(overlay_file, error_file);
540 free_string(error_file);
541 }
542 return 1;
543 }
544
545 return 0;
546}
547
548static int dtoverlay_remove(STATE_T *state, const char *overlay, int and_later)
549{
550 const char *overlay_dir;
551 const char *dir_name = NULL;
552 char *end;
553 int overlay_len;
554 int count = state->count;
555 int rmpos;
556 int i;
557
558 if (chdir(work_dir) != 0)
559 fatal_error("Failed to chdir to '%s'", work_dir);
560
561 if (overlay)
562 {
563 overlay_len = strlen(overlay);
564
565 rmpos = strtoul(overlay, &end, 10);
566 if (end && (*end == '\0'))
567 {
568 if (rmpos >= count)
569 return error("Overlay index (%d) too large", rmpos);
570 dir_name = state->namelist[rmpos]->d_name;
571 }
572 /* Locate the most recent reference to the overlay */
573 else for (rmpos = count - 1; rmpos >= 0; rmpos--)
574 {
575 const char *left, *right;
576 dir_name = state->namelist[rmpos]->d_name;
577 left = strchr(dir_name, '_');
578 if (!left)
579 return error("Internal error");
580 left++;
581 right = strchr(left, '.');
582 if (!right)
583 return error("Internal error");
584 if (((right - left) == overlay_len) &&
585 (memcmp(overlay, left, overlay_len) == 0))
586 break;
587 dir_name = NULL;
588 }
589
590 if (rmpos < 0)
591 return error("Overlay '%s' is not loaded", overlay);
592 }
593 else
594 {
595 if (!count)
596 return error("No overlays loaded");
597 rmpos = and_later ? 0 : (count - 1);
598 dir_name = state->namelist[rmpos]->d_name;
599 }
600
601 if (rmpos < count)
602 {
603 /* Unload it and all subsequent overlays in reverse order */
604 for (i = count - 1; i >= rmpos; i--)
605 {
606 const char *left, *right;
607 left = state->namelist[i]->d_name;
608 right = strrchr(left, '.');
609 if (!right)
610 return error("Internal error");
611
612 overlay_dir = sprintf_dup("%s/%.*s", dt_overlays_dir,
613 right - left, left);
614 if (rmdir(overlay_dir) != 0)
615 return error("Failed to remove directory '%s'", overlay_dir);
616
617 free_string(overlay_dir);
618 }
619
620 /* Replay the sequence, deleting files for the specified overlay,
621 and renumbering and reloading all other overlays. */
622 for (i = rmpos, state->count = rmpos; i < count; i++)
623 {
624 const char *left, *right;
625 const char *filename = state->namelist[i]->d_name;
626
627 left = strchr(filename, '_');
628 if (!left)
629 return error("Internal error");
630 left++;
631 right = strchr(left, '.');
632 if (!right)
633 return error("Internal error");
634
635 if (and_later || (i == rmpos))
636 {
637 /* This one is being deleted */
638 unlink(filename);
639 }
640 else
641 {
642 /* Keep this one - renumber and reload */
643 int len = right - left;
644 char *new_name = sprintf_dup("%d_%.*s", state->count,
645 len, left);
646 char *new_file = sprintf_dup("%s.dtbo", new_name);
647 int ret = 0;
648
649 if ((len == 7) && (memcmp(left, "dtparam", 7) == 0))
650 {
651 /* Regenerate the overlay in case multiple overlays target
652 different parts of the same property. */
653
654 DTBLOB_T *dtb;
655 char *params;
656 const char **paramv;
657 int paramc;
658 int j;
659 char *p;
660
661 /* Extract the parameters */
662 dtb = dtoverlay_load_dtb(filename, 0);
663 unlink(filename);
664
665 if (!dtb)
666 {
667 error("Failed to re-apply dtparam");
668 continue;
669 }
670
671 params = (char *)dtoverlay_dtb_trailer(dtb);
672 if (!params)
673 {
674 error("Failed to re-apply dtparam");
675 dtoverlay_free_dtb(dtb);
676 continue;
677 }
678
679 /* Count and NUL-separate the params */
680 p = params;
681 paramc = 0;
682 while (*p)
683 {
684 int paramlen;
685 *(p++) = '\0';
686 paramlen = strcspn(p, " ");
687 paramc++;
688 p += paramlen;
689 }
690
691 paramv = malloc((paramc + 1) * sizeof(const char *));
692 if (!paramv)
693 {
694 error("out of memory re-applying dtparam");
695 dtoverlay_free_dtb(dtb);
696 continue;
697 }
698
699 for (j = 0, p = params + 1; j < paramc; j++)
700 {
701 paramv[j] = p;
702 p += strlen(p) + 1;
703 }
704 paramv[j] = NULL;
705
706 /* Create the new overlay */
707 ret = dtoverlay_add(state, "dtparam", paramc, paramv);
708 free(paramv);
709 dtoverlay_free_dtb(dtb);
710 }
711 else
712 {
713 rename(filename, new_file);
714 ret = !apply_overlay(new_file, new_name);
715 }
716 if (ret != 0)
717 {
718 error("Failed to re-apply dtparam");
719 continue;
720 }
721 state->count++;
722 }
723 }
724 }
725
726 return 0;
727}
728
729static int dtoverlay_list(STATE_T *state)
730{
731 if (state->count == 0)
732 {
733 printf("No overlays loaded\n");
734 }
735 else
736 {
737 int i;
738 printf("Overlays (in load order):\n");
739 for (i = 0; i < state->count; i++)
740 {
741 const char *name, *left, *right;
742 const char *saved_overlay;
743 DTBLOB_T *dtb;
744 name = state->namelist[i]->d_name;
745 left = strchr(name, '_');
746 if (!left)
747 return error("Internal error");
748 left++;
749 right = strchr(left, '.');
750 if (!right)
751 return error("Internal error");
752
753 saved_overlay = sprintf_dup("%s/%s", work_dir, name);
754 dtb = dtoverlay_load_dtb(saved_overlay, 0);
755
756 if (dtoverlay_dtb_trailer(dtb))
757 printf("%d: %.*s %.*s\n", i, (int)(right - left), left,
758 dtoverlay_dtb_trailer_len(dtb),
759 (char *)dtoverlay_dtb_trailer(dtb));
760 else
761 printf("%d: %.*s\n", i, (int)(right - left), left);
762
763 dtoverlay_free_dtb(dtb);
764 }
765 }
766
767 return 0;
768}
769
770static int dtoverlay_list_all(STATE_T *state)
771{
772 int i;
773 DIR *dh;
774 struct dirent *de;
775 STRING_VEC_T strings;
776
777 string_vec_init(&strings);
778
779 /* Enumerate .dtbo files in the /boot/overlays directory */
780 dh = opendir(overlay_src_dir);
781 while ((de = readdir(dh)) != NULL)
782 {
783 int len = strlen(de->d_name) - 5;
784 if ((len >= 0) && strcmp(de->d_name + len, ".dtbo") == 0)
785 {
786 char *str = string_vec_add(&strings, de->d_name, len + 2);
787 str[len] = '\0';
788 str[len + 1] = ' ';
789 }
790 }
791 closedir(dh);
792
793 /* Merge in active overlays, marking them */
794 for (i = 0; i < state->count; i++)
795 {
796 const char *left, *right;
797 char *str;
798 int len, idx;
799
800 left = strchr(state->namelist[i]->d_name, '_');
801 if (!left)
802 return error("Internal error");
803 left++;
804 right = strchr(left, '.');
805 if (!right)
806 return error("Internal error");
807
808 len = right - left;
809 if ((len == 7) && (memcmp(left, "dtparam", 7) == 0))
810 continue;
811 idx = string_vec_find(&strings, left, len);
812 if (idx >= 0)
813 {
814 str = strings.strings[idx];
815 len = strlen(str);
816 }
817 else
818 {
819 str = string_vec_add(&strings, left, len + 2);
820 str[len] = '\0';
821 }
822 str[len + 1] = '*';
823 }
824
825 if (strings.num_strings == 0)
826 {
827 printf("No overlays found\n");
828 }
829 else
830 {
831 /* Sort */
832 string_vec_sort(&strings);
833
834 /* Display */
835 printf("All overlays (* = loaded):\n");
836
837 for (i = 0; i < strings.num_strings; i++)
838 {
839 const char *str = strings.strings[i];
840 printf("%c %s\n", str[strlen(str)+1], str);
841 }
842 }
843
844 string_vec_uninit(&strings);
845
846 return 0;
847}
848
849static void usage(void)
850{
851 printf("Usage:\n");
852 if (strcmp(cmd_name, "dtparam") == 0)
853 {
854 printf(" %s Display help on all parameters\n", cmd_name);
855 printf(" %s <param>=<val>...\n", cmd_name);
856 printf(" %*s Add an overlay (with parameters)\n", (int)strlen(cmd_name), "");
857 printf(" %s -D [<idx>] Dry-run (prepare overlay, but don't apply -\n",
858 cmd_name);
859 printf(" %*s save it as dry-run.dtbo)\n", (int)strlen(cmd_name), "");
860 printf(" %s -r [<idx>] Remove an overlay (by index, or the last)\n", cmd_name);
861 printf(" %s -R [<idx>] Remove from an overlay (by index, or all)\n",
862 cmd_name);
863 printf(" %s -l List active overlays/dtparams\n", cmd_name);
864 printf(" %s -a List all overlays/dtparams (marking the active)\n", cmd_name);
865 printf(" %s -h Show this usage message\n", cmd_name);
866 printf(" %s -h <param>... Display help on the listed parameters\n", cmd_name);
867 }
868 else
869 {
870 printf(" %s <overlay> [<param>=<val>...]\n", cmd_name);
871 printf(" %*s Add an overlay (with parameters)\n", (int)strlen(cmd_name), "");
872 printf(" %s -D [<idx>] Dry-run (prepare overlay, but don't apply -\n",
873 cmd_name);
874 printf(" %*s save it as dry-run.dtbo)\n", (int)strlen(cmd_name), "");
875 printf(" %s -r [<overlay>] Remove an overlay (by name, index or the last)\n", cmd_name);
876 printf(" %s -R [<overlay>] Remove from an overlay (by name, index or all)\n",
877 cmd_name);
878 printf(" %s -l List active overlays/params\n", cmd_name);
879 printf(" %s -a List all overlays (marking the active)\n", cmd_name);
880 printf(" %s -h Show this usage message\n", cmd_name);
881 printf(" %s -h <overlay> Display help on an overlay\n", cmd_name);
882 printf(" %s -h <overlay> <param>.. Or its parameters\n", cmd_name);
883 printf(" where <overlay> is the name of an overlay or 'dtparam' for dtparams\n");
884 }
885 printf("Options applicable to most variants:\n");
886 printf(" -d <dir> Specify an alternate location for the overlays\n");
887 printf(" (defaults to /boot/overlays or /flash/overlays)\n");
888 printf(" -v Verbose operation\n");
889 printf("\n");
890 printf("Adding or removing overlays and parameters requires root privileges.\n");
891
892 exit(1);
893}
894
895static void root_check(void)
896{
897 if (getuid() != 0)
898 fatal_error("Must be run as root - try 'sudo %s ...'", cmd_name);
899}
900
901static void overlay_help(const char *overlay, const char **params)
902{
903 OVERLAY_HELP_STATE_T *state;
904 const char *readme_path = sprintf_dup("%s/%s", overlay_src_dir,
905 README_FILE);
906
907 state = overlay_help_open(readme_path);
908 free_string(readme_path);
909
910 if (state)
911 {
912 if (strcmp(overlay, "dtparam") == 0)
913 overlay = "<The base DTB>";
914
915 if (overlay_help_find(state, overlay))
916 {
917 if (params && overlay_help_find_field(state, "Params"))
918 {
919 int in_param = 0;
920
921 while (1)
922 {
923 const char *line = overlay_help_field_data(state);
924 if (!line)
925 break;
926 if (line[0] == '\0')
927 continue;
928 if (line[0] != ' ')
929 {
930 /* This is a parameter name */
931 int param_len = strcspn(line, " ");
932 const char **p = params;
933 const char **q = p;
934 in_param = 0;
935 while (*p)
936 {
937 if ((param_len == strlen(*p)) &&
938 (memcmp(line, *p, param_len) == 0))
939 in_param = 1;
940 else
941 *(q++) = *p;
942 p++;
943 }
944 *(q++) = 0;
945 }
946 if (in_param)
947 printf("%s\n", line);
948 }
949 /* This only shows the first unknown parameter, but
950 * that is enough. */
951 if (*params)
952 fatal_error("Unknown parameter '%s'", *params);
953 }
954 else
955 {
956 printf("Name: %s\n\n", overlay);
957 overlay_help_print_field(state, "Info", "Info:", 8, 0);
958 overlay_help_print_field(state, "Load", "Usage:", 8, 0);
959 overlay_help_print_field(state, "Params", "Params:", 8, 0);
960 }
961 }
962 else
963 {
964 fatal_error("No help found for overlay '%s'", overlay);
965 }
966 overlay_help_close(state);
967 }
968 else
969 {
970 fatal_error("Help file not found");
971 }
972}
973
974static int apply_overlay(const char *overlay_file, const char *overlay)
975{
976 const char *overlay_dir = sprintf_dup("%s/%s", dt_overlays_dir, overlay);
977 int ret = 0;
978 if (dir_exists(overlay_dir))
979 {
980 error("Overlay '%s' is already loaded", overlay);
981 }
982 else if (mkdir(overlay_dir, DIR_MODE) == 0)
983 {
984 DTBLOB_T *dtb = dtoverlay_load_dtb(overlay_file, 0);
985 if (!dtb)
986 {
987 error("Failed to apply overlay '%s' (load)", overlay);
988 }
989 else
990 {
991 const char *dest_file = sprintf_dup("%s/dtbo", overlay_dir);
992
993 /* then write the overlay to the file */
994 if (dtoverlay_save_dtb(dtb, dest_file) != 0)
995 error("Failed to apply overlay '%s' (save)", overlay);
996 else if (!overlay_applied(overlay_dir))
997 error("Failed to apply overlay '%s' (kernel)", overlay);
998 else
999 ret = 1;
1000
1001 free_string(dest_file);
1002 dtoverlay_free_dtb(dtb);
1003 }
1004
1005 if (!ret)
1006 rmdir(overlay_dir);
1007 }
1008 else
1009 {
1010 error("Failed to create overlay directory");
1011 }
1012
1013 return ret;
1014}
1015
1016static int overlay_applied(const char *overlay_dir)
1017{
1018 char status[7] = { '\0' };
1019 const char *status_path = sprintf_dup("%s/status", overlay_dir);
1020 FILE *fp = fopen(status_path, "r");
1021 int bytes = 0;
1022 if (fp)
1023 {
1024 bytes = fread(status, 1, sizeof(status), fp);
1025 fclose(fp);
1026 }
1027 free_string(status_path);
1028 return (bytes == sizeof(status)) &&
1029 (memcmp(status, "applied", sizeof(status)) == 0);
1030}
1031
1032int seq_filter(const struct dirent *de)
1033{
1034 int num;
1035 return (sscanf(de->d_name, "%d_", &num) == 1);
1036}
1037
1038int seq_compare(const struct dirent **de1, const struct dirent **de2)
1039{
1040 int num1 = atoi((*de1)->d_name);
1041 int num2 = atoi((*de2)->d_name);
1042 if (num1 < num2)
1043 return -1;
1044 else if (num1 == num2)
1045 return 0;
1046 else
1047 return 1;
1048}
1049
1050static STATE_T *read_state(const char *dir)
1051{
1052 STATE_T *state = malloc(sizeof(STATE_T));
1053 int i;
1054
1055 if (state)
1056 {
1057 state->count = scandir(dir, &state->namelist, seq_filter, seq_compare);
1058
1059 for (i = 0; i < state->count; i++)
1060 {
1061 int num = atoi(state->namelist[i]->d_name);
1062 if (i != num)
1063 error("Overlay sequence error");
1064 }
1065 }
1066 return state;
1067}
1068
1069static void free_state(STATE_T *state)
1070{
1071 int i;
1072 for (i = 0; i < state->count; i++)
1073 {
1074 free(state->namelist[i]);
1075 }
1076 free(state->namelist);
1077 free(state);
1078}
1079