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
18/* taken from lib/string.c */
19static char *__strstr(const char *s1, const char *s2)
20{
21 size_t l1, l2;
22
23 l2 = strlen(s2);
24 if (!l2)
25 return (char *)s1;
26 l1 = strlen(s1);
27 while (l1 >= l2) {
28 l1--;
29 if (!memcmp(p: s1, q: s2, size: l2))
30 return (char *)s1;
31 s1++;
32 }
33 return NULL;
34}
35static bool cmdline_contains_nokaslr(const u8 *cmdline)
36{
37 const u8 *str;
38
39 str = __strstr(s1: cmdline, s2: "nokaslr");
40 return str == cmdline || (str > cmdline && *(str - 1) == ' ');
41}
42
43static bool is_kaslr_disabled_cmdline(void *fdt)
44{
45 if (!IS_ENABLED(CONFIG_CMDLINE_FORCE)) {
46 int node;
47 const u8 *prop;
48
49 node = fdt_path_offset(fdt, path: "/chosen");
50 if (node < 0)
51 goto out;
52
53 prop = fdt_getprop(fdt, nodeoffset: node, name: "bootargs", NULL);
54 if (!prop)
55 goto out;
56
57 if (cmdline_contains_nokaslr(cmdline: prop))
58 return true;
59
60 if (IS_ENABLED(CONFIG_CMDLINE_EXTEND))
61 goto out;
62
63 return false;
64 }
65out:
66 return cmdline_contains_nokaslr(CONFIG_CMDLINE);
67}
68
69static u64 get_kaslr_seed(void *fdt)
70{
71 int node, len;
72 fdt64_t *prop;
73 u64 ret;
74
75 node = fdt_path_offset(fdt, path: "/chosen");
76 if (node < 0)
77 return 0;
78
79 prop = fdt_getprop_w(fdt, nodeoffset: node, name: "kaslr-seed", lenp: &len);
80 if (!prop || len != sizeof(u64))
81 return 0;
82
83 ret = fdt64_to_cpu(*prop);
84 *prop = 0;
85 return ret;
86}
87
88asmlinkage u64 kaslr_early_init(void *fdt)
89{
90 u64 seed;
91
92 if (is_kaslr_disabled_cmdline(fdt))
93 return 0;
94
95 seed = get_kaslr_seed(fdt);
96 if (!seed) {
97 if (!__early_cpu_has_rndr() ||
98 !__arm64_rndr((unsigned long *)&seed))
99 return 0;
100 }
101
102 /*
103 * OK, so we are proceeding with KASLR enabled. Calculate a suitable
104 * kernel image offset from the seed. Let's place the kernel in the
105 * middle half of the VMALLOC area (VA_BITS_MIN - 2), and stay clear of
106 * the lower and upper quarters to avoid colliding with other
107 * allocations.
108 */
109 return BIT(VA_BITS_MIN - 3) + (seed & GENMASK(VA_BITS_MIN - 3, 0));
110}
111

source code of linux/arch/arm64/kernel/pi/kaslr_early.c