Commit c23e7f01 authored by WANG Xuerui's avatar WANG Xuerui Committed by Huacai Chen

LoongArch: Relay BCE exceptions to userland as SIGSEGV with si_code=SEGV_BNDERR

SEGV_BNDERR was introduced initially for supporting the Intel MPX, but
fell into disuse after the MPX support was removed. The LoongArch
bounds-checking instructions behave very differently than MPX, but
overall the interface is still kind of suitable for conveying the
information to userland when bounds-checking assertions trigger, so we
wouldn't have to invent more UAPI. Specifically, when the BCE triggers,
a SEGV_BNDERR is sent to userland, with si_addr set to the out-of-bounds
address or value (in asrt{gt,le}'s case), and one of si_lower or
si_upper set to the configured bound depending on the faulting
instruction. The other bound is set to either 0 or ULONG_MAX to resemble
a range with both lower and upper bounds.

Note that it is possible to have si_addr == si_lower in case of a
failing asrtgt or {ld,st}gt, because those instructions test for strict
greater-than relationship. This should not pose a problem for userland,
though, because the faulting PC is available for the application to
associate back to the exact instruction for figuring out the
expectation.

Example exception context generated by a faulting `asrtgt.d t0, t1`
(assert t0 > t1 or BCE) with t0=100 and t1=200:

> pc 00005555558206a4 ra 00007ffff2d854fc tp 00007ffff2f2f180 sp 00007ffffbf9fb80
> a0 0000000000000002 a1 00007ffffbf9fce8 a2 00007ffffbf9fd00 a3 00007ffff2ed4558
> a4 0000000000000000 a5 00007ffff2f044c8 a6 00007ffffbf9fce0 a7 fffffffffffff000
> t0 0000000000000064 t1 00000000000000c8 t2 00007ffffbfa2d5e t3 00007ffff2f12aa0
> t4 00007ffff2ed6158 t5 00007ffff2ed6158 t6 000000000000002e t7 0000000003d8f538
> t8 0000000000000005 u0 0000000000000000 s9 0000000000000000 s0 00007ffffbf9fce8
> s1 0000000000000002 s2 0000000000000000 s3 00007ffff2f2c038 s4 0000555555820610
> s5 00007ffff2ed5000 s6 0000555555827e38 s7 00007ffffbf9fd00 s8 0000555555827e38
>    ra: 00007ffff2d854fc
>   ERA: 00005555558206a4
>  CRMD: 000000b0 (PLV0 -IE -DA +PG DACF=CC DACM=CC -WE)
>  PRMD: 00000007 (PPLV3 +PIE -PWE)
>  EUEN: 00000000 (-FPE -SXE -ASXE -BTE)
>  ECFG: 0007181c (LIE=2-4,11-12 VS=7)
> ESTAT: 000a0000 [BCE] (IS= ECode=10 EsubCode=0)
>  PRID: 0014c010 (Loongson-64bit, Loongson-3A5000)
Signed-off-by: default avatarWANG Xuerui <git@xen0n.name>
Signed-off-by: default avatarHuacai Chen <chenhuacai@loongson.cn>
parent 325a38b5
......@@ -121,6 +121,8 @@ enum reg2bstrd_op {
};
enum reg3_op {
asrtle_op = 0x02,
asrtgt_op = 0x03,
addw_op = 0x20,
addd_op = 0x21,
subw_op = 0x22,
......@@ -176,6 +178,30 @@ enum reg3_op {
amord_op = 0x70c7,
amxorw_op = 0x70c8,
amxord_op = 0x70c9,
fldgts_op = 0x70e8,
fldgtd_op = 0x70e9,
fldles_op = 0x70ea,
fldled_op = 0x70eb,
fstgts_op = 0x70ec,
fstgtd_op = 0x70ed,
fstles_op = 0x70ee,
fstled_op = 0x70ef,
ldgtb_op = 0x70f0,
ldgth_op = 0x70f1,
ldgtw_op = 0x70f2,
ldgtd_op = 0x70f3,
ldleb_op = 0x70f4,
ldleh_op = 0x70f5,
ldlew_op = 0x70f6,
ldled_op = 0x70f7,
stgtb_op = 0x70f8,
stgth_op = 0x70f9,
stgtw_op = 0x70fa,
stgtd_op = 0x70fb,
stleb_op = 0x70fc,
stleh_op = 0x70fd,
stlew_op = 0x70fe,
stled_op = 0x70ff,
};
enum reg3sa2_op {
......
......@@ -82,6 +82,7 @@ SYM_FUNC_END(except_vec_cex)
BUILD_HANDLER ade ade badv
BUILD_HANDLER ale ale badv
BUILD_HANDLER bce bce none
BUILD_HANDLER bp bp none
BUILD_HANDLER fpe fpe fcsr
BUILD_HANDLER fpu fpu none
......
......@@ -36,6 +36,7 @@
#include <asm/break.h>
#include <asm/cpu.h>
#include <asm/fpu.h>
#include <asm/inst.h>
#include <asm/loongarch.h>
#include <asm/mmu_context.h>
#include <asm/pgtable.h>
......@@ -51,6 +52,7 @@
extern asmlinkage void handle_ade(void);
extern asmlinkage void handle_ale(void);
extern asmlinkage void handle_bce(void);
extern asmlinkage void handle_sys(void);
extern asmlinkage void handle_bp(void);
extern asmlinkage void handle_ri(void);
......@@ -588,6 +590,95 @@ static void bug_handler(struct pt_regs *regs)
}
}
asmlinkage void noinstr do_bce(struct pt_regs *regs)
{
bool user = user_mode(regs);
unsigned long era = exception_era(regs);
u64 badv = 0, lower = 0, upper = ULONG_MAX;
union loongarch_instruction insn;
irqentry_state_t state = irqentry_enter(regs);
if (regs->csr_prmd & CSR_PRMD_PIE)
local_irq_enable();
current->thread.trap_nr = read_csr_excode();
die_if_kernel("Bounds check error in kernel code", regs);
/*
* Pull out the address that failed bounds checking, and the lower /
* upper bound, by minimally looking at the faulting instruction word
* and reading from the correct register.
*/
if (__get_inst(&insn.word, (u32 *)era, user))
goto bad_era;
switch (insn.reg3_format.opcode) {
case asrtle_op:
if (insn.reg3_format.rd != 0)
break; /* not asrtle */
badv = regs->regs[insn.reg3_format.rj];
upper = regs->regs[insn.reg3_format.rk];
break;
case asrtgt_op:
if (insn.reg3_format.rd != 0)
break; /* not asrtgt */
badv = regs->regs[insn.reg3_format.rj];
lower = regs->regs[insn.reg3_format.rk];
break;
case ldleb_op:
case ldleh_op:
case ldlew_op:
case ldled_op:
case stleb_op:
case stleh_op:
case stlew_op:
case stled_op:
case fldles_op:
case fldled_op:
case fstles_op:
case fstled_op:
badv = regs->regs[insn.reg3_format.rj];
upper = regs->regs[insn.reg3_format.rk];
break;
case ldgtb_op:
case ldgth_op:
case ldgtw_op:
case ldgtd_op:
case stgtb_op:
case stgth_op:
case stgtw_op:
case stgtd_op:
case fldgts_op:
case fldgtd_op:
case fstgts_op:
case fstgtd_op:
badv = regs->regs[insn.reg3_format.rj];
lower = regs->regs[insn.reg3_format.rk];
break;
}
force_sig_bnderr((void __user *)badv, (void __user *)lower, (void __user *)upper);
out:
if (regs->csr_prmd & CSR_PRMD_PIE)
local_irq_disable();
irqentry_exit(regs, state);
return;
bad_era:
/*
* Cannot pull out the instruction word, hence cannot provide more
* info than a regular SIGSEGV in this case.
*/
force_sig(SIGSEGV);
goto out;
}
asmlinkage void noinstr do_bp(struct pt_regs *regs)
{
bool user = user_mode(regs);
......@@ -955,6 +1046,7 @@ void __init trap_init(void)
set_handler(EXCCODE_ADE * VECSIZE, handle_ade, VECSIZE);
set_handler(EXCCODE_ALE * VECSIZE, handle_ale, VECSIZE);
set_handler(EXCCODE_BCE * VECSIZE, handle_bce, VECSIZE);
set_handler(EXCCODE_SYS * VECSIZE, handle_sys, VECSIZE);
set_handler(EXCCODE_BP * VECSIZE, handle_bp, VECSIZE);
set_handler(EXCCODE_INE * VECSIZE, handle_ri, VECSIZE);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment