Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 | 2 | /* |
1da177e4 | 3 | * S390 version |
a53c8fab | 4 | * Copyright IBM Corp. 1999 |
1da177e4 LT |
5 | * Author(s): Hartmut Penner (hp@de.ibm.com) |
6 | * | |
7 | * Derived from "arch/i386/mm/init.c" | |
8 | * Copyright (C) 1995 Linus Torvalds | |
9 | */ | |
10 | ||
b49ee5b3 | 11 | #include <linux/cpufeature.h> |
1da177e4 LT |
12 | #include <linux/signal.h> |
13 | #include <linux/sched.h> | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/errno.h> | |
16 | #include <linux/string.h> | |
17 | #include <linux/types.h> | |
18 | #include <linux/ptrace.h> | |
19 | #include <linux/mman.h> | |
20 | #include <linux/mm.h> | |
21 | #include <linux/swap.h> | |
64e1f0c5 | 22 | #include <linux/swiotlb.h> |
1da177e4 LT |
23 | #include <linux/smp.h> |
24 | #include <linux/init.h> | |
25 | #include <linux/pagemap.h> | |
57c8a661 | 26 | #include <linux/memblock.h> |
e5d709bb | 27 | #include <linux/memory.h> |
d882b172 | 28 | #include <linux/pfn.h> |
028d9b3c | 29 | #include <linux/poison.h> |
2b67fc46 | 30 | #include <linux/initrd.h> |
3a4c5d59 | 31 | #include <linux/export.h> |
34ad7cdc | 32 | #include <linux/cma.h> |
5a0e3ad6 | 33 | #include <linux/gfp.h> |
9087c375 | 34 | #include <linux/dma-direct.h> |
254b2fd0 | 35 | #include <linux/percpu.h> |
1da177e4 | 36 | #include <asm/processor.h> |
7c0f6ba6 | 37 | #include <linux/uaccess.h> |
1da177e4 | 38 | #include <asm/pgalloc.h> |
ebe1cd53 | 39 | #include <asm/ctlreg.h> |
e41ba111 | 40 | #include <asm/kfence.h> |
1da177e4 | 41 | #include <asm/dma.h> |
4df29d2b | 42 | #include <asm/abs_lowcore.h> |
1da177e4 | 43 | #include <asm/tlbflush.h> |
d882b172 | 44 | #include <asm/sections.h> |
e5d709bb | 45 | #include <asm/sclp.h> |
e6c7c630 | 46 | #include <asm/set_memory.h> |
42db5ed8 | 47 | #include <asm/kasan.h> |
64e1f0c5 HP |
48 | #include <asm/dma-mapping.h> |
49 | #include <asm/uv.h> | |
a603002e | 50 | #include <linux/virtio_anchor.h> |
4ce1cf7b | 51 | #include <linux/virtio_config.h> |
0cc2dc49 | 52 | #include <linux/execmem.h> |
1da177e4 | 53 | |
33def849 | 54 | pgd_t swapper_pg_dir[PTRS_PER_PGD] __section(".bss..swapper_pg_dir"); |
bb1520d5 | 55 | pgd_t invalid_pg_dir[PTRS_PER_PGD] __section(".bss..invalid_pg_dir"); |
0290c9e3 | 56 | |
527618ab | 57 | struct ctlreg __bootdata_preserved(s390_invalid_asce); |
1485c5c8 | 58 | |
f8107a8b HC |
59 | unsigned long __bootdata_preserved(page_noexec_mask); |
60 | EXPORT_SYMBOL(page_noexec_mask); | |
61 | ||
62 | unsigned long __bootdata_preserved(segment_noexec_mask); | |
63 | EXPORT_SYMBOL(segment_noexec_mask); | |
64 | ||
65 | unsigned long __bootdata_preserved(region_noexec_mask); | |
66 | EXPORT_SYMBOL(region_noexec_mask); | |
67 | ||
238ec4ef | 68 | unsigned long empty_zero_page, zero_page_mask; |
1485c5c8 | 69 | EXPORT_SYMBOL(empty_zero_page); |
0b70068e | 70 | EXPORT_SYMBOL(zero_page_mask); |
1da177e4 | 71 | |
0999f119 | 72 | static void __init setup_zero_pages(void) |
238ec4ef | 73 | { |
cb088e38 | 74 | unsigned long total_pages = memblock_estimated_nr_free_pages(); |
238ec4ef | 75 | unsigned int order; |
238ec4ef | 76 | |
c7e8b2c2 MS |
77 | /* Latest machines require a mapping granularity of 512KB */ |
78 | order = 7; | |
79 | ||
7919e91b | 80 | /* Limit number of empty zero pages for small memory sizes */ |
37db17c1 | 81 | while (order > 2 && (total_pages >> 10) < (1UL << order)) |
1f6b83e5 | 82 | order--; |
238ec4ef | 83 | |
54ccf66f | 84 | empty_zero_page = (unsigned long)memblock_alloc_or_panic(PAGE_SIZE << order, PAGE_SIZE); |
238ec4ef | 85 | |
0999f119 | 86 | zero_page_mask = ((PAGE_SIZE << order) - 1) & PAGE_MASK; |
238ec4ef MS |
87 | } |
88 | ||
1da177e4 LT |
89 | /* |
90 | * paging_init() sets up the page tables | |
91 | */ | |
1da177e4 LT |
92 | void __init paging_init(void) |
93 | { | |
39b742f9 | 94 | unsigned long max_zone_pfns[MAX_NR_ZONES]; |
d882b172 | 95 | |
f4eb07c1 | 96 | vmem_map_init(); |
17f34580 | 97 | sparse_init(); |
ba0fb44a | 98 | zone_dma_limit = DMA_BIT_MASK(31); |
39b742f9 | 99 | memset(max_zone_pfns, 0, sizeof(max_zone_pfns)); |
08d90f46 | 100 | max_zone_pfns[ZONE_DMA] = virt_to_pfn(MAX_DMA_ADDRESS); |
39b742f9 | 101 | max_zone_pfns[ZONE_NORMAL] = max_low_pfn; |
9691a071 | 102 | free_area_init(max_zone_pfns); |
1da177e4 | 103 | } |
1da177e4 | 104 | |
91d37211 HC |
105 | void mark_rodata_ro(void) |
106 | { | |
d07a980c HC |
107 | unsigned long size = __end_ro_after_init - __start_ro_after_init; |
108 | ||
b49ee5b3 | 109 | if (cpu_has_nx()) |
33bd8d15 | 110 | system_ctl_set_bit(0, CR0_INSTRUCTION_EXEC_PROTECTION_BIT); |
a7eb2880 | 111 | __set_memory_ro(__start_ro_after_init, __end_ro_after_init); |
d07a980c | 112 | pr_info("Write protected read-only-after-init data: %luk\n", size >> 10); |
91d37211 HC |
113 | } |
114 | ||
58635d66 | 115 | int set_memory_encrypted(unsigned long vaddr, int numpages) |
64e1f0c5 HP |
116 | { |
117 | int i; | |
118 | ||
119 | /* make specified pages unshared, (swiotlb, dma_free) */ | |
120 | for (i = 0; i < numpages; ++i) { | |
58635d66 NB |
121 | uv_remove_shared(virt_to_phys((void *)vaddr)); |
122 | vaddr += PAGE_SIZE; | |
64e1f0c5 HP |
123 | } |
124 | return 0; | |
125 | } | |
126 | ||
58635d66 | 127 | int set_memory_decrypted(unsigned long vaddr, int numpages) |
64e1f0c5 HP |
128 | { |
129 | int i; | |
130 | /* make specified pages shared (swiotlb, dma_alloca) */ | |
131 | for (i = 0; i < numpages; ++i) { | |
58635d66 NB |
132 | uv_set_shared(virt_to_phys((void *)vaddr)); |
133 | vaddr += PAGE_SIZE; | |
64e1f0c5 HP |
134 | } |
135 | return 0; | |
136 | } | |
137 | ||
138 | /* are we a protected virtualization guest? */ | |
9087c375 TL |
139 | bool force_dma_unencrypted(struct device *dev) |
140 | { | |
5cbdaeef | 141 | return is_prot_virt_guest(); |
9087c375 TL |
142 | } |
143 | ||
64e1f0c5 HP |
144 | /* protected virtualization */ |
145 | static void pv_init(void) | |
146 | { | |
147 | if (!is_prot_virt_guest()) | |
148 | return; | |
149 | ||
a603002e | 150 | virtio_set_mem_acc_cb(virtio_require_restricted_mem_acc); |
3f9dfbeb | 151 | |
64e1f0c5 | 152 | /* make sure bounce buffers are shared */ |
c6af2aa9 | 153 | swiotlb_init(true, SWIOTLB_FORCE | SWIOTLB_VERBOSE); |
64e1f0c5 | 154 | swiotlb_update_mem_attributes(); |
64e1f0c5 HP |
155 | } |
156 | ||
0d98484e | 157 | void __init arch_mm_preinit(void) |
1da177e4 | 158 | { |
64f31d58 | 159 | cpumask_set_cpu(0, &init_mm.context.cpu_attach_mask); |
1b948d6c | 160 | cpumask_set_cpu(0, mm_cpumask(&init_mm)); |
1b948d6c | 161 | |
64e1f0c5 | 162 | pv_init(); |
45e576b1 | 163 | |
0999f119 | 164 | setup_zero_pages(); /* Setup zeroed pages. */ |
1da177e4 LT |
165 | } |
166 | ||
604ddad0 HC |
167 | unsigned long memory_block_size_bytes(void) |
168 | { | |
169 | /* | |
170 | * Make sure the memory block size is always greater | |
171 | * or equal than the memory increment size. | |
172 | */ | |
173 | return max_t(unsigned long, MIN_MEMORY_BLOCK_SIZE, sclp.rzm); | |
174 | } | |
175 | ||
254b2fd0 HC |
176 | unsigned long __per_cpu_offset[NR_CPUS] __read_mostly; |
177 | EXPORT_SYMBOL(__per_cpu_offset); | |
178 | ||
179 | static int __init pcpu_cpu_distance(unsigned int from, unsigned int to) | |
180 | { | |
181 | return LOCAL_DISTANCE; | |
182 | } | |
183 | ||
184 | static int __init pcpu_cpu_to_node(int cpu) | |
185 | { | |
186 | return 0; | |
187 | } | |
188 | ||
189 | void __init setup_per_cpu_areas(void) | |
190 | { | |
191 | unsigned long delta; | |
192 | unsigned int cpu; | |
193 | int rc; | |
194 | ||
195 | /* | |
196 | * Always reserve area for module percpu variables. That's | |
197 | * what the legacy allocator did. | |
198 | */ | |
199 | rc = pcpu_embed_first_chunk(PERCPU_MODULE_RESERVE, | |
200 | PERCPU_DYNAMIC_RESERVE, PAGE_SIZE, | |
201 | pcpu_cpu_distance, | |
202 | pcpu_cpu_to_node); | |
203 | if (rc < 0) | |
204 | panic("Failed to initialize percpu areas."); | |
205 | ||
206 | delta = (unsigned long)pcpu_base_addr - (unsigned long)__per_cpu_start; | |
207 | for_each_possible_cpu(cpu) | |
208 | __per_cpu_offset[cpu] = delta + pcpu_unit_offsets[cpu]; | |
209 | } | |
210 | ||
421c175c | 211 | #ifdef CONFIG_MEMORY_HOTPLUG |
34ad7cdc HC |
212 | |
213 | #ifdef CONFIG_CMA | |
214 | ||
215 | /* Prevent memory blocks which contain cma regions from going offline */ | |
216 | ||
217 | struct s390_cma_mem_data { | |
218 | unsigned long start; | |
219 | unsigned long end; | |
220 | }; | |
221 | ||
222 | static int s390_cma_check_range(struct cma *cma, void *data) | |
223 | { | |
224 | struct s390_cma_mem_data *mem_data; | |
34ad7cdc HC |
225 | |
226 | mem_data = data; | |
624ab90b FL |
227 | |
228 | if (cma_intersects(cma, mem_data->start, mem_data->end)) | |
229 | return -EBUSY; | |
230 | ||
231 | return 0; | |
34ad7cdc HC |
232 | } |
233 | ||
234 | static int s390_cma_mem_notifier(struct notifier_block *nb, | |
235 | unsigned long action, void *data) | |
236 | { | |
237 | struct s390_cma_mem_data mem_data; | |
238 | struct memory_notify *arg; | |
239 | int rc = 0; | |
240 | ||
241 | arg = data; | |
242 | mem_data.start = arg->start_pfn << PAGE_SHIFT; | |
243 | mem_data.end = mem_data.start + (arg->nr_pages << PAGE_SHIFT); | |
244 | if (action == MEM_GOING_OFFLINE) | |
245 | rc = cma_for_each_area(s390_cma_check_range, &mem_data); | |
246 | return notifier_from_errno(rc); | |
247 | } | |
248 | ||
249 | static struct notifier_block s390_cma_mem_nb = { | |
250 | .notifier_call = s390_cma_mem_notifier, | |
251 | }; | |
252 | ||
253 | static int __init s390_cma_mem_init(void) | |
254 | { | |
255 | return register_memory_notifier(&s390_cma_mem_nb); | |
256 | } | |
257 | device_initcall(s390_cma_mem_init); | |
258 | ||
259 | #endif /* CONFIG_CMA */ | |
260 | ||
940519f0 | 261 | int arch_add_memory(int nid, u64 start, u64 size, |
f5637d3b | 262 | struct mhp_params *params) |
421c175c | 263 | { |
892365ab GS |
264 | unsigned long start_pfn = PFN_DOWN(start); |
265 | unsigned long size_pages = PFN_DOWN(size); | |
f1dd2cd1 | 266 | int rc; |
421c175c | 267 | |
f8c425a9 | 268 | if (WARN_ON_ONCE(pgprot_val(params->pgprot) != pgprot_val(PAGE_KERNEL))) |
bfeb022f LG |
269 | return -EINVAL; |
270 | ||
7707248a | 271 | VM_BUG_ON(!mhp_range_allowed(start, size, true)); |
421c175c HC |
272 | rc = vmem_add_mapping(start, size); |
273 | if (rc) | |
274 | return rc; | |
199071f1 | 275 | |
f5637d3b | 276 | rc = __add_pages(nid, start_pfn, size_pages, params); |
421c175c HC |
277 | if (rc) |
278 | vmem_remove_mapping(start, size); | |
279 | return rc; | |
280 | } | |
24d335ca | 281 | |
65a2aa5f | 282 | void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap) |
24d335ca | 283 | { |
18c86506 DH |
284 | unsigned long start_pfn = start >> PAGE_SHIFT; |
285 | unsigned long nr_pages = size >> PAGE_SHIFT; | |
18c86506 | 286 | |
feee6b29 | 287 | __remove_pages(start_pfn, nr_pages, altmap); |
18c86506 | 288 | vmem_remove_mapping(start, size); |
24d335ca | 289 | } |
421c175c | 290 | #endif /* CONFIG_MEMORY_HOTPLUG */ |
0cc2dc49 MRI |
291 | |
292 | #ifdef CONFIG_EXECMEM | |
293 | static struct execmem_info execmem_info __ro_after_init; | |
294 | ||
295 | struct execmem_info __init *execmem_arch_setup(void) | |
296 | { | |
297 | unsigned long module_load_offset = 0; | |
298 | unsigned long start; | |
299 | ||
300 | if (kaslr_enabled()) | |
301 | module_load_offset = get_random_u32_inclusive(1, 1024) * PAGE_SIZE; | |
302 | ||
303 | start = MODULES_VADDR + module_load_offset; | |
304 | ||
305 | execmem_info = (struct execmem_info){ | |
306 | .ranges = { | |
307 | [EXECMEM_DEFAULT] = { | |
308 | .flags = EXECMEM_KASAN_SHADOW, | |
309 | .start = start, | |
310 | .end = MODULES_END, | |
311 | .pgprot = PAGE_KERNEL, | |
312 | .alignment = MODULE_ALIGN, | |
313 | }, | |
314 | }, | |
315 | }; | |
316 | ||
317 | return &execmem_info; | |
318 | } | |
319 | #endif /* CONFIG_EXECMEM */ |