xref: /openbmc/linux/arch/mips/kernel/relocate.c (revision ecc23d0a422a3118fcf6e4f0a46e17a6c2047b02)
1279b991bSMatt Redfearn /*
2279b991bSMatt Redfearn  * This file is subject to the terms and conditions of the GNU General Public
3279b991bSMatt Redfearn  * License.  See the file "COPYING" in the main directory of this archive
4279b991bSMatt Redfearn  * for more details.
5279b991bSMatt Redfearn  *
6279b991bSMatt Redfearn  * Support for Kernel relocation at boot time
7279b991bSMatt Redfearn  *
8279b991bSMatt Redfearn  * Copyright (C) 2015, Imagination Technologies Ltd.
9fb615d61SPaul Burton  * Authors: Matt Redfearn (matt.redfearn@mips.com)
10279b991bSMatt Redfearn  */
11405bc8fdSMatt Redfearn #include <asm/bootinfo.h>
12279b991bSMatt Redfearn #include <asm/cacheflush.h>
13405bc8fdSMatt Redfearn #include <asm/fw/fw.h>
14279b991bSMatt Redfearn #include <asm/sections.h>
15279b991bSMatt Redfearn #include <asm/setup.h>
16279b991bSMatt Redfearn #include <asm/timex.h>
17279b991bSMatt Redfearn #include <linux/elf.h>
18279b991bSMatt Redfearn #include <linux/kernel.h>
19405bc8fdSMatt Redfearn #include <linux/libfdt.h>
20405bc8fdSMatt Redfearn #include <linux/of_fdt.h>
21f39650deSAndy Shevchenko #include <linux/panic_notifier.h>
22ec62a3bdSMatt Redfearn #include <linux/sched/task.h>
23279b991bSMatt Redfearn #include <linux/start_kernel.h>
24279b991bSMatt Redfearn #include <linux/string.h>
25405bc8fdSMatt Redfearn #include <linux/printk.h>
26279b991bSMatt Redfearn 
27279b991bSMatt Redfearn #define RELOCATED(x) ((void *)((long)x + offset))
28279b991bSMatt Redfearn 
29279b991bSMatt Redfearn extern u32 _relocation_start[];	/* End kernel image / start relocation table */
30279b991bSMatt Redfearn extern u32 _relocation_end[];	/* End relocation table */
31279b991bSMatt Redfearn 
32279b991bSMatt Redfearn extern long __start___ex_table;	/* Start exception table */
33279b991bSMatt Redfearn extern long __stop___ex_table;	/* End exception table */
34279b991bSMatt Redfearn 
354c9fff36SMarcin Nowakowski extern void __weak plat_fdt_relocated(void *new_location);
364c9fff36SMarcin Nowakowski 
378cc709d7SSteven J. Hill /*
388cc709d7SSteven J. Hill  * This function may be defined for a platform to perform any post-relocation
398cc709d7SSteven J. Hill  * fixup necessary.
408cc709d7SSteven J. Hill  * Return non-zero to abort relocation
418cc709d7SSteven J. Hill  */
plat_post_relocation(long offset)428cc709d7SSteven J. Hill int __weak plat_post_relocation(long offset)
438cc709d7SSteven J. Hill {
448cc709d7SSteven J. Hill 	return 0;
458cc709d7SSteven J. Hill }
468cc709d7SSteven J. Hill 
get_synci_step(void)47279b991bSMatt Redfearn static inline u32 __init get_synci_step(void)
48279b991bSMatt Redfearn {
49279b991bSMatt Redfearn 	u32 res;
50279b991bSMatt Redfearn 
51279b991bSMatt Redfearn 	__asm__("rdhwr  %0, $1" : "=r" (res));
52279b991bSMatt Redfearn 
53279b991bSMatt Redfearn 	return res;
54279b991bSMatt Redfearn }
55279b991bSMatt Redfearn 
sync_icache(void * kbase,unsigned long kernel_length)56279b991bSMatt Redfearn static void __init sync_icache(void *kbase, unsigned long kernel_length)
57279b991bSMatt Redfearn {
58279b991bSMatt Redfearn 	void *kend = kbase + kernel_length;
59279b991bSMatt Redfearn 	u32 step = get_synci_step();
60279b991bSMatt Redfearn 
61279b991bSMatt Redfearn 	do {
62279b991bSMatt Redfearn 		__asm__ __volatile__(
63279b991bSMatt Redfearn 			"synci  0(%0)"
64279b991bSMatt Redfearn 			: /* no output */
65279b991bSMatt Redfearn 			: "r" (kbase));
66279b991bSMatt Redfearn 
67279b991bSMatt Redfearn 		kbase += step;
68c0aac3a5SJinyang He 	} while (step && kbase < kend);
69279b991bSMatt Redfearn 
70279b991bSMatt Redfearn 	/* Completion barrier */
71279b991bSMatt Redfearn 	__sync();
72279b991bSMatt Redfearn }
73279b991bSMatt Redfearn 
apply_r_mips_64_rel(u32 * loc_new,long offset)74d9e84fb1SAlexander Lobakin static void __init apply_r_mips_64_rel(u32 *loc_new, long offset)
75279b991bSMatt Redfearn {
76279b991bSMatt Redfearn 	*(u64 *)loc_new += offset;
77279b991bSMatt Redfearn }
78279b991bSMatt Redfearn 
apply_r_mips_32_rel(u32 * loc_new,long offset)79d9e84fb1SAlexander Lobakin static void __init apply_r_mips_32_rel(u32 *loc_new, long offset)
80279b991bSMatt Redfearn {
81279b991bSMatt Redfearn 	*loc_new += offset;
82279b991bSMatt Redfearn }
83279b991bSMatt Redfearn 
apply_r_mips_26_rel(u32 * loc_orig,u32 * loc_new,long offset)84279b991bSMatt Redfearn static int __init apply_r_mips_26_rel(u32 *loc_orig, u32 *loc_new, long offset)
85279b991bSMatt Redfearn {
86279b991bSMatt Redfearn 	unsigned long target_addr = (*loc_orig) & 0x03ffffff;
87279b991bSMatt Redfearn 
88279b991bSMatt Redfearn 	if (offset % 4) {
89279b991bSMatt Redfearn 		pr_err("Dangerous R_MIPS_26 REL relocation\n");
90279b991bSMatt Redfearn 		return -ENOEXEC;
91279b991bSMatt Redfearn 	}
92279b991bSMatt Redfearn 
93279b991bSMatt Redfearn 	/* Original target address */
94279b991bSMatt Redfearn 	target_addr <<= 2;
9574a2810bSJinyang He 	target_addr += (unsigned long)loc_orig & 0xf0000000;
96279b991bSMatt Redfearn 
97279b991bSMatt Redfearn 	/* Get the new target address */
98279b991bSMatt Redfearn 	target_addr += offset;
99279b991bSMatt Redfearn 
100279b991bSMatt Redfearn 	if ((target_addr & 0xf0000000) != ((unsigned long)loc_new & 0xf0000000)) {
101279b991bSMatt Redfearn 		pr_err("R_MIPS_26 REL relocation overflow\n");
102279b991bSMatt Redfearn 		return -ENOEXEC;
103279b991bSMatt Redfearn 	}
104279b991bSMatt Redfearn 
10574a2810bSJinyang He 	target_addr -= (unsigned long)loc_new & 0xf0000000;
106279b991bSMatt Redfearn 	target_addr >>= 2;
107279b991bSMatt Redfearn 
108279b991bSMatt Redfearn 	*loc_new = (*loc_new & ~0x03ffffff) | (target_addr & 0x03ffffff);
109279b991bSMatt Redfearn 
110279b991bSMatt Redfearn 	return 0;
111279b991bSMatt Redfearn }
112279b991bSMatt Redfearn 
113279b991bSMatt Redfearn 
apply_r_mips_hi16_rel(u32 * loc_orig,u32 * loc_new,long offset)114d9e84fb1SAlexander Lobakin static void __init apply_r_mips_hi16_rel(u32 *loc_orig, u32 *loc_new,
115d9e84fb1SAlexander Lobakin 					 long offset)
116279b991bSMatt Redfearn {
117279b991bSMatt Redfearn 	unsigned long insn = *loc_orig;
118279b991bSMatt Redfearn 	unsigned long target = (insn & 0xffff) << 16; /* high 16bits of target */
119279b991bSMatt Redfearn 
120279b991bSMatt Redfearn 	target += offset;
121279b991bSMatt Redfearn 
122279b991bSMatt Redfearn 	*loc_new = (insn & ~0xffff) | ((target >> 16) & 0xffff);
123d9e84fb1SAlexander Lobakin }
124d9e84fb1SAlexander Lobakin 
reloc_handler(u32 type,u32 * loc_orig,u32 * loc_new,long offset)125d9e84fb1SAlexander Lobakin static int __init reloc_handler(u32 type, u32 *loc_orig, u32 *loc_new,
126d9e84fb1SAlexander Lobakin 				long offset)
127d9e84fb1SAlexander Lobakin {
128d9e84fb1SAlexander Lobakin 	switch (type) {
129d9e84fb1SAlexander Lobakin 	case R_MIPS_64:
130d9e84fb1SAlexander Lobakin 		apply_r_mips_64_rel(loc_new, offset);
131d9e84fb1SAlexander Lobakin 		break;
132d9e84fb1SAlexander Lobakin 	case R_MIPS_32:
133d9e84fb1SAlexander Lobakin 		apply_r_mips_32_rel(loc_new, offset);
134d9e84fb1SAlexander Lobakin 		break;
135d9e84fb1SAlexander Lobakin 	case R_MIPS_26:
136d9e84fb1SAlexander Lobakin 		return apply_r_mips_26_rel(loc_orig, loc_new, offset);
137d9e84fb1SAlexander Lobakin 	case R_MIPS_HI16:
138d9e84fb1SAlexander Lobakin 		apply_r_mips_hi16_rel(loc_orig, loc_new, offset);
139d9e84fb1SAlexander Lobakin 		break;
140d9e84fb1SAlexander Lobakin 	default:
141d9e84fb1SAlexander Lobakin 		pr_err("Unhandled relocation type %d at 0x%pK\n", type,
142d9e84fb1SAlexander Lobakin 		       loc_orig);
143d9e84fb1SAlexander Lobakin 		return -ENOEXEC;
144d9e84fb1SAlexander Lobakin 	}
145d9e84fb1SAlexander Lobakin 
146279b991bSMatt Redfearn 	return 0;
147279b991bSMatt Redfearn }
148279b991bSMatt Redfearn 
do_relocations(void * kbase_old,void * kbase_new,long offset)149d9e84fb1SAlexander Lobakin static int __init do_relocations(void *kbase_old, void *kbase_new, long offset)
150279b991bSMatt Redfearn {
151279b991bSMatt Redfearn 	u32 *r;
152279b991bSMatt Redfearn 	u32 *loc_orig;
153279b991bSMatt Redfearn 	u32 *loc_new;
154279b991bSMatt Redfearn 	int type;
155279b991bSMatt Redfearn 	int res;
156279b991bSMatt Redfearn 
157279b991bSMatt Redfearn 	for (r = _relocation_start; r < _relocation_end; r++) {
158279b991bSMatt Redfearn 		/* Sentinel for last relocation */
159279b991bSMatt Redfearn 		if (*r == 0)
160279b991bSMatt Redfearn 			break;
161279b991bSMatt Redfearn 
162279b991bSMatt Redfearn 		type = (*r >> 24) & 0xff;
16349881542SPaul Burton 		loc_orig = kbase_old + ((*r & 0x00ffffff) << 2);
164279b991bSMatt Redfearn 		loc_new = RELOCATED(loc_orig);
165279b991bSMatt Redfearn 
166d9e84fb1SAlexander Lobakin 		res = reloc_handler(type, loc_orig, loc_new, offset);
167279b991bSMatt Redfearn 		if (res)
168279b991bSMatt Redfearn 			return res;
169279b991bSMatt Redfearn 	}
170279b991bSMatt Redfearn 
171279b991bSMatt Redfearn 	return 0;
172279b991bSMatt Redfearn }
173279b991bSMatt Redfearn 
174279b991bSMatt Redfearn /*
175279b991bSMatt Redfearn  * The exception table is filled in by the relocs tool after vmlinux is linked.
176279b991bSMatt Redfearn  * It must be relocated separately since there will not be any relocation
177279b991bSMatt Redfearn  * information for it filled in by the linker.
178279b991bSMatt Redfearn  */
relocate_exception_table(long offset)179279b991bSMatt Redfearn static int __init relocate_exception_table(long offset)
180279b991bSMatt Redfearn {
181279b991bSMatt Redfearn 	unsigned long *etable_start, *etable_end, *e;
182279b991bSMatt Redfearn 
183279b991bSMatt Redfearn 	etable_start = RELOCATED(&__start___ex_table);
184279b991bSMatt Redfearn 	etable_end = RELOCATED(&__stop___ex_table);
185279b991bSMatt Redfearn 
186279b991bSMatt Redfearn 	for (e = etable_start; e < etable_end; e++)
187279b991bSMatt Redfearn 		*e += offset;
188279b991bSMatt Redfearn 
189279b991bSMatt Redfearn 	return 0;
190279b991bSMatt Redfearn }
191279b991bSMatt Redfearn 
192405bc8fdSMatt Redfearn #ifdef CONFIG_RANDOMIZE_BASE
193405bc8fdSMatt Redfearn 
rotate_xor(unsigned long hash,const void * area,size_t size)194405bc8fdSMatt Redfearn static inline __init unsigned long rotate_xor(unsigned long hash,
195405bc8fdSMatt Redfearn 					      const void *area, size_t size)
196405bc8fdSMatt Redfearn {
19769e97683SAlexander Lobakin 	const typeof(hash) *ptr = PTR_ALIGN(area, sizeof(hash));
19869e97683SAlexander Lobakin 	size_t diff, i;
19969e97683SAlexander Lobakin 
20069e97683SAlexander Lobakin 	diff = (void *)ptr - area;
20169e97683SAlexander Lobakin 	if (unlikely(size < diff + sizeof(hash)))
20269e97683SAlexander Lobakin 		return hash;
20369e97683SAlexander Lobakin 
20469e97683SAlexander Lobakin 	size = ALIGN_DOWN(size - diff, sizeof(hash));
205405bc8fdSMatt Redfearn 
206405bc8fdSMatt Redfearn 	for (i = 0; i < size / sizeof(hash); i++) {
207405bc8fdSMatt Redfearn 		/* Rotate by odd number of bits and XOR. */
208405bc8fdSMatt Redfearn 		hash = (hash << ((sizeof(hash) * 8) - 7)) | (hash >> 7);
209405bc8fdSMatt Redfearn 		hash ^= ptr[i];
210405bc8fdSMatt Redfearn 	}
211405bc8fdSMatt Redfearn 
212405bc8fdSMatt Redfearn 	return hash;
213405bc8fdSMatt Redfearn }
214405bc8fdSMatt Redfearn 
get_random_boot(void)215405bc8fdSMatt Redfearn static inline __init unsigned long get_random_boot(void)
216405bc8fdSMatt Redfearn {
217405bc8fdSMatt Redfearn 	unsigned long entropy = random_get_entropy();
218405bc8fdSMatt Redfearn 	unsigned long hash = 0;
219405bc8fdSMatt Redfearn 
220405bc8fdSMatt Redfearn 	/* Attempt to create a simple but unpredictable starting entropy. */
221405bc8fdSMatt Redfearn 	hash = rotate_xor(hash, linux_banner, strlen(linux_banner));
222405bc8fdSMatt Redfearn 
223405bc8fdSMatt Redfearn 	/* Add in any runtime entropy we can get */
224405bc8fdSMatt Redfearn 	hash = rotate_xor(hash, &entropy, sizeof(entropy));
225405bc8fdSMatt Redfearn 
226405bc8fdSMatt Redfearn #if defined(CONFIG_USE_OF)
227405bc8fdSMatt Redfearn 	/* Get any additional entropy passed in device tree */
22847366979SMatt Redfearn 	if (initial_boot_params) {
229405bc8fdSMatt Redfearn 		int node, len;
230405bc8fdSMatt Redfearn 		u64 *prop;
231405bc8fdSMatt Redfearn 
232405bc8fdSMatt Redfearn 		node = fdt_path_offset(initial_boot_params, "/chosen");
233405bc8fdSMatt Redfearn 		if (node >= 0) {
234405bc8fdSMatt Redfearn 			prop = fdt_getprop_w(initial_boot_params, node,
235405bc8fdSMatt Redfearn 					     "kaslr-seed", &len);
236405bc8fdSMatt Redfearn 			if (prop && (len == sizeof(u64)))
237405bc8fdSMatt Redfearn 				hash = rotate_xor(hash, prop, sizeof(*prop));
238405bc8fdSMatt Redfearn 		}
239405bc8fdSMatt Redfearn 	}
240405bc8fdSMatt Redfearn #endif /* CONFIG_USE_OF */
241405bc8fdSMatt Redfearn 
242405bc8fdSMatt Redfearn 	return hash;
243405bc8fdSMatt Redfearn }
244405bc8fdSMatt Redfearn 
kaslr_disabled(void)245405bc8fdSMatt Redfearn static inline __init bool kaslr_disabled(void)
246405bc8fdSMatt Redfearn {
247405bc8fdSMatt Redfearn 	char *str;
248405bc8fdSMatt Redfearn 
249405bc8fdSMatt Redfearn #if defined(CONFIG_CMDLINE_BOOL)
250405bc8fdSMatt Redfearn 	const char *builtin_cmdline = CONFIG_CMDLINE;
251405bc8fdSMatt Redfearn 
252405bc8fdSMatt Redfearn 	str = strstr(builtin_cmdline, "nokaslr");
253405bc8fdSMatt Redfearn 	if (str == builtin_cmdline ||
254405bc8fdSMatt Redfearn 	    (str > builtin_cmdline && *(str - 1) == ' '))
255405bc8fdSMatt Redfearn 		return true;
256405bc8fdSMatt Redfearn #endif
257405bc8fdSMatt Redfearn 	str = strstr(arcs_cmdline, "nokaslr");
258405bc8fdSMatt Redfearn 	if (str == arcs_cmdline || (str > arcs_cmdline && *(str - 1) == ' '))
259405bc8fdSMatt Redfearn 		return true;
260405bc8fdSMatt Redfearn 
261405bc8fdSMatt Redfearn 	return false;
262405bc8fdSMatt Redfearn }
263405bc8fdSMatt Redfearn 
determine_relocation_address(void)264405bc8fdSMatt Redfearn static inline void __init *determine_relocation_address(void)
265405bc8fdSMatt Redfearn {
266405bc8fdSMatt Redfearn 	/* Choose a new address for the kernel */
267405bc8fdSMatt Redfearn 	unsigned long kernel_length;
268405bc8fdSMatt Redfearn 	void *dest = &_text;
269405bc8fdSMatt Redfearn 	unsigned long offset;
270405bc8fdSMatt Redfearn 
271405bc8fdSMatt Redfearn 	if (kaslr_disabled())
272405bc8fdSMatt Redfearn 		return dest;
273405bc8fdSMatt Redfearn 
274405bc8fdSMatt Redfearn 	kernel_length = (long)_end - (long)(&_text);
275405bc8fdSMatt Redfearn 
276405bc8fdSMatt Redfearn 	offset = get_random_boot() << 16;
277405bc8fdSMatt Redfearn 	offset &= (CONFIG_RANDOMIZE_BASE_MAX_OFFSET - 1);
278405bc8fdSMatt Redfearn 	if (offset < kernel_length)
279405bc8fdSMatt Redfearn 		offset += ALIGN(kernel_length, 0xffff);
280405bc8fdSMatt Redfearn 
281405bc8fdSMatt Redfearn 	return RELOCATED(dest);
282405bc8fdSMatt Redfearn }
283405bc8fdSMatt Redfearn 
284405bc8fdSMatt Redfearn #else
285405bc8fdSMatt Redfearn 
determine_relocation_address(void)286279b991bSMatt Redfearn static inline void __init *determine_relocation_address(void)
287279b991bSMatt Redfearn {
288279b991bSMatt Redfearn 	/*
289279b991bSMatt Redfearn 	 * Choose a new address for the kernel
290279b991bSMatt Redfearn 	 * For now we'll hard code the destination
291279b991bSMatt Redfearn 	 */
292279b991bSMatt Redfearn 	return (void *)0xffffffff81000000;
293279b991bSMatt Redfearn }
294279b991bSMatt Redfearn 
295405bc8fdSMatt Redfearn #endif
296405bc8fdSMatt Redfearn 
relocation_addr_valid(void * loc_new)297279b991bSMatt Redfearn static inline int __init relocation_addr_valid(void *loc_new)
298279b991bSMatt Redfearn {
299279b991bSMatt Redfearn 	if ((unsigned long)loc_new & 0x0000ffff) {
300279b991bSMatt Redfearn 		/* Inappropriately aligned new location */
301279b991bSMatt Redfearn 		return 0;
302279b991bSMatt Redfearn 	}
303279b991bSMatt Redfearn 	if ((unsigned long)loc_new < (unsigned long)&_end) {
304279b991bSMatt Redfearn 		/* New location overlaps original kernel */
305279b991bSMatt Redfearn 		return 0;
306279b991bSMatt Redfearn 	}
307279b991bSMatt Redfearn 	return 1;
308279b991bSMatt Redfearn }
309279b991bSMatt Redfearn 
update_kaslr_offset(unsigned long * addr,long offset)310d4d3ef8bSJinyang He static inline void __init update_kaslr_offset(unsigned long *addr, long offset)
311d4d3ef8bSJinyang He {
312d4d3ef8bSJinyang He 	unsigned long *new_addr = (unsigned long *)RELOCATED(addr);
313d4d3ef8bSJinyang He 
314d4d3ef8bSJinyang He 	*new_addr = (unsigned long)offset;
315d4d3ef8bSJinyang He }
316d4d3ef8bSJinyang He 
317a307a4ceSJinyang He #if defined(CONFIG_USE_OF)
plat_get_fdt(void)318a307a4ceSJinyang He void __weak *plat_get_fdt(void)
319a307a4ceSJinyang He {
320a307a4ceSJinyang He 	return NULL;
321a307a4ceSJinyang He }
322a307a4ceSJinyang He #endif
323a307a4ceSJinyang He 
relocate_kernel(void)324279b991bSMatt Redfearn void *__init relocate_kernel(void)
325279b991bSMatt Redfearn {
326279b991bSMatt Redfearn 	void *loc_new;
327279b991bSMatt Redfearn 	unsigned long kernel_length;
328279b991bSMatt Redfearn 	unsigned long bss_length;
329279b991bSMatt Redfearn 	long offset = 0;
330279b991bSMatt Redfearn 	int res = 1;
331279b991bSMatt Redfearn 	/* Default to original kernel entry point */
332279b991bSMatt Redfearn 	void *kernel_entry = start_kernel;
3334c9fff36SMarcin Nowakowski 	void *fdt = NULL;
334279b991bSMatt Redfearn 
335405bc8fdSMatt Redfearn 	/* Get the command line */
336405bc8fdSMatt Redfearn 	fw_init_cmdline();
337405bc8fdSMatt Redfearn #if defined(CONFIG_USE_OF)
338405bc8fdSMatt Redfearn 	/* Deal with the device tree */
3394c9fff36SMarcin Nowakowski 	fdt = plat_get_fdt();
340*1103d3b5SUsama Arif 	early_init_dt_scan(fdt, __pa(fdt));
341405bc8fdSMatt Redfearn 	if (boot_command_line[0]) {
342405bc8fdSMatt Redfearn 		/* Boot command line was passed in device tree */
343877336c1SWolfram Sang 		strscpy(arcs_cmdline, boot_command_line, COMMAND_LINE_SIZE);
344405bc8fdSMatt Redfearn 	}
345405bc8fdSMatt Redfearn #endif /* CONFIG_USE_OF */
346405bc8fdSMatt Redfearn 
347279b991bSMatt Redfearn 	kernel_length = (long)(&_relocation_start) - (long)(&_text);
348279b991bSMatt Redfearn 	bss_length = (long)&__bss_stop - (long)&__bss_start;
349279b991bSMatt Redfearn 
350279b991bSMatt Redfearn 	loc_new = determine_relocation_address();
351279b991bSMatt Redfearn 
352279b991bSMatt Redfearn 	/* Sanity check relocation address */
353279b991bSMatt Redfearn 	if (relocation_addr_valid(loc_new))
354279b991bSMatt Redfearn 		offset = (unsigned long)loc_new - (unsigned long)(&_text);
355279b991bSMatt Redfearn 
356405bc8fdSMatt Redfearn 	/* Reset the command line now so we don't end up with a duplicate */
357405bc8fdSMatt Redfearn 	arcs_cmdline[0] = '\0';
358405bc8fdSMatt Redfearn 
359279b991bSMatt Redfearn 	if (offset) {
3604c9fff36SMarcin Nowakowski 		void (*fdt_relocated_)(void *) = NULL;
3614c9fff36SMarcin Nowakowski #if defined(CONFIG_USE_OF)
3624c9fff36SMarcin Nowakowski 		unsigned long fdt_phys = virt_to_phys(fdt);
3634c9fff36SMarcin Nowakowski 
3644c9fff36SMarcin Nowakowski 		/*
3654c9fff36SMarcin Nowakowski 		 * If built-in dtb is used then it will have been relocated
3664c9fff36SMarcin Nowakowski 		 * during kernel _text relocation. If appended DTB is used
3674c9fff36SMarcin Nowakowski 		 * then it will not be relocated, but it should remain
3684c9fff36SMarcin Nowakowski 		 * intact in the original location. If dtb is loaded by
3694c9fff36SMarcin Nowakowski 		 * the bootloader then it may need to be moved if it crosses
3704c9fff36SMarcin Nowakowski 		 * the target memory area
3714c9fff36SMarcin Nowakowski 		 */
3724c9fff36SMarcin Nowakowski 
3734c9fff36SMarcin Nowakowski 		if (fdt_phys >= virt_to_phys(RELOCATED(&_text)) &&
3744c9fff36SMarcin Nowakowski 			fdt_phys <= virt_to_phys(RELOCATED(&_end))) {
3754c9fff36SMarcin Nowakowski 			void *fdt_relocated =
3764c9fff36SMarcin Nowakowski 				RELOCATED(ALIGN((long)&_end, PAGE_SIZE));
3774c9fff36SMarcin Nowakowski 			memcpy(fdt_relocated, fdt, fdt_totalsize(fdt));
3784c9fff36SMarcin Nowakowski 			fdt = fdt_relocated;
3794c9fff36SMarcin Nowakowski 			fdt_relocated_ = RELOCATED(&plat_fdt_relocated);
3804c9fff36SMarcin Nowakowski 		}
3814c9fff36SMarcin Nowakowski #endif /* CONFIG_USE_OF */
3824c9fff36SMarcin Nowakowski 
383279b991bSMatt Redfearn 		/* Copy the kernel to it's new location */
384279b991bSMatt Redfearn 		memcpy(loc_new, &_text, kernel_length);
385279b991bSMatt Redfearn 
386279b991bSMatt Redfearn 		/* Perform relocations on the new kernel */
387279b991bSMatt Redfearn 		res = do_relocations(&_text, loc_new, offset);
388279b991bSMatt Redfearn 		if (res < 0)
389279b991bSMatt Redfearn 			goto out;
390279b991bSMatt Redfearn 
391279b991bSMatt Redfearn 		/* Sync the caches ready for execution of new kernel */
392279b991bSMatt Redfearn 		sync_icache(loc_new, kernel_length);
393279b991bSMatt Redfearn 
394279b991bSMatt Redfearn 		res = relocate_exception_table(offset);
395279b991bSMatt Redfearn 		if (res < 0)
396279b991bSMatt Redfearn 			goto out;
397279b991bSMatt Redfearn 
398279b991bSMatt Redfearn 		/*
399279b991bSMatt Redfearn 		 * The original .bss has already been cleared, and
400279b991bSMatt Redfearn 		 * some variables such as command line parameters
401279b991bSMatt Redfearn 		 * stored to it so make a copy in the new location.
402279b991bSMatt Redfearn 		 */
403279b991bSMatt Redfearn 		memcpy(RELOCATED(&__bss_start), &__bss_start, bss_length);
404279b991bSMatt Redfearn 
4058cc709d7SSteven J. Hill 		/*
4064c9fff36SMarcin Nowakowski 		 * If fdt was stored outside of the kernel image and
4074c9fff36SMarcin Nowakowski 		 * had to be moved then update platform's state data
4084c9fff36SMarcin Nowakowski 		 * with the new fdt location
4094c9fff36SMarcin Nowakowski 		 */
4104c9fff36SMarcin Nowakowski 		if (fdt_relocated_)
4114c9fff36SMarcin Nowakowski 			fdt_relocated_(fdt);
4124c9fff36SMarcin Nowakowski 
4134c9fff36SMarcin Nowakowski 		/*
4148cc709d7SSteven J. Hill 		 * Last chance for the platform to abort relocation.
4158cc709d7SSteven J. Hill 		 * This may also be used by the platform to perform any
4168cc709d7SSteven J. Hill 		 * initialisation required now that the new kernel is
4178cc709d7SSteven J. Hill 		 * resident in memory and ready to be executed.
4188cc709d7SSteven J. Hill 		 */
4198cc709d7SSteven J. Hill 		if (plat_post_relocation(offset))
4208cc709d7SSteven J. Hill 			goto out;
4218cc709d7SSteven J. Hill 
422279b991bSMatt Redfearn 		/* The current thread is now within the relocated image */
423279b991bSMatt Redfearn 		__current_thread_info = RELOCATED(&init_thread_union);
424279b991bSMatt Redfearn 
425279b991bSMatt Redfearn 		/* Return the new kernel's entry point */
426279b991bSMatt Redfearn 		kernel_entry = RELOCATED(start_kernel);
427d4d3ef8bSJinyang He 
428d4d3ef8bSJinyang He 		/* Error may occur before, so keep it at last */
429d4d3ef8bSJinyang He 		update_kaslr_offset(&__kaslr_offset, offset);
430279b991bSMatt Redfearn 	}
431279b991bSMatt Redfearn out:
432279b991bSMatt Redfearn 	return kernel_entry;
433279b991bSMatt Redfearn }
434405bc8fdSMatt Redfearn 
435405bc8fdSMatt Redfearn /*
436405bc8fdSMatt Redfearn  * Show relocation information on panic.
437405bc8fdSMatt Redfearn  */
show_kernel_relocation(const char * level)438d9e84fb1SAlexander Lobakin static void show_kernel_relocation(const char *level)
439405bc8fdSMatt Redfearn {
440457d2fc5SJinyang He 	if (__kaslr_offset > 0) {
441405bc8fdSMatt Redfearn 		printk(level);
442457d2fc5SJinyang He 		pr_cont("Kernel relocated by 0x%pK\n", (void *)__kaslr_offset);
443405bc8fdSMatt Redfearn 		pr_cont(" .text @ 0x%pK\n", _text);
444405bc8fdSMatt Redfearn 		pr_cont(" .data @ 0x%pK\n", _sdata);
445405bc8fdSMatt Redfearn 		pr_cont(" .bss  @ 0x%pK\n", __bss_start);
446405bc8fdSMatt Redfearn 	}
447405bc8fdSMatt Redfearn }
448405bc8fdSMatt Redfearn 
kernel_location_notifier_fn(struct notifier_block * self,unsigned long v,void * p)449405bc8fdSMatt Redfearn static int kernel_location_notifier_fn(struct notifier_block *self,
450405bc8fdSMatt Redfearn 				       unsigned long v, void *p)
451405bc8fdSMatt Redfearn {
452405bc8fdSMatt Redfearn 	show_kernel_relocation(KERN_EMERG);
453405bc8fdSMatt Redfearn 	return NOTIFY_DONE;
454405bc8fdSMatt Redfearn }
455405bc8fdSMatt Redfearn 
456405bc8fdSMatt Redfearn static struct notifier_block kernel_location_notifier = {
457405bc8fdSMatt Redfearn 	.notifier_call = kernel_location_notifier_fn
458405bc8fdSMatt Redfearn };
459405bc8fdSMatt Redfearn 
register_kernel_offset_dumper(void)460405bc8fdSMatt Redfearn static int __init register_kernel_offset_dumper(void)
461405bc8fdSMatt Redfearn {
462405bc8fdSMatt Redfearn 	atomic_notifier_chain_register(&panic_notifier_list,
463405bc8fdSMatt Redfearn 				       &kernel_location_notifier);
464405bc8fdSMatt Redfearn 	return 0;
465405bc8fdSMatt Redfearn }
466405bc8fdSMatt Redfearn __initcall(register_kernel_offset_dumper);
467