1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com) |
4 | */ |
5 | |
6 | #include <linux/kernel.h> |
7 | #include <linux/mm.h> |
8 | #include <linux/memblock.h> |
9 | #ifdef CONFIG_BLK_DEV_INITRD |
10 | #include <linux/initrd.h> |
11 | #endif |
12 | #include <linux/of_fdt.h> |
13 | #include <linux/swap.h> |
14 | #include <linux/module.h> |
15 | #include <linux/highmem.h> |
16 | #include <asm/page.h> |
17 | #include <asm/sections.h> |
18 | #include <asm/setup.h> |
19 | #include <asm/arcregs.h> |
20 | |
21 | pgd_t swapper_pg_dir[PTRS_PER_PGD] __aligned(PAGE_SIZE); |
22 | char empty_zero_page[PAGE_SIZE] __aligned(PAGE_SIZE); |
23 | EXPORT_SYMBOL(empty_zero_page); |
24 | |
25 | static const unsigned long low_mem_start = CONFIG_LINUX_RAM_BASE; |
26 | static unsigned long low_mem_sz; |
27 | |
28 | #ifdef CONFIG_HIGHMEM |
29 | static unsigned long min_high_pfn, max_high_pfn; |
30 | static phys_addr_t high_mem_start; |
31 | static phys_addr_t high_mem_sz; |
32 | unsigned long arch_pfn_offset; |
33 | EXPORT_SYMBOL(arch_pfn_offset); |
34 | #endif |
35 | |
36 | long __init arc_get_mem_sz(void) |
37 | { |
38 | return low_mem_sz; |
39 | } |
40 | |
41 | /* User can over-ride above with "mem=nnn[KkMm]" in cmdline */ |
42 | static int __init setup_mem_sz(char *str) |
43 | { |
44 | low_mem_sz = memparse(ptr: str, NULL) & PAGE_MASK; |
45 | |
46 | /* early console might not be setup yet - it will show up later */ |
47 | pr_info("\"mem=%s\": mem sz set to %ldM\n" , str, TO_MB(low_mem_sz)); |
48 | |
49 | return 0; |
50 | } |
51 | early_param("mem" , setup_mem_sz); |
52 | |
53 | void __init early_init_dt_add_memory_arch(u64 base, u64 size) |
54 | { |
55 | int in_use = 0; |
56 | |
57 | if (!low_mem_sz) { |
58 | if (base != low_mem_start) |
59 | panic(fmt: "CONFIG_LINUX_RAM_BASE != DT memory { }" ); |
60 | |
61 | low_mem_sz = size; |
62 | in_use = 1; |
63 | memblock_add_node(base, size, nid: 0, flags: MEMBLOCK_NONE); |
64 | } else { |
65 | #ifdef CONFIG_HIGHMEM |
66 | high_mem_start = base; |
67 | high_mem_sz = size; |
68 | in_use = 1; |
69 | memblock_add_node(base, size, 1, MEMBLOCK_NONE); |
70 | memblock_reserve(base, size); |
71 | #endif |
72 | } |
73 | |
74 | pr_info("Memory @ %llx [%lldM] %s\n" , |
75 | base, TO_MB(size), !in_use ? "Not used" :"" ); |
76 | } |
77 | |
78 | /* |
79 | * First memory setup routine called from setup_arch() |
80 | * 1. setup swapper's mm @init_mm |
81 | * 2. Count the pages we have and setup bootmem allocator |
82 | * 3. zone setup |
83 | */ |
84 | void __init setup_arch_memory(void) |
85 | { |
86 | unsigned long max_zone_pfn[MAX_NR_ZONES] = { 0 }; |
87 | |
88 | setup_initial_init_mm(start_code: _text, end_code: _etext, end_data: _edata, brk: _end); |
89 | |
90 | /* first page of system - kernel .vector starts here */ |
91 | min_low_pfn = virt_to_pfn((void *)CONFIG_LINUX_RAM_BASE); |
92 | |
93 | /* Last usable page of low mem */ |
94 | max_low_pfn = max_pfn = PFN_DOWN(low_mem_start + low_mem_sz); |
95 | |
96 | /*------------- bootmem allocator setup -----------------------*/ |
97 | |
98 | /* |
99 | * seed the bootmem allocator after any DT memory node parsing or |
100 | * "mem=xxx" cmdline overrides have potentially updated @arc_mem_sz |
101 | * |
102 | * Only low mem is added, otherwise we have crashes when allocating |
103 | * mem_map[] itself. NO_BOOTMEM allocates mem_map[] at the end of |
104 | * avail memory, ending in highmem with a > 32-bit address. However |
105 | * it then tries to memset it with a truncaed 32-bit handle, causing |
106 | * the crash |
107 | */ |
108 | |
109 | memblock_reserve(base: CONFIG_LINUX_LINK_BASE, |
110 | __pa(_end) - CONFIG_LINUX_LINK_BASE); |
111 | |
112 | #ifdef CONFIG_BLK_DEV_INITRD |
113 | if (phys_initrd_size) { |
114 | memblock_reserve(base: phys_initrd_start, size: phys_initrd_size); |
115 | initrd_start = (unsigned long)__va(phys_initrd_start); |
116 | initrd_end = initrd_start + phys_initrd_size; |
117 | } |
118 | #endif |
119 | |
120 | early_init_fdt_reserve_self(); |
121 | early_init_fdt_scan_reserved_mem(); |
122 | |
123 | memblock_dump_all(); |
124 | |
125 | /*----------------- node/zones setup --------------------------*/ |
126 | max_zone_pfn[ZONE_NORMAL] = max_low_pfn; |
127 | |
128 | #ifdef CONFIG_HIGHMEM |
129 | /* |
130 | * On ARC (w/o PAE) HIGHMEM addresses are actually smaller (0 based) |
131 | * than addresses in normal aka low memory (0x8000_0000 based). |
132 | * Even with PAE, the huge peripheral space hole would waste a lot of |
133 | * mem with single contiguous mem_map[]. |
134 | * Thus when HIGHMEM on ARC is enabled the memory map corresponding |
135 | * to the hole is freed and ARC specific version of pfn_valid() |
136 | * handles the hole in the memory map. |
137 | */ |
138 | |
139 | min_high_pfn = PFN_DOWN(high_mem_start); |
140 | max_high_pfn = PFN_DOWN(high_mem_start + high_mem_sz); |
141 | |
142 | /* |
143 | * max_high_pfn should be ok here for both HIGHMEM and HIGHMEM+PAE. |
144 | * For HIGHMEM without PAE max_high_pfn should be less than |
145 | * min_low_pfn to guarantee that these two regions don't overlap. |
146 | * For PAE case highmem is greater than lowmem, so it is natural |
147 | * to use max_high_pfn. |
148 | * |
149 | * In both cases, holes should be handled by pfn_valid(). |
150 | */ |
151 | max_zone_pfn[ZONE_HIGHMEM] = max_high_pfn; |
152 | |
153 | high_memory = (void *)(min_high_pfn << PAGE_SHIFT); |
154 | |
155 | arch_pfn_offset = min(min_low_pfn, min_high_pfn); |
156 | kmap_init(); |
157 | |
158 | #else /* CONFIG_HIGHMEM */ |
159 | /* pfn_valid() uses this when FLATMEM=y and HIGHMEM=n */ |
160 | max_mapnr = max_low_pfn - min_low_pfn; |
161 | |
162 | #endif /* CONFIG_HIGHMEM */ |
163 | |
164 | free_area_init(max_zone_pfn); |
165 | } |
166 | |
167 | static void __init highmem_init(void) |
168 | { |
169 | #ifdef CONFIG_HIGHMEM |
170 | unsigned long tmp; |
171 | |
172 | memblock_phys_free(high_mem_start, high_mem_sz); |
173 | for (tmp = min_high_pfn; tmp < max_high_pfn; tmp++) |
174 | free_highmem_page(pfn_to_page(tmp)); |
175 | #endif |
176 | } |
177 | |
178 | /* |
179 | * mem_init - initializes memory |
180 | * |
181 | * Frees up bootmem |
182 | * Calculates and displays memory available/used |
183 | */ |
184 | void __init mem_init(void) |
185 | { |
186 | memblock_free_all(); |
187 | highmem_init(); |
188 | |
189 | BUILD_BUG_ON((PTRS_PER_PGD * sizeof(pgd_t)) > PAGE_SIZE); |
190 | BUILD_BUG_ON((PTRS_PER_PUD * sizeof(pud_t)) > PAGE_SIZE); |
191 | BUILD_BUG_ON((PTRS_PER_PMD * sizeof(pmd_t)) > PAGE_SIZE); |
192 | BUILD_BUG_ON((PTRS_PER_PTE * sizeof(pte_t)) > PAGE_SIZE); |
193 | } |
194 | |
195 | #ifdef CONFIG_HIGHMEM |
196 | int pfn_valid(unsigned long pfn) |
197 | { |
198 | return (pfn >= min_high_pfn && pfn <= max_high_pfn) || |
199 | (pfn >= min_low_pfn && pfn <= max_low_pfn); |
200 | } |
201 | EXPORT_SYMBOL(pfn_valid); |
202 | #endif |
203 | |