111 lines
2.3 KiB
C
111 lines
2.3 KiB
C
|
// 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));
|
||
|
}
|