// SPDX-License-Identifier: GPL-2.0
/*
* Author: Hanlu Li <lihanlu@loongson.cn>
* Huacai Chen <chenhuacai@loongson.cn>
*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*
* Derived from MIPS:
* Copyright (C) 1992 Ross Biro
* Copyright (C) Linus Torvalds
* Copyright (C) 1994, 95, 96, 97, 98, 2000 Ralf Baechle
* Copyright (C) 1996 David S. Miller
* Kevin D. Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
* Copyright (C) 1999 MIPS Technologies, Inc.
* Copyright (C) 2000 Ulf Carlsson
*/
#include <linux/kernel.h>
#include <linux/audit.h>
#include <linux/compiler.h>
#include <linux/context_tracking.h>
#include <linux/elf.h>
#include <linux/errno.h>
#include <linux/hw_breakpoint.h>
#include <linux/mm.h>
#include <linux/nospec.h>
#include <linux/ptrace.h>
#include <linux/regset.h>
#include <linux/sched.h>
#include <linux/sched/task_stack.h>
#include <linux/security.h>
#include <linux/smp.h>
#include <linux/stddef.h>
#include <linux/seccomp.h>
#include <linux/thread_info.h>
#include <linux/uaccess.h>
#include <asm/byteorder.h>
#include <asm/cpu.h>
#include <asm/cpu-info.h>
#include <asm/fpu.h>
#include <asm/lbt.h>
#include <asm/loongarch.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#include <asm/ptrace.h>
#include <asm/reg.h>
#include <asm/syscall.h>
static void init_fp_ctx(struct task_struct *target)
{
/* The target already has context */
if (tsk_used_math(target))
return;
/* Begin with data registers set to all 1s... */
memset(&target->thread.fpu.fpr, ~0, sizeof(target->thread.fpu.fpr));
set_stopped_child_used_math(target);
}
/*
* Called by kernel/ptrace.c when detaching..
*
* Make sure single step bits etc are not set.
*/
void ptrace_disable(struct task_struct *child)
{
/* Don't load the watchpoint registers for the ex-child. */
clear_tsk_thread_flag(child, TIF_LOAD_WATCH);
clear_tsk_thread_flag(child, TIF_SINGLESTEP);
}
/* regset get/set implementations */
static int gpr_get(struct task_struct *target,
const struct user_regset *regset,
struct membuf to)
{
int r;
struct pt_regs *regs = task_pt_regs(target);
r = membuf_write(&to, ®s->regs, sizeof(u64) * GPR_NUM);
r = membuf_write(&to, ®s->orig_a0, sizeof(u64));
r = membuf_write(&to, ®s->csr_era, sizeof(u64));
r = membuf_write(&to, ®s->csr_badvaddr, sizeof(u64));
return r;
}
static int gpr_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
{
int err;
int a0_start = sizeof(u64) * GPR_NUM;
int era_start = a0_start + sizeof(u64);
int badvaddr_start = era_start + sizeof(u64);
struct pt_regs *regs = task_pt_regs(target);
err = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
®s->regs,
0, a0_start);
err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf,
®s->orig_a0,
a0_start, a0_start + sizeof(u64));
err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf,
®s->csr_era,
era_start, era_start + sizeof(u64));
err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf,
®s->csr_badvaddr,
badvaddr_start, badvaddr_start + sizeof(u64));
return err;
}
/*
* Get the general floating-point registers.
*/
static int gfpr_get(struct task_struct *target, struct membuf *to)
{
return membuf_write(to, &target->thread.fpu.fpr,
sizeof(elf_fpreg_t) * NUM_FPU_REGS);
}
static int gfpr_get_simd(struct task_struct *target, struct membuf *to)
{
int i, r;
u64 fpr_val;
BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t));
for (i = 0; i < NUM_FPU_REGS; i++) {
fpr_val = get_fpr64(&target->thread.fpu.fpr[i], 0);
r = membuf_write(to, &fpr_val, sizeof(elf_fpreg_t));
}
return r;
}
/*
* Choose the appropriate helper for general registers, and then copy
* the FCC and FCSR registers separately.
*/
static int fpr_get(struct task_struct *target,
const struct user_regset *regset,
struct membuf to)
{
int r;
save_fpu_regs(target);
if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t))
r