cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

kaslr_booke.c (9790B)


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