18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// Copyright (C) 2019 Jason Yan <yanaijie@huawei.com>
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci#include <linux/kernel.h>
68c2ecf20Sopenharmony_ci#include <linux/errno.h>
78c2ecf20Sopenharmony_ci#include <linux/string.h>
88c2ecf20Sopenharmony_ci#include <linux/types.h>
98c2ecf20Sopenharmony_ci#include <linux/mm.h>
108c2ecf20Sopenharmony_ci#include <linux/swap.h>
118c2ecf20Sopenharmony_ci#include <linux/stddef.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/delay.h>
148c2ecf20Sopenharmony_ci#include <linux/memblock.h>
158c2ecf20Sopenharmony_ci#include <linux/libfdt.h>
168c2ecf20Sopenharmony_ci#include <linux/crash_core.h>
178c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
188c2ecf20Sopenharmony_ci#include <asm/prom.h>
198c2ecf20Sopenharmony_ci#include <asm/kdump.h>
208c2ecf20Sopenharmony_ci#include <mm/mmu_decl.h>
218c2ecf20Sopenharmony_ci#include <generated/utsrelease.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistruct regions {
248c2ecf20Sopenharmony_ci	unsigned long pa_start;
258c2ecf20Sopenharmony_ci	unsigned long pa_end;
268c2ecf20Sopenharmony_ci	unsigned long kernel_size;
278c2ecf20Sopenharmony_ci	unsigned long dtb_start;
288c2ecf20Sopenharmony_ci	unsigned long dtb_end;
298c2ecf20Sopenharmony_ci	unsigned long initrd_start;
308c2ecf20Sopenharmony_ci	unsigned long initrd_end;
318c2ecf20Sopenharmony_ci	unsigned long crash_start;
328c2ecf20Sopenharmony_ci	unsigned long crash_end;
338c2ecf20Sopenharmony_ci	int reserved_mem;
348c2ecf20Sopenharmony_ci	int reserved_mem_addr_cells;
358c2ecf20Sopenharmony_ci	int reserved_mem_size_cells;
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistruct regions __initdata regions;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic __init void kaslr_get_cmdline(void *fdt)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	int node = fdt_path_offset(fdt, "/chosen");
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	early_init_dt_scan_chosen(node, "chosen", 1, boot_command_line);
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic unsigned long __init rotate_xor(unsigned long hash, const void *area,
488c2ecf20Sopenharmony_ci				       size_t size)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	size_t i;
518c2ecf20Sopenharmony_ci	const unsigned long *ptr = area;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	for (i = 0; i < size / sizeof(hash); i++) {
548c2ecf20Sopenharmony_ci		/* Rotate by odd number of bits and XOR. */
558c2ecf20Sopenharmony_ci		hash = (hash << ((sizeof(hash) * 8) - 7)) | (hash >> 7);
568c2ecf20Sopenharmony_ci		hash ^= ptr[i];
578c2ecf20Sopenharmony_ci	}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	return hash;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* Attempt to create a simple starting entropy. This can make it defferent for
638c2ecf20Sopenharmony_ci * every build but it is still not enough. Stronger entropy should
648c2ecf20Sopenharmony_ci * be added to make it change for every boot.
658c2ecf20Sopenharmony_ci */
668c2ecf20Sopenharmony_cistatic unsigned long __init get_boot_seed(void *fdt)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	unsigned long hash = 0;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	/* build-specific string for starting entropy. */
718c2ecf20Sopenharmony_ci	hash = rotate_xor(hash, linux_banner, strlen(linux_banner));
728c2ecf20Sopenharmony_ci	hash = rotate_xor(hash, fdt, fdt_totalsize(fdt));
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	return hash;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic __init u64 get_kaslr_seed(void *fdt)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	int node, len;
808c2ecf20Sopenharmony_ci	fdt64_t *prop;
818c2ecf20Sopenharmony_ci	u64 ret;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	node = fdt_path_offset(fdt, "/chosen");
848c2ecf20Sopenharmony_ci	if (node < 0)
858c2ecf20Sopenharmony_ci		return 0;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	prop = fdt_getprop_w(fdt, node, "kaslr-seed", &len);
888c2ecf20Sopenharmony_ci	if (!prop || len != sizeof(u64))
898c2ecf20Sopenharmony_ci		return 0;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	ret = fdt64_to_cpu(*prop);
928c2ecf20Sopenharmony_ci	*prop = 0;
938c2ecf20Sopenharmony_ci	return ret;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic __init bool regions_overlap(u32 s1, u32 e1, u32 s2, u32 e2)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	return e1 >= s2 && e2 >= s1;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic __init bool overlaps_reserved_region(const void *fdt, u32 start,
1028c2ecf20Sopenharmony_ci					    u32 end)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	int subnode, len, i;
1058c2ecf20Sopenharmony_ci	u64 base, size;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	/* check for overlap with /memreserve/ entries */
1088c2ecf20Sopenharmony_ci	for (i = 0; i < fdt_num_mem_rsv(fdt); i++) {
1098c2ecf20Sopenharmony_ci		if (fdt_get_mem_rsv(fdt, i, &base, &size) < 0)
1108c2ecf20Sopenharmony_ci			continue;
1118c2ecf20Sopenharmony_ci		if (regions_overlap(start, end, base, base + size))
1128c2ecf20Sopenharmony_ci			return true;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	if (regions.reserved_mem < 0)
1168c2ecf20Sopenharmony_ci		return false;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	/* check for overlap with static reservations in /reserved-memory */
1198c2ecf20Sopenharmony_ci	for (subnode = fdt_first_subnode(fdt, regions.reserved_mem);
1208c2ecf20Sopenharmony_ci	     subnode >= 0;
1218c2ecf20Sopenharmony_ci	     subnode = fdt_next_subnode(fdt, subnode)) {
1228c2ecf20Sopenharmony_ci		const fdt32_t *reg;
1238c2ecf20Sopenharmony_ci		u64 rsv_end;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci		len = 0;
1268c2ecf20Sopenharmony_ci		reg = fdt_getprop(fdt, subnode, "reg", &len);
1278c2ecf20Sopenharmony_ci		while (len >= (regions.reserved_mem_addr_cells +
1288c2ecf20Sopenharmony_ci			       regions.reserved_mem_size_cells)) {
1298c2ecf20Sopenharmony_ci			base = fdt32_to_cpu(reg[0]);
1308c2ecf20Sopenharmony_ci			if (regions.reserved_mem_addr_cells == 2)
1318c2ecf20Sopenharmony_ci				base = (base << 32) | fdt32_to_cpu(reg[1]);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci			reg += regions.reserved_mem_addr_cells;
1348c2ecf20Sopenharmony_ci			len -= 4 * regions.reserved_mem_addr_cells;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci			size = fdt32_to_cpu(reg[0]);
1378c2ecf20Sopenharmony_ci			if (regions.reserved_mem_size_cells == 2)
1388c2ecf20Sopenharmony_ci				size = (size << 32) | fdt32_to_cpu(reg[1]);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci			reg += regions.reserved_mem_size_cells;
1418c2ecf20Sopenharmony_ci			len -= 4 * regions.reserved_mem_size_cells;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci			if (base >= regions.pa_end)
1448c2ecf20Sopenharmony_ci				continue;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci			rsv_end = min(base + size, (u64)U32_MAX);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci			if (regions_overlap(start, end, base, rsv_end))
1498c2ecf20Sopenharmony_ci				return true;
1508c2ecf20Sopenharmony_ci		}
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci	return false;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic __init bool overlaps_region(const void *fdt, u32 start,
1568c2ecf20Sopenharmony_ci				   u32 end)
1578c2ecf20Sopenharmony_ci{
1588c2ecf20Sopenharmony_ci	if (regions_overlap(start, end, __pa(_stext), __pa(_end)))
1598c2ecf20Sopenharmony_ci		return true;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (regions_overlap(start, end, regions.dtb_start,
1628c2ecf20Sopenharmony_ci			    regions.dtb_end))
1638c2ecf20Sopenharmony_ci		return true;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	if (regions_overlap(start, end, regions.initrd_start,
1668c2ecf20Sopenharmony_ci			    regions.initrd_end))
1678c2ecf20Sopenharmony_ci		return true;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	if (regions_overlap(start, end, regions.crash_start,
1708c2ecf20Sopenharmony_ci			    regions.crash_end))
1718c2ecf20Sopenharmony_ci		return true;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return overlaps_reserved_region(fdt, start, end);
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic void __init get_crash_kernel(void *fdt, unsigned long size)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci#ifdef CONFIG_CRASH_CORE
1798c2ecf20Sopenharmony_ci	unsigned long long crash_size, crash_base;
1808c2ecf20Sopenharmony_ci	int ret;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	ret = parse_crashkernel(boot_command_line, size, &crash_size,
1838c2ecf20Sopenharmony_ci				&crash_base);
1848c2ecf20Sopenharmony_ci	if (ret != 0 || crash_size == 0)
1858c2ecf20Sopenharmony_ci		return;
1868c2ecf20Sopenharmony_ci	if (crash_base == 0)
1878c2ecf20Sopenharmony_ci		crash_base = KDUMP_KERNELBASE;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	regions.crash_start = (unsigned long)crash_base;
1908c2ecf20Sopenharmony_ci	regions.crash_end = (unsigned long)(crash_base + crash_size);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	pr_debug("crash_base=0x%llx crash_size=0x%llx\n", crash_base, crash_size);
1938c2ecf20Sopenharmony_ci#endif
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic void __init get_initrd_range(void *fdt)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	u64 start, end;
1998c2ecf20Sopenharmony_ci	int node, len;
2008c2ecf20Sopenharmony_ci	const __be32 *prop;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	node = fdt_path_offset(fdt, "/chosen");
2038c2ecf20Sopenharmony_ci	if (node < 0)
2048c2ecf20Sopenharmony_ci		return;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	prop = fdt_getprop(fdt, node, "linux,initrd-start", &len);
2078c2ecf20Sopenharmony_ci	if (!prop)
2088c2ecf20Sopenharmony_ci		return;
2098c2ecf20Sopenharmony_ci	start = of_read_number(prop, len / 4);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	prop = fdt_getprop(fdt, node, "linux,initrd-end", &len);
2128c2ecf20Sopenharmony_ci	if (!prop)
2138c2ecf20Sopenharmony_ci		return;
2148c2ecf20Sopenharmony_ci	end = of_read_number(prop, len / 4);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	regions.initrd_start = (unsigned long)start;
2178c2ecf20Sopenharmony_ci	regions.initrd_end = (unsigned long)end;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	pr_debug("initrd_start=0x%llx  initrd_end=0x%llx\n", start, end);
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic __init unsigned long get_usable_address(const void *fdt,
2238c2ecf20Sopenharmony_ci					       unsigned long start,
2248c2ecf20Sopenharmony_ci					       unsigned long offset)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	unsigned long pa;
2278c2ecf20Sopenharmony_ci	unsigned long pa_end;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	for (pa = offset; (long)pa > (long)start; pa -= SZ_16K) {
2308c2ecf20Sopenharmony_ci		pa_end = pa + regions.kernel_size;
2318c2ecf20Sopenharmony_ci		if (overlaps_region(fdt, pa, pa_end))
2328c2ecf20Sopenharmony_ci			continue;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci		return pa;
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci	return 0;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic __init void get_cell_sizes(const void *fdt, int node, int *addr_cells,
2408c2ecf20Sopenharmony_ci				  int *size_cells)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	const int *prop;
2438c2ecf20Sopenharmony_ci	int len;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	/*
2468c2ecf20Sopenharmony_ci	 * Retrieve the #address-cells and #size-cells properties
2478c2ecf20Sopenharmony_ci	 * from the 'node', or use the default if not provided.
2488c2ecf20Sopenharmony_ci	 */
2498c2ecf20Sopenharmony_ci	*addr_cells = *size_cells = 1;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	prop = fdt_getprop(fdt, node, "#address-cells", &len);
2528c2ecf20Sopenharmony_ci	if (len == 4)
2538c2ecf20Sopenharmony_ci		*addr_cells = fdt32_to_cpu(*prop);
2548c2ecf20Sopenharmony_ci	prop = fdt_getprop(fdt, node, "#size-cells", &len);
2558c2ecf20Sopenharmony_ci	if (len == 4)
2568c2ecf20Sopenharmony_ci		*size_cells = fdt32_to_cpu(*prop);
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic unsigned long __init kaslr_legal_offset(void *dt_ptr, unsigned long index,
2608c2ecf20Sopenharmony_ci					       unsigned long offset)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	unsigned long koffset = 0;
2638c2ecf20Sopenharmony_ci	unsigned long start;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	while ((long)index >= 0) {
2668c2ecf20Sopenharmony_ci		offset = memstart_addr + index * SZ_64M + offset;
2678c2ecf20Sopenharmony_ci		start = memstart_addr + index * SZ_64M;
2688c2ecf20Sopenharmony_ci		koffset = get_usable_address(dt_ptr, start, offset);
2698c2ecf20Sopenharmony_ci		if (koffset)
2708c2ecf20Sopenharmony_ci			break;
2718c2ecf20Sopenharmony_ci		index--;
2728c2ecf20Sopenharmony_ci	}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (koffset != 0)
2758c2ecf20Sopenharmony_ci		koffset -= memstart_addr;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	return koffset;
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic inline __init bool kaslr_disabled(void)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	return strstr(boot_command_line, "nokaslr") != NULL;
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic unsigned long __init kaslr_choose_location(void *dt_ptr, phys_addr_t size,
2868c2ecf20Sopenharmony_ci						  unsigned long kernel_sz)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	unsigned long offset, random;
2898c2ecf20Sopenharmony_ci	unsigned long ram, linear_sz;
2908c2ecf20Sopenharmony_ci	u64 seed;
2918c2ecf20Sopenharmony_ci	unsigned long index;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	kaslr_get_cmdline(dt_ptr);
2948c2ecf20Sopenharmony_ci	if (kaslr_disabled())
2958c2ecf20Sopenharmony_ci		return 0;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	random = get_boot_seed(dt_ptr);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	seed = get_tb() << 32;
3008c2ecf20Sopenharmony_ci	seed ^= get_tb();
3018c2ecf20Sopenharmony_ci	random = rotate_xor(random, &seed, sizeof(seed));
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	/*
3048c2ecf20Sopenharmony_ci	 * Retrieve (and wipe) the seed from the FDT
3058c2ecf20Sopenharmony_ci	 */
3068c2ecf20Sopenharmony_ci	seed = get_kaslr_seed(dt_ptr);
3078c2ecf20Sopenharmony_ci	if (seed)
3088c2ecf20Sopenharmony_ci		random = rotate_xor(random, &seed, sizeof(seed));
3098c2ecf20Sopenharmony_ci	else
3108c2ecf20Sopenharmony_ci		pr_warn("KASLR: No safe seed for randomizing the kernel base.\n");
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	ram = min_t(phys_addr_t, __max_low_memory, size);
3138c2ecf20Sopenharmony_ci	ram = map_mem_in_cams(ram, CONFIG_LOWMEM_CAM_NUM, true);
3148c2ecf20Sopenharmony_ci	linear_sz = min_t(unsigned long, ram, SZ_512M);
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/* If the linear size is smaller than 64M, do not randmize */
3178c2ecf20Sopenharmony_ci	if (linear_sz < SZ_64M)
3188c2ecf20Sopenharmony_ci		return 0;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	/* check for a reserved-memory node and record its cell sizes */
3218c2ecf20Sopenharmony_ci	regions.reserved_mem = fdt_path_offset(dt_ptr, "/reserved-memory");
3228c2ecf20Sopenharmony_ci	if (regions.reserved_mem >= 0)
3238c2ecf20Sopenharmony_ci		get_cell_sizes(dt_ptr, regions.reserved_mem,
3248c2ecf20Sopenharmony_ci			       &regions.reserved_mem_addr_cells,
3258c2ecf20Sopenharmony_ci			       &regions.reserved_mem_size_cells);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	regions.pa_start = memstart_addr;
3288c2ecf20Sopenharmony_ci	regions.pa_end = memstart_addr + linear_sz;
3298c2ecf20Sopenharmony_ci	regions.dtb_start = __pa(dt_ptr);
3308c2ecf20Sopenharmony_ci	regions.dtb_end = __pa(dt_ptr) + fdt_totalsize(dt_ptr);
3318c2ecf20Sopenharmony_ci	regions.kernel_size = kernel_sz;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	get_initrd_range(dt_ptr);
3348c2ecf20Sopenharmony_ci	get_crash_kernel(dt_ptr, ram);
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/*
3378c2ecf20Sopenharmony_ci	 * Decide which 64M we want to start
3388c2ecf20Sopenharmony_ci	 * Only use the low 8 bits of the random seed
3398c2ecf20Sopenharmony_ci	 */
3408c2ecf20Sopenharmony_ci	index = random & 0xFF;
3418c2ecf20Sopenharmony_ci	index %= linear_sz / SZ_64M;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	/* Decide offset inside 64M */
3448c2ecf20Sopenharmony_ci	offset = random % (SZ_64M - kernel_sz);
3458c2ecf20Sopenharmony_ci	offset = round_down(offset, SZ_16K);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	return kaslr_legal_offset(dt_ptr, index, offset);
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci/*
3518c2ecf20Sopenharmony_ci * To see if we need to relocate the kernel to a random offset
3528c2ecf20Sopenharmony_ci * void *dt_ptr - address of the device tree
3538c2ecf20Sopenharmony_ci * phys_addr_t size - size of the first memory block
3548c2ecf20Sopenharmony_ci */
3558c2ecf20Sopenharmony_cinotrace void __init kaslr_early_init(void *dt_ptr, phys_addr_t size)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	unsigned long tlb_virt;
3588c2ecf20Sopenharmony_ci	phys_addr_t tlb_phys;
3598c2ecf20Sopenharmony_ci	unsigned long offset;
3608c2ecf20Sopenharmony_ci	unsigned long kernel_sz;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	kernel_sz = (unsigned long)_end - (unsigned long)_stext;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	offset = kaslr_choose_location(dt_ptr, size, kernel_sz);
3658c2ecf20Sopenharmony_ci	if (offset == 0)
3668c2ecf20Sopenharmony_ci		return;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	kernstart_virt_addr += offset;
3698c2ecf20Sopenharmony_ci	kernstart_addr += offset;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	is_second_reloc = 1;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	if (offset >= SZ_64M) {
3748c2ecf20Sopenharmony_ci		tlb_virt = round_down(kernstart_virt_addr, SZ_64M);
3758c2ecf20Sopenharmony_ci		tlb_phys = round_down(kernstart_addr, SZ_64M);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci		/* Create kernel map to relocate in */
3788c2ecf20Sopenharmony_ci		create_kaslr_tlb_entry(1, tlb_virt, tlb_phys);
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	/* Copy the kernel to it's new location and run */
3828c2ecf20Sopenharmony_ci	memcpy((void *)kernstart_virt_addr, (void *)_stext, kernel_sz);
3838c2ecf20Sopenharmony_ci	flush_icache_range(kernstart_virt_addr, kernstart_virt_addr + kernel_sz);
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	reloc_kernel_entry(dt_ptr, kernstart_virt_addr);
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_civoid __init kaslr_late_init(void)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	/* If randomized, clear the original kernel */
3918c2ecf20Sopenharmony_ci	if (kernstart_virt_addr != KERNELBASE) {
3928c2ecf20Sopenharmony_ci		unsigned long kernel_sz;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci		kernel_sz = (unsigned long)_end - kernstart_virt_addr;
3958c2ecf20Sopenharmony_ci		memzero_explicit((void *)KERNELBASE, kernel_sz);
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci}
398