xref: /linux/arch/arm64/kernel/pi/kaslr_early.c (revision 20dfee95936413708701eb151f419597fdd9d948)
1 // SPDX-License-Identifier: GPL-2.0-only
2 // Copyright 2022 Google LLC
3 // Author: Ard Biesheuvel <ardb@google.com>
4 
5 // NOTE: code in this file runs *very* early, and is not permitted to use
6 // global variables or anything that relies on absolute addressing.
7 
8 #include <linux/libfdt.h>
9 #include <linux/init.h>
10 #include <linux/linkage.h>
11 #include <linux/types.h>
12 #include <linux/sizes.h>
13 #include <linux/string.h>
14 
15 #include <asm/archrandom.h>
16 #include <asm/memory.h>
17 #include <asm/pgtable.h>
18 
19 #include "pi.h"
20 
21 extern u16 memstart_offset_seed;
22 
23 static u64 __init get_kaslr_seed(void *fdt, int node)
24 {
25 	static char const seed_str[] __initconst = "kaslr-seed";
26 	fdt64_t *prop;
27 	u64 ret;
28 	int len;
29 
30 	if (node < 0)
31 		return 0;
32 
33 	prop = fdt_getprop_w(fdt, node, seed_str, &len);
34 	if (!prop || len != sizeof(u64))
35 		return 0;
36 
37 	ret = fdt64_to_cpu(*prop);
38 	*prop = 0;
39 	return ret;
40 }
41 
42 u64 __init kaslr_early_init(void *fdt, int chosen)
43 {
44 	u64 seed, range;
45 
46 	if (kaslr_disabled_cmdline())
47 		return 0;
48 
49 	seed = get_kaslr_seed(fdt, chosen);
50 	if (!seed) {
51 		if (!__early_cpu_has_rndr() ||
52 		    !__arm64_rndr((unsigned long *)&seed))
53 			return 0;
54 	}
55 
56 	memstart_offset_seed = seed & U16_MAX;
57 
58 	/*
59 	 * OK, so we are proceeding with KASLR enabled. Calculate a suitable
60 	 * kernel image offset from the seed. Let's place the kernel in the
61 	 * 'middle' half of the VMALLOC area, and stay clear of the lower and
62 	 * upper quarters to avoid colliding with other allocations.
63 	 */
64 	range = (VMALLOC_END - KIMAGE_VADDR) / 2;
65 	return range / 2 + (((__uint128_t)range * seed) >> 64);
66 }
67