/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/linkage.h>
#include <linux/kexec.h>

#include <asm/assembly.h>
#include <asm/asm-offsets.h>
#include <asm/page.h>
#include <asm/setup.h>
#include <asm/psw.h>

.level PA_ASM_LEVEL

.macro	kexec_param name
.align 8
ENTRY(kexec\()_\name)
#ifdef CONFIG_64BIT
	.dword 0
#else
	.word 0
#endif

ENTRY(kexec\()_\name\()_offset)
	.word kexec\()_\name - relocate_new_kernel
.endm

.text

/* args:
 * r26 - kimage->head
 * r25 - start address of kernel
 * r24 - physical address of relocate code
 */

ENTRY_CFI(relocate_new_kernel)
0:	copy	%arg1, %rp
	/* disable I and Q bit, so we are allowed to execute RFI */
	rsm PSW_SM_I, %r0
	nop
	nop
	nop
	nop
	nop
	nop
	nop

	rsm PSW_SM_Q, %r0
	nop
	nop
	nop
	nop
	nop
	nop
	nop

	/*
	 * After return-from-interrupt, we want to run without Code/Data
	 * translation enabled just like on a normal boot.
	 */

	/* calculate new physical execution address */
	ldo	1f-0b(%arg2), %r1
	mtctl	%r0, %cr17 /* IIASQ */
	mtctl	%r0, %cr17 /* IIASQ */
	mtctl	%r1, %cr18 /* IIAOQ */
	ldo	4(%r1),%r1
	mtctl	%r1, %cr18 /* IIAOQ */
#ifdef CONFIG_64BIT
	depdi,z	1, PSW_W_BIT, 1, %r1
	mtctl	%r1, %cr22 /* IPSW */
#else
	mtctl	%r0, %cr22 /* IPSW */
#endif
	/* lets go... */
	rfi
1:	nop
	nop

.Lloop:
	LDREG,ma	REG_SZ(%arg0), %r3
	/* If crash kernel, no copy needed */
	cmpib,COND(=),n 0,%r3,boot

	bb,<,n		%r3, 31 - IND_DONE_BIT, boot
	bb,>=,n		%r3, 31 - IND_INDIRECTION_BIT, .Lnotind
	/* indirection, load and restart */
	movb		%r3, %arg0, .Lloop
	depi		0, 31, PAGE_SHIFT, %arg0

.Lnotind:
	bb,>=,n		%r3, 31 - IND_DESTINATION_BIT, .Lnotdest
	b		.Lloop
	copy		%r3, %r20

.Lnotdest:
	bb,>=		%r3, 31 - IND_SOURCE_BIT, .Lloop
	depi		0, 31, PAGE_SHIFT, %r3
	copy		%r3, %r21

	/* copy page */
	copy		%r0, %r18
	zdepi		1, 31 - PAGE_SHIFT, 1, %r18
	add		%r20, %r18, %r17

	depi		0, 31, PAGE_SHIFT, %r20
.Lcopy:
	copy		%r20, %r12
	LDREG,ma	REG_SZ(%r21), %r8
	LDREG,ma	REG_SZ(%r21), %r9
	LDREG,ma	REG_SZ(%r21), %r10
	LDREG,ma	REG_SZ(%r21), %r11
	STREG,ma	%r8, REG_SZ(%r20)
	STREG,ma	%r9, REG_SZ(%r20)
	STREG,ma	%r10, REG_SZ(%r20)
	STREG,ma	%r11, REG_SZ(%r20)

#ifndef CONFIG_64BIT
	LDREG,ma	REG_SZ(%r21), %r8
	LDREG,ma	REG_SZ(%r21), %r9
	LDREG,ma	REG_SZ(%r21), %r10
	LDREG,ma	REG_SZ(%r21), %r11
	STREG,ma	%r8, REG_SZ(%r20)
	STREG,ma	%r9, REG_SZ(%r20)
	STREG,ma	%r10, REG_SZ(%r20)
	STREG,ma	%r11, REG_SZ(%r20)
#endif

	fdc		%r0(%r12)
	cmpb,COND(<<)	%r20,%r17,.Lcopy
	fic		(%sr4, %r12)
	b,n		.Lloop

boot:
	mtctl	%r0, %cr15

	LDREG	kexec_free_mem-0b(%arg2), %arg0
	LDREG	kexec_cmdline-0b(%arg2), %arg1
	LDREG	kexec_initrd_end-0b(%arg2), %arg3
	LDREG	kexec_initrd_start-0b(%arg2), %arg2
	bv,n %r0(%rp)

ENDPROC_CFI(relocate_new_kernel);

ENTRY(relocate_new_kernel_size)
       .word relocate_new_kernel_size - relocate_new_kernel

kexec_param cmdline
kexec_param initrd_start
kexec_param initrd_end
kexec_param free_mem
