1 | /* |
2 | Copyright (c) 2016 Raspberry Pi (Trading) Ltd. |
3 | All rights reserved. |
4 | |
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, 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 | |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
23 | ON 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 |
25 | SOFTWARE, 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 | |
54 | enum { |
55 | OPT_ADD, |
56 | OPT_REMOVE, |
57 | OPT_REMOVE_FROM, |
58 | OPT_LIST, |
59 | OPT_LIST_ALL, |
60 | OPT_HELP |
61 | }; |
62 | |
63 | static 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 | |
77 | typedef struct state_struct |
78 | { |
79 | int count; |
80 | struct dirent **namelist; |
81 | } STATE_T; |
82 | |
83 | static int dtoverlay_add(STATE_T *state, const char *overlay, |
84 | int argc, const char **argv); |
85 | static int dtoverlay_remove(STATE_T *state, const char *overlay, int and_later); |
86 | static int dtoverlay_list(STATE_T *state); |
87 | static int dtoverlay_list_all(STATE_T *state); |
88 | static void usage(void); |
89 | static void root_check(void); |
90 | |
91 | static void overlay_help(const char *overlay, const char **params); |
92 | |
93 | static int apply_overlay(const char *overlay_file, const char *overlay); |
94 | static int overlay_applied(const char *overlay_dir); |
95 | |
96 | static STATE_T *read_state(const char *dir); |
97 | static void free_state(STATE_T *state); |
98 | |
99 | const char *cmd_name; |
100 | |
101 | const char *work_dir = WORK_DIR; |
102 | const char *overlay_src_dir; |
103 | const char *dt_overlays_dir; |
104 | const char *error_file = NULL; |
105 | int dry_run = 0; |
106 | |
107 | int 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 | |
320 | orderly_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 | |
331 | int 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 |
361 | int 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 | |
389 | static 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 | |
548 | static 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 | |
729 | static 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 | |
770 | static 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 | |
849 | static 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 | |
895 | static void root_check(void) |
896 | { |
897 | if (getuid() != 0) |
898 | fatal_error("Must be run as root - try 'sudo %s ...'" , cmd_name); |
899 | } |
900 | |
901 | static 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 | |
974 | static 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 | |
1016 | static 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 | |
1032 | int seq_filter(const struct dirent *de) |
1033 | { |
1034 | int num; |
1035 | return (sscanf(de->d_name, "%d_" , &num) == 1); |
1036 | } |
1037 | |
1038 | int 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 | |
1050 | static 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 | |
1069 | static 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 | |