| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Procedures for creating, accessing and interpreting the device tree. |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 6 | 7 | * |
|---|
| 7 | 8 | * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner. |
|---|
| 8 | 9 | * {engebret|bergner}@us.ibm.com |
|---|
| 9 | | - * |
|---|
| 10 | | - * This program is free software; you can redistribute it and/or |
|---|
| 11 | | - * modify it under the terms of the GNU General Public License |
|---|
| 12 | | - * as published by the Free Software Foundation; either version |
|---|
| 13 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 14 | 10 | */ |
|---|
| 15 | 11 | |
|---|
| 16 | 12 | #undef DEBUG |
|---|
| .. | .. |
|---|
| 34 | 30 | #include <linux/of_fdt.h> |
|---|
| 35 | 31 | #include <linux/libfdt.h> |
|---|
| 36 | 32 | #include <linux/cpu.h> |
|---|
| 33 | +#include <linux/pgtable.h> |
|---|
| 37 | 34 | |
|---|
| 38 | 35 | #include <asm/prom.h> |
|---|
| 39 | 36 | #include <asm/rtas.h> |
|---|
| .. | .. |
|---|
| 45 | 42 | #include <asm/smp.h> |
|---|
| 46 | 43 | #include <asm/mmu.h> |
|---|
| 47 | 44 | #include <asm/paca.h> |
|---|
| 48 | | -#include <asm/pgtable.h> |
|---|
| 49 | 45 | #include <asm/powernv.h> |
|---|
| 50 | 46 | #include <asm/iommu.h> |
|---|
| 51 | 47 | #include <asm/btext.h> |
|---|
| .. | .. |
|---|
| 59 | 55 | #include <asm/firmware.h> |
|---|
| 60 | 56 | #include <asm/dt_cpu_ftrs.h> |
|---|
| 61 | 57 | #include <asm/drmem.h> |
|---|
| 58 | +#include <asm/ultravisor.h> |
|---|
| 62 | 59 | |
|---|
| 63 | 60 | #include <mm/mmu_decl.h> |
|---|
| 64 | 61 | |
|---|
| .. | .. |
|---|
| 99 | 96 | if (!initrd_start) |
|---|
| 100 | 97 | return 0; |
|---|
| 101 | 98 | |
|---|
| 102 | | - return (start + size) > _ALIGN_DOWN(initrd_start, PAGE_SIZE) && |
|---|
| 103 | | - start <= _ALIGN_UP(initrd_end, PAGE_SIZE); |
|---|
| 99 | + return (start + size) > ALIGN_DOWN(initrd_start, PAGE_SIZE) && |
|---|
| 100 | + start <= ALIGN(initrd_end, PAGE_SIZE); |
|---|
| 104 | 101 | #else |
|---|
| 105 | 102 | return 0; |
|---|
| 106 | 103 | #endif |
|---|
| .. | .. |
|---|
| 124 | 121 | size = fdt_totalsize(initial_boot_params); |
|---|
| 125 | 122 | |
|---|
| 126 | 123 | if ((memory_limit && (start + size) > PHYSICAL_START + memory_limit) || |
|---|
| 127 | | - overlaps_crashkernel(start, size) || |
|---|
| 128 | | - overlaps_initrd(start, size)) { |
|---|
| 129 | | - p = __va(memblock_alloc(size, PAGE_SIZE)); |
|---|
| 124 | + !memblock_is_memory(start + size - 1) || |
|---|
| 125 | + overlaps_crashkernel(start, size) || overlaps_initrd(start, size)) { |
|---|
| 126 | + p = memblock_alloc_raw(size, PAGE_SIZE); |
|---|
| 127 | + if (!p) |
|---|
| 128 | + panic("Failed to allocate %lu bytes to move device tree\n", |
|---|
| 129 | + size); |
|---|
| 130 | 130 | memcpy(p, initial_boot_params, size); |
|---|
| 131 | 131 | initial_boot_params = p; |
|---|
| 132 | 132 | DBG("Moved device tree to 0x%px\n", p); |
|---|
| .. | .. |
|---|
| 163 | 163 | { .pabyte = 0, .pabit = 6, .cpu_features = CPU_FTR_NOEXECUTE }, |
|---|
| 164 | 164 | { .pabyte = 1, .pabit = 2, .mmu_features = MMU_FTR_CI_LARGE_PAGE }, |
|---|
| 165 | 165 | #ifdef CONFIG_PPC_RADIX_MMU |
|---|
| 166 | | - { .pabyte = 40, .pabit = 0, .mmu_features = MMU_FTR_TYPE_RADIX }, |
|---|
| 166 | + { .pabyte = 40, .pabit = 0, .mmu_features = MMU_FTR_TYPE_RADIX | MMU_FTR_GTSE }, |
|---|
| 167 | 167 | #endif |
|---|
| 168 | 168 | { .pabyte = 1, .pabit = 1, .invert = 1, .cpu_features = CPU_FTR_NODSISRALIGN }, |
|---|
| 169 | 169 | { .pabyte = 5, .pabit = 0, .cpu_features = CPU_FTR_REAL_LE, |
|---|
| .. | .. |
|---|
| 175 | 175 | */ |
|---|
| 176 | 176 | { .pabyte = 22, .pabit = 0, .cpu_features = CPU_FTR_TM_COMP, |
|---|
| 177 | 177 | .cpu_user_ftrs2 = PPC_FEATURE2_HTM_COMP | PPC_FEATURE2_HTM_NOSC_COMP }, |
|---|
| 178 | + |
|---|
| 179 | + { .pabyte = 64, .pabit = 0, .cpu_features = CPU_FTR_DAWR1 }, |
|---|
| 178 | 180 | }; |
|---|
| 179 | 181 | |
|---|
| 180 | 182 | static void __init scan_features(unsigned long node, const unsigned char *ftrs, |
|---|
| .. | .. |
|---|
| 468 | 470 | * This contains a list of memory blocks along with NUMA affinity |
|---|
| 469 | 471 | * information. |
|---|
| 470 | 472 | */ |
|---|
| 471 | | -static void __init early_init_drmem_lmb(struct drmem_lmb *lmb, |
|---|
| 472 | | - const __be32 **usm) |
|---|
| 473 | +static int __init early_init_drmem_lmb(struct drmem_lmb *lmb, |
|---|
| 474 | + const __be32 **usm, |
|---|
| 475 | + void *data) |
|---|
| 473 | 476 | { |
|---|
| 474 | 477 | u64 base, size; |
|---|
| 475 | 478 | int is_kexec_kdump = 0, rngs; |
|---|
| .. | .. |
|---|
| 484 | 487 | */ |
|---|
| 485 | 488 | if ((lmb->flags & DRCONF_MEM_RESERVED) || |
|---|
| 486 | 489 | !(lmb->flags & DRCONF_MEM_ASSIGNED)) |
|---|
| 487 | | - return; |
|---|
| 490 | + return 0; |
|---|
| 488 | 491 | |
|---|
| 489 | 492 | if (*usm) |
|---|
| 490 | 493 | is_kexec_kdump = 1; |
|---|
| .. | .. |
|---|
| 499 | 502 | */ |
|---|
| 500 | 503 | rngs = dt_mem_next_cell(dt_root_size_cells, usm); |
|---|
| 501 | 504 | if (!rngs) /* there are no (base, size) duple */ |
|---|
| 502 | | - return; |
|---|
| 505 | + return 0; |
|---|
| 503 | 506 | } |
|---|
| 504 | 507 | |
|---|
| 505 | 508 | do { |
|---|
| .. | .. |
|---|
| 515 | 518 | size = 0x80000000ul - base; |
|---|
| 516 | 519 | } |
|---|
| 517 | 520 | |
|---|
| 521 | + if (!validate_mem_limit(base, &size)) |
|---|
| 522 | + continue; |
|---|
| 523 | + |
|---|
| 518 | 524 | DBG("Adding: %llx -> %llx\n", base, size); |
|---|
| 519 | | - if (validate_mem_limit(base, &size)) |
|---|
| 520 | | - memblock_add(base, size); |
|---|
| 525 | + memblock_add(base, size); |
|---|
| 526 | + |
|---|
| 527 | + if (lmb->flags & DRCONF_MEM_HOTREMOVABLE) |
|---|
| 528 | + memblock_mark_hotplug(base, size); |
|---|
| 521 | 529 | } while (--rngs); |
|---|
| 530 | + |
|---|
| 531 | + return 0; |
|---|
| 522 | 532 | } |
|---|
| 523 | 533 | #endif /* CONFIG_PPC_PSERIES */ |
|---|
| 524 | 534 | |
|---|
| .. | .. |
|---|
| 529 | 539 | #ifdef CONFIG_PPC_PSERIES |
|---|
| 530 | 540 | if (depth == 1 && |
|---|
| 531 | 541 | strcmp(uname, "ibm,dynamic-reconfiguration-memory") == 0) { |
|---|
| 532 | | - walk_drmem_lmbs_early(node, early_init_drmem_lmb); |
|---|
| 542 | + walk_drmem_lmbs_early(node, NULL, early_init_drmem_lmb); |
|---|
| 533 | 543 | return 0; |
|---|
| 534 | 544 | } |
|---|
| 535 | 545 | #endif |
|---|
| .. | .. |
|---|
| 623 | 633 | #ifdef CONFIG_BLK_DEV_INITRD |
|---|
| 624 | 634 | /* Then reserve the initrd, if any */ |
|---|
| 625 | 635 | if (initrd_start && (initrd_end > initrd_start)) { |
|---|
| 626 | | - memblock_reserve(_ALIGN_DOWN(__pa(initrd_start), PAGE_SIZE), |
|---|
| 627 | | - _ALIGN_UP(initrd_end, PAGE_SIZE) - |
|---|
| 628 | | - _ALIGN_DOWN(initrd_start, PAGE_SIZE)); |
|---|
| 636 | + memblock_reserve(ALIGN_DOWN(__pa(initrd_start), PAGE_SIZE), |
|---|
| 637 | + ALIGN(initrd_end, PAGE_SIZE) - |
|---|
| 638 | + ALIGN_DOWN(initrd_start, PAGE_SIZE)); |
|---|
| 629 | 639 | } |
|---|
| 630 | 640 | #endif /* CONFIG_BLK_DEV_INITRD */ |
|---|
| 631 | 641 | |
|---|
| .. | .. |
|---|
| 720 | 730 | #ifdef CONFIG_PPC_POWERNV |
|---|
| 721 | 731 | /* Some machines might need OPAL info for debugging, grab it now. */ |
|---|
| 722 | 732 | of_scan_flat_dt(early_init_dt_scan_opal, NULL); |
|---|
| 733 | + |
|---|
| 734 | + /* Scan tree for ultravisor feature */ |
|---|
| 735 | + of_scan_flat_dt(early_init_dt_scan_ultravisor, NULL); |
|---|
| 723 | 736 | #endif |
|---|
| 724 | 737 | |
|---|
| 725 | | -#ifdef CONFIG_FA_DUMP |
|---|
| 738 | +#if defined(CONFIG_FA_DUMP) || defined(CONFIG_PRESERVE_FA_DUMP) |
|---|
| 726 | 739 | /* scan tree to see if dump is active during last boot */ |
|---|
| 727 | 740 | of_scan_flat_dt(early_init_dt_scan_fw_dump, NULL); |
|---|
| 728 | 741 | #endif |
|---|
| .. | .. |
|---|
| 737 | 750 | of_scan_flat_dt(early_init_dt_scan_root, NULL); |
|---|
| 738 | 751 | of_scan_flat_dt(early_init_dt_scan_memory_ppc, NULL); |
|---|
| 739 | 752 | |
|---|
| 753 | + /* |
|---|
| 754 | + * As generic code authors expect to be able to use static keys |
|---|
| 755 | + * in early_param() handlers, we initialize the static keys just |
|---|
| 756 | + * before parsing early params (it's fine to call jump_label_init() |
|---|
| 757 | + * more than once). |
|---|
| 758 | + */ |
|---|
| 759 | + jump_label_init(); |
|---|
| 740 | 760 | parse_early_param(); |
|---|
| 741 | 761 | |
|---|
| 742 | 762 | /* make sure we've parsed cmdline for mem= before this */ |
|---|
| .. | .. |
|---|
| 749 | 769 | if (PHYSICAL_START > MEMORY_START) |
|---|
| 750 | 770 | memblock_reserve(MEMORY_START, 0x8000); |
|---|
| 751 | 771 | reserve_kdump_trampoline(); |
|---|
| 752 | | -#ifdef CONFIG_FA_DUMP |
|---|
| 772 | +#if defined(CONFIG_FA_DUMP) || defined(CONFIG_PRESERVE_FA_DUMP) |
|---|
| 753 | 773 | /* |
|---|
| 754 | 774 | * If we fail to reserve memory for firmware-assisted dump then |
|---|
| 755 | 775 | * fallback to kexec based kdump. |
|---|
| .. | .. |
|---|
| 762 | 782 | /* Ensure that total memory size is page-aligned. */ |
|---|
| 763 | 783 | limit = ALIGN(memory_limit ?: memblock_phys_mem_size(), PAGE_SIZE); |
|---|
| 764 | 784 | memblock_enforce_memory_limit(limit); |
|---|
| 785 | + |
|---|
| 786 | +#if defined(CONFIG_PPC_BOOK3S_64) && defined(CONFIG_PPC_4K_PAGES) |
|---|
| 787 | + if (!early_radix_enabled()) |
|---|
| 788 | + memblock_cap_memory_range(0, 1UL << (H_MAX_PHYSMEM_BITS)); |
|---|
| 789 | +#endif |
|---|
| 765 | 790 | |
|---|
| 766 | 791 | memblock_allow_resize(); |
|---|
| 767 | 792 | memblock_dump_all(); |
|---|
| .. | .. |
|---|
| 807 | 832 | /* Now try to figure out if we are running on LPAR and so on */ |
|---|
| 808 | 833 | pseries_probe_fw_features(); |
|---|
| 809 | 834 | |
|---|
| 835 | + /* |
|---|
| 836 | + * Initialize pkey features and default AMR/IAMR values |
|---|
| 837 | + */ |
|---|
| 838 | + pkey_early_init_devtree(); |
|---|
| 839 | + |
|---|
| 810 | 840 | #ifdef CONFIG_PPC_PS3 |
|---|
| 811 | 841 | /* Identify PS3 firmware */ |
|---|
| 812 | 842 | if (of_flat_dt_is_compatible(of_get_flat_dt_root(), "sony,ps3")) |
|---|