// SPDX-License-Identifier: GPL-2.0-only // Copyright 2022 Google LLC // Author: Ard Biesheuvel <ardb@google.com> // NOTE: code in this file runs *very* early, and is not permitted to use // global variables or anything that relies on absolute addressing. #include <linux/libfdt.h> #include <linux/init.h> #include <linux/linkage.h> #include <linux/types.h> #include <linux/sizes.h> #include <linux/string.h> #include <asm/archrandom.h> #include <asm/memory.h> /* taken from lib/string.c */ static char *__strstr(const char *s1, const char *s2) { size_t l1, l2; l2 = strlen(s2); if (!l2) return (char *)s1; l1 = strlen(s1); while (l1 >= l2) { l1--; if (!memcmp(s1, s2, l2)) return (char *)s1; s1++; } return NULL; } static bool cmdline_contains_nokaslr(const u8 *cmdline) { const u8 *str; str = __strstr(cmdline, "nokaslr"); return str == cmdline || (str > cmdline && *(str - 1) == ' '); } static bool is_kaslr_disabled_cmdline(void *fdt) { if (!IS_ENABLED(CONFIG_CMDLINE_FORCE)) { int node; const u8 *prop; node = fdt_path_offset(fdt, "/chosen"); if (node < 0) goto out; prop = fdt_getprop(fdt, node, "bootargs", NULL); if (!prop) goto out; if (cmdline_contains_nokaslr(prop)) return true; if (IS_ENABLED(CONFIG_CMDLINE_EXTEND)) goto out; return false; } out: return cmdline_contains_nokaslr(CONFIG_CMDLINE); } static u64 get_kaslr_seed(void *fdt) { int node, len; fdt64_t *prop; u64 ret; node = fdt_path_offset(fdt, "/chosen"); if (node < 0) return 0; prop = fdt_getprop_w(fdt, node, "kaslr-seed", &len); if (!prop || len != sizeof(u64)) return 0; ret = fdt64_to_cpu(*prop); *prop = 0; return ret; } asmlinkage u64 kaslr_early_init(void *fdt) { u64 seed; if (is_kaslr_disabled_cmdline(fdt)) return 0; seed = get_kaslr_seed(fdt); if (!seed) { if (!__early_cpu_has_rndr() || !__arm64_rndr((unsigned long *)&seed)) return 0; } /* * OK, so we are proceeding with KASLR enabled. Calculate a suitable * kernel image offset from the seed. Let's place the kernel in the * middle half of the VMALLOC area (VA_BITS_MIN - 2), and stay clear of * the lower and upper quarters to avoid colliding with other * allocations. */ return BIT(VA_BITS_MIN - 3) + (seed & GENMASK(VA_BITS_MIN - 3, 0)); }