Skip to content

Commit c23e7f0

Browse files
xen0nchenhuacai
authored andcommitted
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: WANG Xuerui <git@xen0n.name> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
1 parent 325a38b commit c23e7f0

3 files changed

Lines changed: 119 additions & 0 deletions

File tree

arch/loongarch/include/asm/inst.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ enum reg2bstrd_op {
121121
};
122122

123123
enum reg3_op {
124+
asrtle_op = 0x02,
125+
asrtgt_op = 0x03,
124126
addw_op = 0x20,
125127
addd_op = 0x21,
126128
subw_op = 0x22,
@@ -176,6 +178,30 @@ enum reg3_op {
176178
amord_op = 0x70c7,
177179
amxorw_op = 0x70c8,
178180
amxord_op = 0x70c9,
181+
fldgts_op = 0x70e8,
182+
fldgtd_op = 0x70e9,
183+
fldles_op = 0x70ea,
184+
fldled_op = 0x70eb,
185+
fstgts_op = 0x70ec,
186+
fstgtd_op = 0x70ed,
187+
fstles_op = 0x70ee,
188+
fstled_op = 0x70ef,
189+
ldgtb_op = 0x70f0,
190+
ldgth_op = 0x70f1,
191+
ldgtw_op = 0x70f2,
192+
ldgtd_op = 0x70f3,
193+
ldleb_op = 0x70f4,
194+
ldleh_op = 0x70f5,
195+
ldlew_op = 0x70f6,
196+
ldled_op = 0x70f7,
197+
stgtb_op = 0x70f8,
198+
stgth_op = 0x70f9,
199+
stgtw_op = 0x70fa,
200+
stgtd_op = 0x70fb,
201+
stleb_op = 0x70fc,
202+
stleh_op = 0x70fd,
203+
stlew_op = 0x70fe,
204+
stled_op = 0x70ff,
179205
};
180206

181207
enum reg3sa2_op {

arch/loongarch/kernel/genex.S

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ SYM_FUNC_END(except_vec_cex)
8282

8383
BUILD_HANDLER ade ade badv
8484
BUILD_HANDLER ale ale badv
85+
BUILD_HANDLER bce bce none
8586
BUILD_HANDLER bp bp none
8687
BUILD_HANDLER fpe fpe fcsr
8788
BUILD_HANDLER fpu fpu none

arch/loongarch/kernel/traps.c

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <asm/break.h>
3737
#include <asm/cpu.h>
3838
#include <asm/fpu.h>
39+
#include <asm/inst.h>
3940
#include <asm/loongarch.h>
4041
#include <asm/mmu_context.h>
4142
#include <asm/pgtable.h>
@@ -51,6 +52,7 @@
5152

5253
extern asmlinkage void handle_ade(void);
5354
extern asmlinkage void handle_ale(void);
55+
extern asmlinkage void handle_bce(void);
5456
extern asmlinkage void handle_sys(void);
5557
extern asmlinkage void handle_bp(void);
5658
extern asmlinkage void handle_ri(void);
@@ -588,6 +590,95 @@ static void bug_handler(struct pt_regs *regs)
588590
}
589591
}
590592

593+
asmlinkage void noinstr do_bce(struct pt_regs *regs)
594+
{
595+
bool user = user_mode(regs);
596+
unsigned long era = exception_era(regs);
597+
u64 badv = 0, lower = 0, upper = ULONG_MAX;
598+
union loongarch_instruction insn;
599+
irqentry_state_t state = irqentry_enter(regs);
600+
601+
if (regs->csr_prmd & CSR_PRMD_PIE)
602+
local_irq_enable();
603+
604+
current->thread.trap_nr = read_csr_excode();
605+
606+
die_if_kernel("Bounds check error in kernel code", regs);
607+
608+
/*
609+
* Pull out the address that failed bounds checking, and the lower /
610+
* upper bound, by minimally looking at the faulting instruction word
611+
* and reading from the correct register.
612+
*/
613+
if (__get_inst(&insn.word, (u32 *)era, user))
614+
goto bad_era;
615+
616+
switch (insn.reg3_format.opcode) {
617+
case asrtle_op:
618+
if (insn.reg3_format.rd != 0)
619+
break; /* not asrtle */
620+
badv = regs->regs[insn.reg3_format.rj];
621+
upper = regs->regs[insn.reg3_format.rk];
622+
break;
623+
624+
case asrtgt_op:
625+
if (insn.reg3_format.rd != 0)
626+
break; /* not asrtgt */
627+
badv = regs->regs[insn.reg3_format.rj];
628+
lower = regs->regs[insn.reg3_format.rk];
629+
break;
630+
631+
case ldleb_op:
632+
case ldleh_op:
633+
case ldlew_op:
634+
case ldled_op:
635+
case stleb_op:
636+
case stleh_op:
637+
case stlew_op:
638+
case stled_op:
639+
case fldles_op:
640+
case fldled_op:
641+
case fstles_op:
642+
case fstled_op:
643+
badv = regs->regs[insn.reg3_format.rj];
644+
upper = regs->regs[insn.reg3_format.rk];
645+
break;
646+
647+
case ldgtb_op:
648+
case ldgth_op:
649+
case ldgtw_op:
650+
case ldgtd_op:
651+
case stgtb_op:
652+
case stgth_op:
653+
case stgtw_op:
654+
case stgtd_op:
655+
case fldgts_op:
656+
case fldgtd_op:
657+
case fstgts_op:
658+
case fstgtd_op:
659+
badv = regs->regs[insn.reg3_format.rj];
660+
lower = regs->regs[insn.reg3_format.rk];
661+
break;
662+
}
663+
664+
force_sig_bnderr((void __user *)badv, (void __user *)lower, (void __user *)upper);
665+
666+
out:
667+
if (regs->csr_prmd & CSR_PRMD_PIE)
668+
local_irq_disable();
669+
670+
irqentry_exit(regs, state);
671+
return;
672+
673+
bad_era:
674+
/*
675+
* Cannot pull out the instruction word, hence cannot provide more
676+
* info than a regular SIGSEGV in this case.
677+
*/
678+
force_sig(SIGSEGV);
679+
goto out;
680+
}
681+
591682
asmlinkage void noinstr do_bp(struct pt_regs *regs)
592683
{
593684
bool user = user_mode(regs);
@@ -955,6 +1046,7 @@ void __init trap_init(void)
9551046

9561047
set_handler(EXCCODE_ADE * VECSIZE, handle_ade, VECSIZE);
9571048
set_handler(EXCCODE_ALE * VECSIZE, handle_ale, VECSIZE);
1049+
set_handler(EXCCODE_BCE * VECSIZE, handle_bce, VECSIZE);
9581050
set_handler(EXCCODE_SYS * VECSIZE, handle_sys, VECSIZE);
9591051
set_handler(EXCCODE_BP * VECSIZE, handle_bp, VECSIZE);
9601052
set_handler(EXCCODE_INE * VECSIZE, handle_ri, VECSIZE);

0 commit comments

Comments
 (0)