diff options
-rw-r--r-- | drivers/of/fdt.c | 131 | ||||
-rw-r--r-- | include/linux/of_fdt.h | 4 |
2 files changed, 135 insertions, 0 deletions
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index 758b4f8b30b7..819e11209718 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/of.h> | 16 | #include <linux/of.h> |
17 | #include <linux/of_fdt.h> | 17 | #include <linux/of_fdt.h> |
18 | #include <linux/sizes.h> | ||
18 | #include <linux/string.h> | 19 | #include <linux/string.h> |
19 | #include <linux/errno.h> | 20 | #include <linux/errno.h> |
20 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
@@ -440,6 +441,118 @@ struct boot_param_header *initial_boot_params; | |||
440 | #ifdef CONFIG_OF_EARLY_FLATTREE | 441 | #ifdef CONFIG_OF_EARLY_FLATTREE |
441 | 442 | ||
442 | /** | 443 | /** |
444 | * res_mem_reserve_reg() - reserve all memory described in 'reg' property | ||
445 | */ | ||
446 | static int __init __reserved_mem_reserve_reg(unsigned long node, | ||
447 | const char *uname) | ||
448 | { | ||
449 | int t_len = (dt_root_addr_cells + dt_root_size_cells) * sizeof(__be32); | ||
450 | phys_addr_t base, size; | ||
451 | unsigned long len; | ||
452 | __be32 *prop; | ||
453 | int nomap; | ||
454 | |||
455 | prop = of_get_flat_dt_prop(node, "reg", &len); | ||
456 | if (!prop) | ||
457 | return -ENOENT; | ||
458 | |||
459 | if (len && len % t_len != 0) { | ||
460 | pr_err("Reserved memory: invalid reg property in '%s', skipping node.\n", | ||
461 | uname); | ||
462 | return -EINVAL; | ||
463 | } | ||
464 | |||
465 | nomap = of_get_flat_dt_prop(node, "no-map", NULL) != NULL; | ||
466 | |||
467 | while (len >= t_len) { | ||
468 | base = dt_mem_next_cell(dt_root_addr_cells, &prop); | ||
469 | size = dt_mem_next_cell(dt_root_size_cells, &prop); | ||
470 | |||
471 | if (base && size && | ||
472 | early_init_dt_reserve_memory_arch(base, size, nomap) == 0) | ||
473 | pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %ld MiB\n", | ||
474 | uname, &base, (unsigned long)size / SZ_1M); | ||
475 | else | ||
476 | pr_info("Reserved memory: failed to reserve memory for node '%s': base %pa, size %ld MiB\n", | ||
477 | uname, &base, (unsigned long)size / SZ_1M); | ||
478 | |||
479 | len -= t_len; | ||
480 | } | ||
481 | return 0; | ||
482 | } | ||
483 | |||
484 | /** | ||
485 | * __reserved_mem_check_root() - check if #size-cells, #address-cells provided | ||
486 | * in /reserved-memory matches the values supported by the current implementation, | ||
487 | * also check if ranges property has been provided | ||
488 | */ | ||
489 | static int __reserved_mem_check_root(unsigned long node) | ||
490 | { | ||
491 | __be32 *prop; | ||
492 | |||
493 | prop = of_get_flat_dt_prop(node, "#size-cells", NULL); | ||
494 | if (!prop || be32_to_cpup(prop) != dt_root_size_cells) | ||
495 | return -EINVAL; | ||
496 | |||
497 | prop = of_get_flat_dt_prop(node, "#address-cells", NULL); | ||
498 | if (!prop || be32_to_cpup(prop) != dt_root_addr_cells) | ||
499 | return -EINVAL; | ||
500 | |||
501 | prop = of_get_flat_dt_prop(node, "ranges", NULL); | ||
502 | if (!prop) | ||
503 | return -EINVAL; | ||
504 | return 0; | ||
505 | } | ||
506 | |||
507 | /** | ||
508 | * fdt_scan_reserved_mem() - scan a single FDT node for reserved memory | ||
509 | */ | ||
510 | static int __init __fdt_scan_reserved_mem(unsigned long node, const char *uname, | ||
511 | int depth, void *data) | ||
512 | { | ||
513 | static int found; | ||
514 | const char *status; | ||
515 | |||
516 | if (!found && depth == 1 && strcmp(uname, "reserved-memory") == 0) { | ||
517 | if (__reserved_mem_check_root(node) != 0) { | ||
518 | pr_err("Reserved memory: unsupported node format, ignoring\n"); | ||
519 | /* break scan */ | ||
520 | return 1; | ||
521 | } | ||
522 | found = 1; | ||
523 | /* scan next node */ | ||
524 | return 0; | ||
525 | } else if (!found) { | ||
526 | /* scan next node */ | ||
527 | return 0; | ||
528 | } else if (found && depth < 2) { | ||
529 | /* scanning of /reserved-memory has been finished */ | ||
530 | return 1; | ||
531 | } | ||
532 | |||
533 | status = of_get_flat_dt_prop(node, "status", NULL); | ||
534 | if (status && strcmp(status, "okay") != 0 && strcmp(status, "ok") != 0) | ||
535 | return 0; | ||
536 | |||
537 | __reserved_mem_reserve_reg(node, uname); | ||
538 | |||
539 | /* scan next node */ | ||
540 | return 0; | ||
541 | } | ||
542 | |||
543 | /** | ||
544 | * early_init_fdt_scan_reserved_mem() - create reserved memory regions | ||
545 | * | ||
546 | * This function grabs memory from early allocator for device exclusive use | ||
547 | * defined in device tree structures. It should be called by arch specific code | ||
548 | * once the early allocator (i.e. memblock) has been fully activated. | ||
549 | */ | ||
550 | void __init early_init_fdt_scan_reserved_mem(void) | ||
551 | { | ||
552 | of_scan_flat_dt(__fdt_scan_reserved_mem, NULL); | ||
553 | } | ||
554 | |||
555 | /** | ||
443 | * of_scan_flat_dt - scan flattened tree blob and call callback on each. | 556 | * of_scan_flat_dt - scan flattened tree blob and call callback on each. |
444 | * @it: callback function | 557 | * @it: callback function |
445 | * @data: context data pointer | 558 | * @data: context data pointer |
@@ -856,6 +969,16 @@ void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size) | |||
856 | memblock_add(base, size); | 969 | memblock_add(base, size); |
857 | } | 970 | } |
858 | 971 | ||
972 | int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base, | ||
973 | phys_addr_t size, bool nomap) | ||
974 | { | ||
975 | if (memblock_is_region_reserved(base, size)) | ||
976 | return -EBUSY; | ||
977 | if (nomap) | ||
978 | return memblock_remove(base, size); | ||
979 | return memblock_reserve(base, size); | ||
980 | } | ||
981 | |||
859 | /* | 982 | /* |
860 | * called from unflatten_device_tree() to bootstrap devicetree itself | 983 | * called from unflatten_device_tree() to bootstrap devicetree itself |
861 | * Architectures can override this definition if memblock isn't used | 984 | * Architectures can override this definition if memblock isn't used |
@@ -864,6 +987,14 @@ void * __init __weak early_init_dt_alloc_memory_arch(u64 size, u64 align) | |||
864 | { | 987 | { |
865 | return __va(memblock_alloc(size, align)); | 988 | return __va(memblock_alloc(size, align)); |
866 | } | 989 | } |
990 | #else | ||
991 | int __init __weak early_init_dt_reserve_memory_arch(phys_addr_t base, | ||
992 | phys_addr_t size, bool nomap) | ||
993 | { | ||
994 | pr_err("Reserved memory not supported, ignoring range 0x%llx - 0x%llx%s\n", | ||
995 | base, size, nomap ? " (nomap)" : ""); | ||
996 | return -ENOSYS; | ||
997 | } | ||
867 | #endif | 998 | #endif |
868 | 999 | ||
869 | bool __init early_init_dt_scan(void *params) | 1000 | bool __init early_init_dt_scan(void *params) |
diff --git a/include/linux/of_fdt.h b/include/linux/of_fdt.h index 2b77058a7335..ddd7219af8ac 100644 --- a/include/linux/of_fdt.h +++ b/include/linux/of_fdt.h | |||
@@ -98,7 +98,10 @@ extern int early_init_dt_scan_chosen(unsigned long node, const char *uname, | |||
98 | int depth, void *data); | 98 | int depth, void *data); |
99 | extern int early_init_dt_scan_memory(unsigned long node, const char *uname, | 99 | extern int early_init_dt_scan_memory(unsigned long node, const char *uname, |
100 | int depth, void *data); | 100 | int depth, void *data); |
101 | extern void early_init_fdt_scan_reserved_mem(void); | ||
101 | extern void early_init_dt_add_memory_arch(u64 base, u64 size); | 102 | extern void early_init_dt_add_memory_arch(u64 base, u64 size); |
103 | extern int early_init_dt_reserve_memory_arch(phys_addr_t base, phys_addr_t size, | ||
104 | bool no_map); | ||
102 | extern void * early_init_dt_alloc_memory_arch(u64 size, u64 align); | 105 | extern void * early_init_dt_alloc_memory_arch(u64 size, u64 align); |
103 | extern u64 dt_mem_next_cell(int s, __be32 **cellp); | 106 | extern u64 dt_mem_next_cell(int s, __be32 **cellp); |
104 | 107 | ||
@@ -118,6 +121,7 @@ extern void unflatten_and_copy_device_tree(void); | |||
118 | extern void early_init_devtree(void *); | 121 | extern void early_init_devtree(void *); |
119 | extern void early_get_first_memblock_info(void *, phys_addr_t *); | 122 | extern void early_get_first_memblock_info(void *, phys_addr_t *); |
120 | #else /* CONFIG_OF_FLATTREE */ | 123 | #else /* CONFIG_OF_FLATTREE */ |
124 | static inline void early_init_fdt_scan_reserved_mem(void) {} | ||
121 | static inline const char *of_flat_dt_get_machine_name(void) { return NULL; } | 125 | static inline const char *of_flat_dt_get_machine_name(void) { return NULL; } |
122 | static inline void unflatten_device_tree(void) {} | 126 | static inline void unflatten_device_tree(void) {} |
123 | static inline void unflatten_and_copy_device_tree(void) {} | 127 | static inline void unflatten_and_copy_device_tree(void) {} |