18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/ptrace.h>
58c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
68c2ecf20Sopenharmony_ci#include <abi/reg_ops.h>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#define MTCR_MASK	0xFC00FFE0
98c2ecf20Sopenharmony_ci#define MFCR_MASK	0xFC00FFE0
108c2ecf20Sopenharmony_ci#define MTCR_DIST	0xC0006420
118c2ecf20Sopenharmony_ci#define MFCR_DIST	0xC0006020
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/*
148c2ecf20Sopenharmony_ci * fpu_libc_helper() is to help libc to excute:
158c2ecf20Sopenharmony_ci *  - mfcr %a, cr<1, 2>
168c2ecf20Sopenharmony_ci *  - mfcr %a, cr<2, 2>
178c2ecf20Sopenharmony_ci *  - mtcr %a, cr<1, 2>
188c2ecf20Sopenharmony_ci *  - mtcr %a, cr<2, 2>
198c2ecf20Sopenharmony_ci */
208c2ecf20Sopenharmony_ciint fpu_libc_helper(struct pt_regs *regs)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	int fault;
238c2ecf20Sopenharmony_ci	unsigned long instrptr, regx = 0;
248c2ecf20Sopenharmony_ci	unsigned long index = 0, tmp = 0;
258c2ecf20Sopenharmony_ci	unsigned long tinstr = 0;
268c2ecf20Sopenharmony_ci	u16 instr_hi, instr_low;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	instrptr = instruction_pointer(regs);
298c2ecf20Sopenharmony_ci	if (instrptr & 1)
308c2ecf20Sopenharmony_ci		return 0;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	fault = __get_user(instr_low, (u16 *)instrptr);
338c2ecf20Sopenharmony_ci	if (fault)
348c2ecf20Sopenharmony_ci		return 0;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	fault = __get_user(instr_hi, (u16 *)(instrptr + 2));
378c2ecf20Sopenharmony_ci	if (fault)
388c2ecf20Sopenharmony_ci		return 0;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	tinstr = instr_hi | ((unsigned long)instr_low << 16);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	if (((tinstr >> 21) & 0x1F) != 2)
438c2ecf20Sopenharmony_ci		return 0;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	if ((tinstr & MTCR_MASK) == MTCR_DIST) {
468c2ecf20Sopenharmony_ci		index = (tinstr >> 16) & 0x1F;
478c2ecf20Sopenharmony_ci		if (index > 13)
488c2ecf20Sopenharmony_ci			return 0;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci		tmp = tinstr & 0x1F;
518c2ecf20Sopenharmony_ci		if (tmp > 2)
528c2ecf20Sopenharmony_ci			return 0;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci		regx =  *(&regs->a0 + index);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci		if (tmp == 1)
578c2ecf20Sopenharmony_ci			mtcr("cr<1, 2>", regx);
588c2ecf20Sopenharmony_ci		else if (tmp == 2)
598c2ecf20Sopenharmony_ci			mtcr("cr<2, 2>", regx);
608c2ecf20Sopenharmony_ci		else
618c2ecf20Sopenharmony_ci			return 0;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci		regs->pc += 4;
648c2ecf20Sopenharmony_ci		return 1;
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if ((tinstr & MFCR_MASK) == MFCR_DIST) {
688c2ecf20Sopenharmony_ci		index = tinstr & 0x1F;
698c2ecf20Sopenharmony_ci		if (index > 13)
708c2ecf20Sopenharmony_ci			return 0;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci		tmp = ((tinstr >> 16) & 0x1F);
738c2ecf20Sopenharmony_ci		if (tmp > 2)
748c2ecf20Sopenharmony_ci			return 0;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci		if (tmp == 1)
778c2ecf20Sopenharmony_ci			regx = mfcr("cr<1, 2>");
788c2ecf20Sopenharmony_ci		else if (tmp == 2)
798c2ecf20Sopenharmony_ci			regx = mfcr("cr<2, 2>");
808c2ecf20Sopenharmony_ci		else
818c2ecf20Sopenharmony_ci			return 0;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci		*(&regs->a0 + index) = regx;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci		regs->pc += 4;
868c2ecf20Sopenharmony_ci		return 1;
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	return 0;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_civoid fpu_fpe(struct pt_regs *regs)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	int sig, code;
958c2ecf20Sopenharmony_ci	unsigned int fesr;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	fesr = mfcr("cr<2, 2>");
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	sig = SIGFPE;
1008c2ecf20Sopenharmony_ci	code = FPE_FLTUNK;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	if (fesr & FPE_ILLE) {
1038c2ecf20Sopenharmony_ci		sig = SIGILL;
1048c2ecf20Sopenharmony_ci		code = ILL_ILLOPC;
1058c2ecf20Sopenharmony_ci	} else if (fesr & FPE_IDC) {
1068c2ecf20Sopenharmony_ci		sig = SIGILL;
1078c2ecf20Sopenharmony_ci		code = ILL_ILLOPN;
1088c2ecf20Sopenharmony_ci	} else if (fesr & FPE_FEC) {
1098c2ecf20Sopenharmony_ci		sig = SIGFPE;
1108c2ecf20Sopenharmony_ci		if (fesr & FPE_IOC)
1118c2ecf20Sopenharmony_ci			code = FPE_FLTINV;
1128c2ecf20Sopenharmony_ci		else if (fesr & FPE_DZC)
1138c2ecf20Sopenharmony_ci			code = FPE_FLTDIV;
1148c2ecf20Sopenharmony_ci		else if (fesr & FPE_UFC)
1158c2ecf20Sopenharmony_ci			code = FPE_FLTUND;
1168c2ecf20Sopenharmony_ci		else if (fesr & FPE_OFC)
1178c2ecf20Sopenharmony_ci			code = FPE_FLTOVF;
1188c2ecf20Sopenharmony_ci		else if (fesr & FPE_IXC)
1198c2ecf20Sopenharmony_ci			code = FPE_FLTRES;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	force_sig_fault(sig, code, (void __user *)regs->pc);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci#define FMFVR_FPU_REGS(vrx, vry)	\
1268c2ecf20Sopenharmony_ci	"fmfvrl %0, "#vrx"\n"		\
1278c2ecf20Sopenharmony_ci	"fmfvrh %1, "#vrx"\n"		\
1288c2ecf20Sopenharmony_ci	"fmfvrl %2, "#vry"\n"		\
1298c2ecf20Sopenharmony_ci	"fmfvrh %3, "#vry"\n"
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci#define FMTVR_FPU_REGS(vrx, vry)	\
1328c2ecf20Sopenharmony_ci	"fmtvrl "#vrx", %0\n"		\
1338c2ecf20Sopenharmony_ci	"fmtvrh "#vrx", %1\n"		\
1348c2ecf20Sopenharmony_ci	"fmtvrl "#vry", %2\n"		\
1358c2ecf20Sopenharmony_ci	"fmtvrh "#vry", %3\n"
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci#define STW_FPU_REGS(a, b, c, d)	\
1388c2ecf20Sopenharmony_ci	"stw    %0, (%4, "#a")\n"	\
1398c2ecf20Sopenharmony_ci	"stw    %1, (%4, "#b")\n"	\
1408c2ecf20Sopenharmony_ci	"stw    %2, (%4, "#c")\n"	\
1418c2ecf20Sopenharmony_ci	"stw    %3, (%4, "#d")\n"
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci#define LDW_FPU_REGS(a, b, c, d)	\
1448c2ecf20Sopenharmony_ci	"ldw    %0, (%4, "#a")\n"	\
1458c2ecf20Sopenharmony_ci	"ldw    %1, (%4, "#b")\n"	\
1468c2ecf20Sopenharmony_ci	"ldw    %2, (%4, "#c")\n"	\
1478c2ecf20Sopenharmony_ci	"ldw    %3, (%4, "#d")\n"
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_civoid save_to_user_fp(struct user_fp *user_fp)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	unsigned long flg;
1528c2ecf20Sopenharmony_ci	unsigned long tmp1, tmp2;
1538c2ecf20Sopenharmony_ci	unsigned long *fpregs;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	local_irq_save(flg);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	tmp1 = mfcr("cr<1, 2>");
1588c2ecf20Sopenharmony_ci	tmp2 = mfcr("cr<2, 2>");
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	user_fp->fcr = tmp1;
1618c2ecf20Sopenharmony_ci	user_fp->fesr = tmp2;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	fpregs = &user_fp->vr[0];
1648c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_HAS_FPUV2
1658c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_HAS_VDSP
1668c2ecf20Sopenharmony_ci	asm volatile(
1678c2ecf20Sopenharmony_ci		"vstmu.32    vr0-vr3,   (%0)\n"
1688c2ecf20Sopenharmony_ci		"vstmu.32    vr4-vr7,   (%0)\n"
1698c2ecf20Sopenharmony_ci		"vstmu.32    vr8-vr11,  (%0)\n"
1708c2ecf20Sopenharmony_ci		"vstmu.32    vr12-vr15, (%0)\n"
1718c2ecf20Sopenharmony_ci		"fstmu.64    vr16-vr31, (%0)\n"
1728c2ecf20Sopenharmony_ci		: "+a"(fpregs)
1738c2ecf20Sopenharmony_ci		::"memory");
1748c2ecf20Sopenharmony_ci#else
1758c2ecf20Sopenharmony_ci	asm volatile(
1768c2ecf20Sopenharmony_ci		"fstmu.64    vr0-vr31,  (%0)\n"
1778c2ecf20Sopenharmony_ci		: "+a"(fpregs)
1788c2ecf20Sopenharmony_ci		::"memory");
1798c2ecf20Sopenharmony_ci#endif
1808c2ecf20Sopenharmony_ci#else
1818c2ecf20Sopenharmony_ci	{
1828c2ecf20Sopenharmony_ci	unsigned long tmp3, tmp4;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	asm volatile(
1858c2ecf20Sopenharmony_ci		FMFVR_FPU_REGS(vr0, vr1)
1868c2ecf20Sopenharmony_ci		STW_FPU_REGS(0, 4, 16, 20)
1878c2ecf20Sopenharmony_ci		FMFVR_FPU_REGS(vr2, vr3)
1888c2ecf20Sopenharmony_ci		STW_FPU_REGS(32, 36, 48, 52)
1898c2ecf20Sopenharmony_ci		FMFVR_FPU_REGS(vr4, vr5)
1908c2ecf20Sopenharmony_ci		STW_FPU_REGS(64, 68, 80, 84)
1918c2ecf20Sopenharmony_ci		FMFVR_FPU_REGS(vr6, vr7)
1928c2ecf20Sopenharmony_ci		STW_FPU_REGS(96, 100, 112, 116)
1938c2ecf20Sopenharmony_ci		"addi	%4, 128\n"
1948c2ecf20Sopenharmony_ci		FMFVR_FPU_REGS(vr8, vr9)
1958c2ecf20Sopenharmony_ci		STW_FPU_REGS(0, 4, 16, 20)
1968c2ecf20Sopenharmony_ci		FMFVR_FPU_REGS(vr10, vr11)
1978c2ecf20Sopenharmony_ci		STW_FPU_REGS(32, 36, 48, 52)
1988c2ecf20Sopenharmony_ci		FMFVR_FPU_REGS(vr12, vr13)
1998c2ecf20Sopenharmony_ci		STW_FPU_REGS(64, 68, 80, 84)
2008c2ecf20Sopenharmony_ci		FMFVR_FPU_REGS(vr14, vr15)
2018c2ecf20Sopenharmony_ci		STW_FPU_REGS(96, 100, 112, 116)
2028c2ecf20Sopenharmony_ci		: "=a"(tmp1), "=a"(tmp2), "=a"(tmp3),
2038c2ecf20Sopenharmony_ci		  "=a"(tmp4), "+a"(fpregs)
2048c2ecf20Sopenharmony_ci		::"memory");
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci#endif
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	local_irq_restore(flg);
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_civoid restore_from_user_fp(struct user_fp *user_fp)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	unsigned long flg;
2148c2ecf20Sopenharmony_ci	unsigned long tmp1, tmp2;
2158c2ecf20Sopenharmony_ci	unsigned long *fpregs;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	local_irq_save(flg);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	tmp1 = user_fp->fcr;
2208c2ecf20Sopenharmony_ci	tmp2 = user_fp->fesr;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	mtcr("cr<1, 2>", tmp1);
2238c2ecf20Sopenharmony_ci	mtcr("cr<2, 2>", tmp2);
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	fpregs = &user_fp->vr[0];
2268c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_HAS_FPUV2
2278c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_HAS_VDSP
2288c2ecf20Sopenharmony_ci	asm volatile(
2298c2ecf20Sopenharmony_ci		"vldmu.32    vr0-vr3,   (%0)\n"
2308c2ecf20Sopenharmony_ci		"vldmu.32    vr4-vr7,   (%0)\n"
2318c2ecf20Sopenharmony_ci		"vldmu.32    vr8-vr11,  (%0)\n"
2328c2ecf20Sopenharmony_ci		"vldmu.32    vr12-vr15, (%0)\n"
2338c2ecf20Sopenharmony_ci		"fldmu.64    vr16-vr31, (%0)\n"
2348c2ecf20Sopenharmony_ci		: "+a"(fpregs)
2358c2ecf20Sopenharmony_ci		::"memory");
2368c2ecf20Sopenharmony_ci#else
2378c2ecf20Sopenharmony_ci	asm volatile(
2388c2ecf20Sopenharmony_ci		"fldmu.64    vr0-vr31,  (%0)\n"
2398c2ecf20Sopenharmony_ci		: "+a"(fpregs)
2408c2ecf20Sopenharmony_ci		::"memory");
2418c2ecf20Sopenharmony_ci#endif
2428c2ecf20Sopenharmony_ci#else
2438c2ecf20Sopenharmony_ci	{
2448c2ecf20Sopenharmony_ci	unsigned long tmp3, tmp4;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	asm volatile(
2478c2ecf20Sopenharmony_ci		LDW_FPU_REGS(0, 4, 16, 20)
2488c2ecf20Sopenharmony_ci		FMTVR_FPU_REGS(vr0, vr1)
2498c2ecf20Sopenharmony_ci		LDW_FPU_REGS(32, 36, 48, 52)
2508c2ecf20Sopenharmony_ci		FMTVR_FPU_REGS(vr2, vr3)
2518c2ecf20Sopenharmony_ci		LDW_FPU_REGS(64, 68, 80, 84)
2528c2ecf20Sopenharmony_ci		FMTVR_FPU_REGS(vr4, vr5)
2538c2ecf20Sopenharmony_ci		LDW_FPU_REGS(96, 100, 112, 116)
2548c2ecf20Sopenharmony_ci		FMTVR_FPU_REGS(vr6, vr7)
2558c2ecf20Sopenharmony_ci		"addi	%4, 128\n"
2568c2ecf20Sopenharmony_ci		LDW_FPU_REGS(0, 4, 16, 20)
2578c2ecf20Sopenharmony_ci		FMTVR_FPU_REGS(vr8, vr9)
2588c2ecf20Sopenharmony_ci		LDW_FPU_REGS(32, 36, 48, 52)
2598c2ecf20Sopenharmony_ci		FMTVR_FPU_REGS(vr10, vr11)
2608c2ecf20Sopenharmony_ci		LDW_FPU_REGS(64, 68, 80, 84)
2618c2ecf20Sopenharmony_ci		FMTVR_FPU_REGS(vr12, vr13)
2628c2ecf20Sopenharmony_ci		LDW_FPU_REGS(96, 100, 112, 116)
2638c2ecf20Sopenharmony_ci		FMTVR_FPU_REGS(vr14, vr15)
2648c2ecf20Sopenharmony_ci		: "=a"(tmp1), "=a"(tmp2), "=a"(tmp3),
2658c2ecf20Sopenharmony_ci		  "=a"(tmp4), "+a"(fpregs)
2668c2ecf20Sopenharmony_ci		::"memory");
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci#endif
2698c2ecf20Sopenharmony_ci	local_irq_restore(flg);
2708c2ecf20Sopenharmony_ci}
271