summaryrefslogtreecommitdiff
path: root/arch/loongarch/mm
diff options
context:
space:
mode:
authorYouling Tang <tangyouling@loongson.cn>2022-12-10 17:39:59 +0300
committerHuacai Chen <chenhuacai@loongson.cn>2022-12-14 03:36:11 +0300
commit672999cfae3e830a64c4996362a26934fd555ff9 (patch)
tree7f271c51048e7673a165e7f818e9755884686d76 /arch/loongarch/mm
parent26bc82441250f2e01621f5b26606a4f6926ee3ad (diff)
downloadlinux-672999cfae3e830a64c4996362a26934fd555ff9.tar.xz
LoongArch: extable: Add a dedicated uaccess handler
Inspired by commit 2e77a62cb3a6("arm64: extable: add a dedicated uaccess handler"), do similar to LoongArch to add a dedicated uaccess exception handler to update registers in exception context and subsequently return back into the function which faulted, so we remove the need for fixups specialized to each faulting instruction. Add gpr-num.h here because we need to map the same GPR names to integer constants, so that we can use this to build meta-data for the exception fixups. The compiler treats gpr 0 as zero rather than $r0, so set it separately to .L__gpr_num_zero, otherwise the following assembly error will occurs: {standard input}: Assembler messages: {standard input}:1074: Error: invalid operands (*UND* and *ABS* sections) for `<<' {standard input}:1160: Error: invalid operands (*UND* and *ABS* sections) for `<<' make[1]: *** [scripts/Makefile.build:249: fs/fcntl.o] Error 1 Signed-off-by: Youling Tang <tangyouling@loongson.cn> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
Diffstat (limited to 'arch/loongarch/mm')
-rw-r--r--arch/loongarch/mm/extable.c22
1 files changed, 22 insertions, 0 deletions
diff --git a/arch/loongarch/mm/extable.c b/arch/loongarch/mm/extable.c
index fd2395221cff..9b0cfd898940 100644
--- a/arch/loongarch/mm/extable.c
+++ b/arch/loongarch/mm/extable.c
@@ -2,6 +2,7 @@
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
+#include <linux/bitfield.h>
#include <linux/extable.h>
#include <linux/uaccess.h>
#include <asm/asm-extable.h>
@@ -13,6 +14,13 @@ get_ex_fixup(const struct exception_table_entry *ex)
return ((unsigned long)&ex->fixup + ex->fixup);
}
+static inline void regs_set_gpr(struct pt_regs *regs,
+ unsigned int offset, unsigned long val)
+{
+ if (offset && offset <= MAX_REG_OFFSET)
+ *(unsigned long *)((unsigned long)regs + offset) = val;
+}
+
static bool ex_handler_fixup(const struct exception_table_entry *ex,
struct pt_regs *regs)
{
@@ -21,6 +29,18 @@ static bool ex_handler_fixup(const struct exception_table_entry *ex,
return true;
}
+static bool ex_handler_uaccess_err_zero(const struct exception_table_entry *ex,
+ struct pt_regs *regs)
+{
+ int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data);
+ int reg_zero = FIELD_GET(EX_DATA_REG_ZERO, ex->data);
+
+ regs_set_gpr(regs, reg_err * sizeof(unsigned long), -EFAULT);
+ regs_set_gpr(regs, reg_zero * sizeof(unsigned long), 0);
+ regs->csr_era = get_ex_fixup(ex);
+
+ return true;
+}
bool fixup_exception(struct pt_regs *regs)
{
@@ -33,6 +53,8 @@ bool fixup_exception(struct pt_regs *regs)
switch (ex->type) {
case EX_TYPE_FIXUP:
return ex_handler_fixup(ex, regs);
+ case EX_TYPE_UACCESS_ERR_ZERO:
+ return ex_handler_uaccess_err_zero(ex, regs);
}
BUG();