~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/powerpc/mm/nohash/kaslr_booke.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-only
  2 //
  3 // Copyright (C) 2019 Jason Yan <yanaijie@huawei.com>
  4 
  5 #include <linux/kernel.h>
  6 #include <linux/errno.h>
  7 #include <linux/string.h>
  8 #include <linux/types.h>
  9 #include <linux/mm.h>
 10 #include <linux/swap.h>
 11 #include <linux/stddef.h>
 12 #include <linux/init.h>
 13 #include <linux/delay.h>
 14 #include <linux/memblock.h>
 15 #include <linux/libfdt.h>
 16 #include <linux/crash_reserve.h>
 17 #include <linux/of.h>
 18 #include <linux/of_fdt.h>
 19 #include <asm/cacheflush.h>
 20 #include <asm/kdump.h>
 21 #include <mm/mmu_decl.h>
 22 
 23 struct regions {
 24         unsigned long pa_start;
 25         unsigned long pa_end;
 26         unsigned long kernel_size;
 27         unsigned long dtb_start;
 28         unsigned long dtb_end;
 29         unsigned long initrd_start;
 30         unsigned long initrd_end;
 31         unsigned long crash_start;
 32         unsigned long crash_end;
 33         int reserved_mem;
 34         int reserved_mem_addr_cells;
 35         int reserved_mem_size_cells;
 36 };
 37 
 38 struct regions __initdata regions;
 39 
 40 static __init void kaslr_get_cmdline(void *fdt)
 41 {
 42         early_init_dt_scan_chosen(boot_command_line);
 43 }
 44 
 45 static unsigned long __init rotate_xor(unsigned long hash, const void *area,
 46                                        size_t size)
 47 {
 48         size_t i;
 49         const unsigned long *ptr = area;
 50 
 51         for (i = 0; i < size / sizeof(hash); i++) {
 52                 /* Rotate by odd number of bits and XOR. */
 53                 hash = (hash << ((sizeof(hash) * 8) - 7)) | (hash >> 7);
 54                 hash ^= ptr[i];
 55         }
 56 
 57         return hash;
 58 }
 59 
 60 /* Attempt to create a simple starting entropy. This can make it defferent for
 61  * every build but it is still not enough. Stronger entropy should
 62  * be added to make it change for every boot.
 63  */
 64 static unsigned long __init get_boot_seed(void *fdt)
 65 {
 66         unsigned long hash = 0;
 67 
 68         /* build-specific string for starting entropy. */
 69         hash = rotate_xor(hash, linux_banner, strlen(linux_banner));
 70         hash = rotate_xor(hash, fdt, fdt_totalsize(fdt));
 71 
 72         return hash;
 73 }
 74 
 75 static __init u64 get_kaslr_seed(void *fdt)
 76 {
 77         int node, len;
 78         fdt64_t *prop;
 79         u64 ret;
 80 
 81         node = fdt_path_offset(fdt, "/chosen");
 82         if (node < 0)
 83                 return 0;
 84 
 85         prop = fdt_getprop_w(fdt, node, "kaslr-seed", &len);
 86         if (!prop || len != sizeof(u64))
 87                 return 0;
 88 
 89         ret = fdt64_to_cpu(*prop);
 90         *prop = 0;
 91         return ret;
 92 }
 93 
 94 static __init bool regions_overlap(u32 s1, u32 e1, u32 s2, u32 e2)
 95 {
 96         return e1 >= s2 && e2 >= s1;
 97 }
 98 
 99 static __init bool overlaps_reserved_region(const void *fdt, u32 start,
100                                             u32 end)
101 {
102         int subnode, len, i;
103         u64 base, size;
104 
105         /* check for overlap with /memreserve/ entries */
106         for (i = 0; i < fdt_num_mem_rsv(fdt); i++) {
107                 if (fdt_get_mem_rsv(fdt, i, &base, &size) < 0)
108                         continue;
109                 if (regions_overlap(start, end, base, base + size))
110                         return true;
111         }
112 
113         if (regions.reserved_mem < 0)
114                 return false;
115 
116         /* check for overlap with static reservations in /reserved-memory */
117         for (subnode = fdt_first_subnode(fdt, regions.reserved_mem);
118              subnode >= 0;
119              subnode = fdt_next_subnode(fdt, subnode)) {
120                 const fdt32_t *reg;
121                 u64 rsv_end;
122 
123                 len = 0;
124                 reg = fdt_getprop(fdt, subnode, "reg", &len);
125                 while (len >= (regions.reserved_mem_addr_cells +
126                                regions.reserved_mem_size_cells)) {
127                         base = fdt32_to_cpu(reg[0]);
128                         if (regions.reserved_mem_addr_cells == 2)
129                                 base = (base << 32) | fdt32_to_cpu(reg[1]);
130 
131                         reg += regions.reserved_mem_addr_cells;
132                         len -= 4 * regions.reserved_mem_addr_cells;
133 
134                         size = fdt32_to_cpu(reg[0]);
135                         if (regions.reserved_mem_size_cells == 2)
136                                 size = (size << 32) | fdt32_to_cpu(reg[1]);
137 
138                         reg += regions.reserved_mem_size_cells;
139                         len -= 4 * regions.reserved_mem_size_cells;
140 
141                         if (base >= regions.pa_end)
142                                 continue;
143 
144                         rsv_end = min(base + size, (u64)U32_MAX);
145 
146                         if (regions_overlap(start, end, base, rsv_end))
147                                 return true;
148                 }
149         }
150         return false;
151 }
152 
153 static __init bool overlaps_region(const void *fdt, u32 start,
154                                    u32 end)
155 {
156         if (regions_overlap(start, end, __pa(_stext), __pa(_end)))
157                 return true;
158 
159         if (regions_overlap(start, end, regions.dtb_start,
160                             regions.dtb_end))
161                 return true;
162 
163         if (regions_overlap(start, end, regions.initrd_start,
164                             regions.initrd_end))
165                 return true;
166 
167         if (regions_overlap(start, end, regions.crash_start,
168                             regions.crash_end))
169                 return true;
170 
171         return overlaps_reserved_region(fdt, start, end);
172 }
173 
174 static void __init get_crash_kernel(void *fdt, unsigned long size)
175 {
176 #ifdef CONFIG_CRASH_RESERVE
177         unsigned long long crash_size, crash_base;
178         int ret;
179 
180         ret = parse_crashkernel(boot_command_line, size, &crash_size,
181                                 &crash_base, NULL, NULL);
182         if (ret != 0 || crash_size == 0)
183                 return;
184         if (crash_base == 0)
185                 crash_base = KDUMP_KERNELBASE;
186 
187         regions.crash_start = (unsigned long)crash_base;
188         regions.crash_end = (unsigned long)(crash_base + crash_size);
189 
190         pr_debug("crash_base=0x%llx crash_size=0x%llx\n", crash_base, crash_size);
191 #endif
192 }
193 
194 static void __init get_initrd_range(void *fdt)
195 {
196         u64 start, end;
197         int node, len;
198         const __be32 *prop;
199 
200         node = fdt_path_offset(fdt, "/chosen");
201         if (node < 0)
202                 return;
203 
204         prop = fdt_getprop(fdt, node, "linux,initrd-start", &len);
205         if (!prop)
206                 return;
207         start = of_read_number(prop, len / 4);
208 
209         prop = fdt_getprop(fdt, node, "linux,initrd-end", &len);
210         if (!prop)
211                 return;
212         end = of_read_number(prop, len / 4);
213 
214         regions.initrd_start = (unsigned long)start;
215         regions.initrd_end = (unsigned long)end;
216 
217         pr_debug("initrd_start=0x%llx  initrd_end=0x%llx\n", start, end);
218 }
219 
220 static __init unsigned long get_usable_address(const void *fdt,
221                                                unsigned long start,
222                                                unsigned long offset)
223 {
224         unsigned long pa;
225         unsigned long pa_end;
226 
227         for (pa = offset; (long)pa > (long)start; pa -= SZ_16K) {
228                 pa_end = pa + regions.kernel_size;
229                 if (overlaps_region(fdt, pa, pa_end))
230                         continue;
231 
232                 return pa;
233         }
234         return 0;
235 }
236 
237 static __init void get_cell_sizes(const void *fdt, int node, int *addr_cells,
238                                   int *size_cells)
239 {
240         const int *prop;
241         int len;
242 
243         /*
244          * Retrieve the #address-cells and #size-cells properties
245          * from the 'node', or use the default if not provided.
246          */
247         *addr_cells = *size_cells = 1;
248 
249         prop = fdt_getprop(fdt, node, "#address-cells", &len);
250         if (len == 4)
251                 *addr_cells = fdt32_to_cpu(*prop);
252         prop = fdt_getprop(fdt, node, "#size-cells", &len);
253         if (len == 4)
254                 *size_cells = fdt32_to_cpu(*prop);
255 }
256 
257 static unsigned long __init kaslr_legal_offset(void *dt_ptr, unsigned long index,
258                                                unsigned long offset)
259 {
260         unsigned long koffset = 0;
261         unsigned long start;
262 
263         while ((long)index >= 0) {
264                 offset = memstart_addr + index * SZ_64M + offset;
265                 start = memstart_addr + index * SZ_64M;
266                 koffset = get_usable_address(dt_ptr, start, offset);
267                 if (koffset)
268                         break;
269                 index--;
270         }
271 
272         if (koffset != 0)
273                 koffset -= memstart_addr;
274 
275         return koffset;
276 }
277 
278 static inline __init bool kaslr_disabled(void)
279 {
280         return strstr(boot_command_line, "nokaslr") != NULL;
281 }
282 
283 static unsigned long __init kaslr_choose_location(void *dt_ptr, phys_addr_t size,
284                                                   unsigned long kernel_sz)
285 {
286         unsigned long offset, random;
287         unsigned long ram, linear_sz;
288         u64 seed;
289         unsigned long index;
290 
291         kaslr_get_cmdline(dt_ptr);
292         if (kaslr_disabled())
293                 return 0;
294 
295         random = get_boot_seed(dt_ptr);
296 
297         seed = get_tb() << 32;
298         seed ^= get_tb();
299         random = rotate_xor(random, &seed, sizeof(seed));
300 
301         /*
302          * Retrieve (and wipe) the seed from the FDT
303          */
304         seed = get_kaslr_seed(dt_ptr);
305         if (seed)
306                 random = rotate_xor(random, &seed, sizeof(seed));
307         else
308                 pr_warn("KASLR: No safe seed for randomizing the kernel base.\n");
309 
310         ram = min_t(phys_addr_t, __max_low_memory, size);
311         ram = map_mem_in_cams(ram, CONFIG_LOWMEM_CAM_NUM, true, true);
312         linear_sz = min_t(unsigned long, ram, SZ_512M);
313 
314         /* If the linear size is smaller than 64M, do not randomize */
315         if (linear_sz < SZ_64M)
316                 return 0;
317 
318         /* check for a reserved-memory node and record its cell sizes */
319         regions.reserved_mem = fdt_path_offset(dt_ptr, "/reserved-memory");
320         if (regions.reserved_mem >= 0)
321                 get_cell_sizes(dt_ptr, regions.reserved_mem,
322                                &regions.reserved_mem_addr_cells,
323                                &regions.reserved_mem_size_cells);
324 
325         regions.pa_start = memstart_addr;
326         regions.pa_end = memstart_addr + linear_sz;
327         regions.dtb_start = __pa(dt_ptr);
328         regions.dtb_end = __pa(dt_ptr) + fdt_totalsize(dt_ptr);
329         regions.kernel_size = kernel_sz;
330 
331         get_initrd_range(dt_ptr);
332         get_crash_kernel(dt_ptr, ram);
333 
334         /*
335          * Decide which 64M we want to start
336          * Only use the low 8 bits of the random seed
337          */
338         index = random & 0xFF;
339         index %= linear_sz / SZ_64M;
340 
341         /* Decide offset inside 64M */
342         offset = random % (SZ_64M - kernel_sz);
343         offset = round_down(offset, SZ_16K);
344 
345         return kaslr_legal_offset(dt_ptr, index, offset);
346 }
347 
348 /*
349  * To see if we need to relocate the kernel to a random offset
350  * void *dt_ptr - address of the device tree
351  * phys_addr_t size - size of the first memory block
352  */
353 notrace void __init kaslr_early_init(void *dt_ptr, phys_addr_t size)
354 {
355         unsigned long tlb_virt;
356         phys_addr_t tlb_phys;
357         unsigned long offset;
358         unsigned long kernel_sz;
359 
360         kernel_sz = (unsigned long)_end - (unsigned long)_stext;
361 
362         offset = kaslr_choose_location(dt_ptr, size, kernel_sz);
363         if (offset == 0)
364                 return;
365 
366         kernstart_virt_addr += offset;
367         kernstart_addr += offset;
368 
369         is_second_reloc = 1;
370 
371         if (offset >= SZ_64M) {
372                 tlb_virt = round_down(kernstart_virt_addr, SZ_64M);
373                 tlb_phys = round_down(kernstart_addr, SZ_64M);
374 
375                 /* Create kernel map to relocate in */
376                 create_kaslr_tlb_entry(1, tlb_virt, tlb_phys);
377         }
378 
379         /* Copy the kernel to its new location and run */
380         memcpy((void *)kernstart_virt_addr, (void *)_stext, kernel_sz);
381         flush_icache_range(kernstart_virt_addr, kernstart_virt_addr + kernel_sz);
382 
383         reloc_kernel_entry(dt_ptr, kernstart_virt_addr);
384 }
385 
386 void __init kaslr_late_init(void)
387 {
388         /* If randomized, clear the original kernel */
389         if (kernstart_virt_addr != KERNELBASE) {
390                 unsigned long kernel_sz;
391 
392                 kernel_sz = (unsigned long)_end - kernstart_virt_addr;
393                 memzero_explicit((void *)KERNELBASE, kernel_sz);
394         }
395 }
396 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php