/* SPDX-License-Identifier: GPL-2.0-or-later */

/* TOC (Transfer of Control) handler. */

	.level 1.1

#include <asm/assembly.h>
#include <linux/threads.h>
#include <linux/linkage.h>

	.text
	.import toc_intr,code
	.import toc_stack,data
	.align 16
ENTRY_CFI(toc_handler)
	load32	PA(toc_stack),%sp

#ifdef CONFIG_SMP
	/* get per-cpu toc_stack address. */
	mfctl   %cr30, %r1
	tophys  %r1,%r2			/* task_struct */
	LDREG   TASK_TI_CPU(%r2),%r4	/* cpu */
	load32	PA(__per_cpu_offset),%r1
	LDREGX  %r4(%r1),%r4
	add	%r4,%sp,%sp
#endif

	/*
	 * setup pt_regs on stack and save the
	 * floating point registers. PIM_TOC doesn't
	 * save fp registers, so we're doing it here.
	 */
	copy	%sp,%arg0
	ldo	PT_SZ_ALGN(%sp), %sp

	/* clear pt_regs */
	copy	%arg0,%r1
0:	cmpb,<<,n %r1,%sp,0b
	stw,ma	%r0,4(%r1)

	ldo	PT_FR0(%arg0),%r25
	save_fp	%r25

	/* go virtual */
	load32	PA(swapper_pg_dir),%r4
	mtctl	%r4,%cr24
	mtctl	%r4,%cr25

	/* Clear sr4-sr7 */
	mtsp	%r0, %sr4
	mtsp	%r0, %sr5
	mtsp	%r0, %sr6
	mtsp	%r0, %sr7

	tovirt_r1 %sp
	tovirt_r1 %arg0
	virt_map

	loadgp

#ifdef CONFIG_64BIT
	ldo	-16(%sp),%r29
#endif
	load32	toc_intr,%r1
	be	0(%sr7,%r1)
	nop
ENDPROC_CFI(toc_handler)

	/*
	 * keep this checksum here, as it is part of the toc_handler
	 * spanned by toc_handler_size (all words in toc_handler are
	 * added in PDC and the sum must equal to zero.
	 */
SYM_DATA(toc_handler_csum, .long 0)
SYM_DATA(toc_handler_size, .long . - toc_handler)