/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (C) 2020 Google LLC
 */

#include <linux/linkage.h>
#include <asm/assembler.h>

/*
 * Report a tag mismatch detected by tag-based KASAN.
 *
 * A compiler-generated thunk calls this with a non-AAPCS calling
 * convention. Upon entry to this function, registers are as follows:
 *
 * x0:         fault address (see below for restore)
 * x1:         fault description (see below for restore)
 * x2 to x15:  callee-saved
 * x16 to x17: safe to clobber
 * x18 to x30: callee-saved
 * sp:         pre-decremented by 256 bytes (see below for restore)
 *
 * The caller has decremented the SP by 256 bytes, and created a
 * structure on the stack as follows:
 *
 * sp + 0..15:    x0 and x1 to be restored
 * sp + 16..231:  free for use
 * sp + 232..247: x29 and x30 (same as in GPRs)
 * sp + 248..255: free for use
 *
 * Note that this is not a struct pt_regs.
 *
 * To call a regular AAPCS function we must save x2 to x15 (which we can
 * store in the gaps), and create a frame record (for which we can use
 * x29 and x30 spilled by the caller as those match the GPRs).
 *
 * The caller expects x0 and x1 to be restored from the structure, and
 * for the structure to be removed from the stack (i.e. the SP must be
 * incremented by 256 prior to return).
 */
SYM_CODE_START(__hwasan_tag_mismatch)
	bti	c
	add	x29, sp, #232
	stp	x2, x3, [sp, #8 * 2]
	stp	x4, x5, [sp, #8 * 4]
	stp	x6, x7, [sp, #8 * 6]
	stp	x8, x9, [sp, #8 * 8]
	stp	x10, x11, [sp, #8 * 10]
	stp	x12, x13, [sp, #8 * 12]
	stp	x14, x15, [sp, #8 * 14]
#ifndef CONFIG_SHADOW_CALL_STACK
	str	x18, [sp, #8 * 18]
#endif

	mov	x2, x30
	bl	kasan_tag_mismatch

	ldp	x0, x1, [sp]
	ldp	x2, x3, [sp, #8 * 2]
	ldp	x4, x5, [sp, #8 * 4]
	ldp	x6, x7, [sp, #8 * 6]
	ldp	x8, x9, [sp, #8 * 8]
	ldp	x10, x11, [sp, #8 * 10]
	ldp	x12, x13, [sp, #8 * 12]
	ldp	x14, x15, [sp, #8 * 14]
#ifndef CONFIG_SHADOW_CALL_STACK
	ldr	x18, [sp, #8 * 18]
#endif
	ldp	x29, x30, [sp, #8 * 29]

	/* remove the structure from the stack */
	add	sp, sp, #256
	ret
SYM_CODE_END(__hwasan_tag_mismatch)
EXPORT_SYMBOL(__hwasan_tag_mismatch)