1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2012 Regents of the University of California 4 */ 5 6 #include <linux/init.h> 7 #include <linux/mm.h> 8 #include <linux/memblock.h> 9 #include <linux/initrd.h> 10 #include <linux/swap.h> 11 #include <linux/sizes.h> 12 #include <linux/of_fdt.h> 13 14 #include <asm/fixmap.h> 15 #include <asm/tlbflush.h> 16 #include <asm/sections.h> 17 #include <asm/pgtable.h> 18 #include <asm/io.h> 19 20 unsigned long empty_zero_page[PAGE_SIZE / sizeof(unsigned long)] 21 __page_aligned_bss; 22 EXPORT_SYMBOL(empty_zero_page); 23 24 static void __init zone_sizes_init(void) 25 { 26 unsigned long max_zone_pfns[MAX_NR_ZONES] = { 0, }; 27 28 #ifdef CONFIG_ZONE_DMA32 29 max_zone_pfns[ZONE_DMA32] = PFN_DOWN(min(4UL * SZ_1G, 30 (unsigned long) PFN_PHYS(max_low_pfn))); 31 #endif 32 max_zone_pfns[ZONE_NORMAL] = max_low_pfn; 33 34 free_area_init_nodes(max_zone_pfns); 35 } 36 37 void setup_zero_page(void) 38 { 39 memset((void *)empty_zero_page, 0, PAGE_SIZE); 40 } 41 42 void __init paging_init(void) 43 { 44 setup_zero_page(); 45 local_flush_tlb_all(); 46 zone_sizes_init(); 47 } 48 49 void __init mem_init(void) 50 { 51 #ifdef CONFIG_FLATMEM 52 BUG_ON(!mem_map); 53 #endif /* CONFIG_FLATMEM */ 54 55 high_memory = (void *)(__va(PFN_PHYS(max_low_pfn))); 56 memblock_free_all(); 57 58 mem_init_print_info(NULL); 59 } 60 61 #ifdef CONFIG_BLK_DEV_INITRD 62 static void __init setup_initrd(void) 63 { 64 unsigned long size; 65 66 if (initrd_start >= initrd_end) { 67 pr_info("initrd not found or empty"); 68 goto disable; 69 } 70 if (__pa(initrd_end) > PFN_PHYS(max_low_pfn)) { 71 pr_err("initrd extends beyond end of memory"); 72 goto disable; 73 } 74 75 size = initrd_end - initrd_start; 76 memblock_reserve(__pa(initrd_start), size); 77 initrd_below_start_ok = 1; 78 79 pr_info("Initial ramdisk at: 0x%p (%lu bytes)\n", 80 (void *)(initrd_start), size); 81 return; 82 disable: 83 pr_cont(" - disabling initrd\n"); 84 initrd_start = 0; 85 initrd_end = 0; 86 } 87 88 void __init free_initrd_mem(unsigned long start, unsigned long end) 89 { 90 free_reserved_area((void *)start, (void *)end, -1, "initrd"); 91 } 92 #endif /* CONFIG_BLK_DEV_INITRD */ 93 94 void __init setup_bootmem(void) 95 { 96 struct memblock_region *reg; 97 phys_addr_t mem_size = 0; 98 99 /* Find the memory region containing the kernel */ 100 for_each_memblock(memory, reg) { 101 phys_addr_t vmlinux_end = __pa(_end); 102 phys_addr_t end = reg->base + reg->size; 103 104 if (reg->base <= vmlinux_end && vmlinux_end <= end) { 105 /* 106 * Reserve from the start of the region to the end of 107 * the kernel 108 */ 109 memblock_reserve(reg->base, vmlinux_end - reg->base); 110 mem_size = min(reg->size, (phys_addr_t)-PAGE_OFFSET); 111 112 /* 113 * Remove memblock from the end of usable area to the 114 * end of region 115 */ 116 if (reg->base + mem_size < end) 117 memblock_remove(reg->base + mem_size, 118 end - reg->base - mem_size); 119 } 120 } 121 BUG_ON(mem_size == 0); 122 123 set_max_mapnr(PFN_DOWN(mem_size)); 124 max_low_pfn = PFN_DOWN(memblock_end_of_DRAM()); 125 126 #ifdef CONFIG_BLK_DEV_INITRD 127 setup_initrd(); 128 #endif /* CONFIG_BLK_DEV_INITRD */ 129 130 early_init_fdt_reserve_self(); 131 early_init_fdt_scan_reserved_mem(); 132 memblock_allow_resize(); 133 memblock_dump_all(); 134 135 for_each_memblock(memory, reg) { 136 unsigned long start_pfn = memblock_region_memory_base_pfn(reg); 137 unsigned long end_pfn = memblock_region_memory_end_pfn(reg); 138 139 memblock_set_node(PFN_PHYS(start_pfn), 140 PFN_PHYS(end_pfn - start_pfn), 141 &memblock.memory, 0); 142 } 143 } 144 145 unsigned long va_pa_offset; 146 EXPORT_SYMBOL(va_pa_offset); 147 unsigned long pfn_base; 148 EXPORT_SYMBOL(pfn_base); 149 150 pgd_t swapper_pg_dir[PTRS_PER_PGD] __page_aligned_bss; 151 pgd_t trampoline_pg_dir[PTRS_PER_PGD] __initdata __aligned(PAGE_SIZE); 152 153 #ifndef __PAGETABLE_PMD_FOLDED 154 #define NUM_SWAPPER_PMDS ((uintptr_t)-PAGE_OFFSET >> PGDIR_SHIFT) 155 pmd_t swapper_pmd[PTRS_PER_PMD*((-PAGE_OFFSET)/PGDIR_SIZE)] __page_aligned_bss; 156 pmd_t trampoline_pmd[PTRS_PER_PGD] __initdata __aligned(PAGE_SIZE); 157 pmd_t fixmap_pmd[PTRS_PER_PMD] __page_aligned_bss; 158 #endif 159 160 pte_t fixmap_pte[PTRS_PER_PTE] __page_aligned_bss; 161 162 void __set_fixmap(enum fixed_addresses idx, phys_addr_t phys, pgprot_t prot) 163 { 164 unsigned long addr = __fix_to_virt(idx); 165 pte_t *ptep; 166 167 BUG_ON(idx <= FIX_HOLE || idx >= __end_of_fixed_addresses); 168 169 ptep = &fixmap_pte[pte_index(addr)]; 170 171 if (pgprot_val(prot)) { 172 set_pte(ptep, pfn_pte(phys >> PAGE_SHIFT, prot)); 173 } else { 174 pte_clear(&init_mm, addr, ptep); 175 local_flush_tlb_page(addr); 176 } 177 } 178 179 /* 180 * setup_vm() is called from head.S with MMU-off. 181 * 182 * Following requirements should be honoured for setup_vm() to work 183 * correctly: 184 * 1) It should use PC-relative addressing for accessing kernel symbols. 185 * To achieve this we always use GCC cmodel=medany. 186 * 2) The compiler instrumentation for FTRACE will not work for setup_vm() 187 * so disable compiler instrumentation when FTRACE is enabled. 188 * 189 * Currently, the above requirements are honoured by using custom CFLAGS 190 * for init.o in mm/Makefile. 191 */ 192 193 #ifndef __riscv_cmodel_medany 194 #error "setup_vm() is called from head.S before relocate so it should " 195 "not use absolute addressing." 196 #endif 197 198 asmlinkage void __init setup_vm(void) 199 { 200 extern char _start; 201 uintptr_t i; 202 uintptr_t pa = (uintptr_t) &_start; 203 pgprot_t prot = __pgprot(pgprot_val(PAGE_KERNEL) | _PAGE_EXEC); 204 205 va_pa_offset = PAGE_OFFSET - pa; 206 pfn_base = PFN_DOWN(pa); 207 208 /* Sanity check alignment and size */ 209 BUG_ON((PAGE_OFFSET % PGDIR_SIZE) != 0); 210 BUG_ON((pa % (PAGE_SIZE * PTRS_PER_PTE)) != 0); 211 212 #ifndef __PAGETABLE_PMD_FOLDED 213 trampoline_pg_dir[(PAGE_OFFSET >> PGDIR_SHIFT) % PTRS_PER_PGD] = 214 pfn_pgd(PFN_DOWN((uintptr_t)trampoline_pmd), 215 __pgprot(_PAGE_TABLE)); 216 trampoline_pmd[0] = pfn_pmd(PFN_DOWN(pa), prot); 217 218 for (i = 0; i < (-PAGE_OFFSET)/PGDIR_SIZE; ++i) { 219 size_t o = (PAGE_OFFSET >> PGDIR_SHIFT) % PTRS_PER_PGD + i; 220 221 swapper_pg_dir[o] = 222 pfn_pgd(PFN_DOWN((uintptr_t)swapper_pmd) + i, 223 __pgprot(_PAGE_TABLE)); 224 } 225 for (i = 0; i < ARRAY_SIZE(swapper_pmd); i++) 226 swapper_pmd[i] = pfn_pmd(PFN_DOWN(pa + i * PMD_SIZE), prot); 227 228 swapper_pg_dir[(FIXADDR_START >> PGDIR_SHIFT) % PTRS_PER_PGD] = 229 pfn_pgd(PFN_DOWN((uintptr_t)fixmap_pmd), 230 __pgprot(_PAGE_TABLE)); 231 fixmap_pmd[(FIXADDR_START >> PMD_SHIFT) % PTRS_PER_PMD] = 232 pfn_pmd(PFN_DOWN((uintptr_t)fixmap_pte), 233 __pgprot(_PAGE_TABLE)); 234 #else 235 trampoline_pg_dir[(PAGE_OFFSET >> PGDIR_SHIFT) % PTRS_PER_PGD] = 236 pfn_pgd(PFN_DOWN(pa), prot); 237 238 for (i = 0; i < (-PAGE_OFFSET)/PGDIR_SIZE; ++i) { 239 size_t o = (PAGE_OFFSET >> PGDIR_SHIFT) % PTRS_PER_PGD + i; 240 241 swapper_pg_dir[o] = 242 pfn_pgd(PFN_DOWN(pa + i * PGDIR_SIZE), prot); 243 } 244 245 swapper_pg_dir[(FIXADDR_START >> PGDIR_SHIFT) % PTRS_PER_PGD] = 246 pfn_pgd(PFN_DOWN((uintptr_t)fixmap_pte), 247 __pgprot(_PAGE_TABLE)); 248 #endif 249 } 250