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) {} |
