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