# HG changeset patch # User Taylor R Campbell # Date 1788363676 0 # Wed Sep 02 15:41:16 2026 +0000 # Branch trunk # Node ID 96423e8d168f3d8baf9fa19743b2832b89aed9ac # Parent 4fbd288ac1da9e7d9592ddecf04f7513e244152f # EXP-Topic riastradh-pr60664-rdhwrtlbmiss WIP: mips: Emulate rdhwr $3,$29 with fewer temporary registers. This way, if there is a TLB exception when loading the user's instruction, there's no harm in having the TLB exception handler return straight to the user's instruction to retry it -- we haven't saved any registers in the kernel stack frame (previously, v0 and AT) that need to be restored before userland can continue. PR port-mips/60664: mips: rdhwr emulation corrupts v0 on nested TLB miss, random userland SIGSEGV diff -r 4fbd288ac1da -r 96423e8d168f sys/arch/mips/mips/mipsX_subr.S --- a/sys/arch/mips/mips/mipsX_subr.S Mon Aug 10 13:36:10 2026 +0000 +++ b/sys/arch/mips/mips/mipsX_subr.S Wed Sep 02 15:41:16 2026 +0000 @@ -1295,60 +1295,93 @@ 5: bne v0, s1, 5b END(MIPSX(kern_intr)) /* + * user_reserved_insn * + * Handle a reserved instruction exception. We implement a fast + * path for rdhwr $3,$29 or rdhwr v1,$29 (0x7c03e83b), the + * instruction in the ABI to read the thread-local storage + * pointer, whether or not the CPU supports it. Any other + * instructions, we handle by user_gen_exception. */ .p2align 5 NESTED_NOPROFILE(MIPSX(user_reserved_insn), CALLFRAME_SIZ, ra) .set noat .mask 0x80000000, -4 + KERN_ENTRY_ERRATA + /* - * Save a minimum of registers to see if this is rdhwr $3,$29 + * On entry: + * - EXL is set, + * - k0 is garbage, + * - k1 is curlwp, + * - EPC is the address of the reserved instruction, + * and all other general-purpose CPU registers are potentially + * used by the user program, so we would have to save and + * restore them in order to use them. + + * We can reuse k1 at the cost of: + * + * lui k1,%hi(...) + * PTR_L k1,%lo(...)(k1). + * + * However, restoring saved registers is not trivial because of + * TLB exceptions. + * + * We have to load the instruction from EPC to discern whether + * it is rdhwr $3,$29 or something else. But the load may + * provoke a TLB refill/invalid exception. Since the EXL bit + * remains set, if this happens, the CPU will jump to the + * exception vector again with EPC _still pointing to the + * reserved instruction in userland_ -- which means we don't + * have an opportunity to restore any registers we saved. To + * avoid this, we would have to use TLBP here to avoid the + * TLB exceptions altogether, much messier. */ - KERN_ENTRY_ERRATA - /* K1 already has CURLWP */ - PTR_L k0, L_PCB(k1) # XXXuvm_lwp_getuarea - PTR_ADDU k0, USPACE - TF_SIZ - CALLFRAME_SIZ - - /* Need two working registers */ - REG_S AT, CALLFRAME_SIZ+TF_REG_AST(k0) - REG_S v0, CALLFRAME_SIZ+TF_REG_V0(k0) /* If this was in a branch delay slot, take the slow path. */ - mfc0 v0, MIPS_COP_0_CAUSE + _MFC0 k0, MIPS_COP_0_CAUSE + _MFC0 k1, MIPS_COP_0_EXC_PC MFC0_HAZARD - bltz v0, MIPSX(user_gen_exception_common) - nop + bltz k0, 1f + + /* If *EPC != 0x7c03e83b (rdhwr $3,$29), take the slow path. */ + INT_L k1, 0(k1) + lui k0, %lo(0x7c03e83b) + addiu k0, %hi(0x7c03e83b) + bne k0, k1, 1f /* - * Get exception PC and fetch the instruction. We know we can do - * this since the instruction actually got read. + * We have now confirmed the instruction is rdhwr $3,$29, + * i.e., rdhwr v1,$29, so it is safe to use v1 now -- not + * that we have any use for v1 except to hold the result. */ - _MFC0 v0, MIPS_COP_0_EXC_PC - MFC0_HAZARD - INT_L AT, 0(v0) - /* - * Was this rdhwr $3,$29? - */ - lui v0, %hi(0x7c03e83b) # 0x7c03e83b => rdhwr $3,$29 - addiu v0, %lo(0x7c03e83b) # or ... rdhwr v1,ulr - bne AT, v0, MIPSX(user_gen_exception_common) - nop + /* Load EPC into k0 again so we can advance to EPC+4. */ + _MFC0 k0, MIPS_COP_0_EXC_PC + MFC0_HAZARD + + /* Load curlwp into k1 again. */ + lui k1, %hi(CPUVAR(CURLWP)) + PTR_L k1, %lo(CPUVAR(CURLWP))(k1) /* * Advance the PC (don't want to restart at the rdhwr). */ - _MFC0 v0, MIPS_COP_0_EXC_PC - MFC0_HAZARD - PTR_ADDIU v0, 4 - _MTC0 v0, MIPS_COP_0_EXC_PC + PTR_ADDIU k0, 4 + _MTC0 k0, MIPS_COP_0_EXC_PC COP0_SYNC PTR_L v1, L_PRIVATE(k1) # rdhwr $3,$29 updates v1 - REG_L AT, CALLFRAME_SIZ+TF_REG_AST(k0)# restore reg - REG_L v0, CALLFRAME_SIZ+TF_REG_V0(k0) # restore reg eret + + /* + * Slow path. Load curlwp back into k1 and defer to + * user_gen_exception. + */ +1: lui k1, %hi(CPUVAR(CURLWP)) + b MIPSX(user_gen_exception) + PTR_L k1, %lo(CPUVAR(CURLWP))(k1) END(MIPSX(user_reserved_insn)) /* @@ -1372,7 +1405,6 @@ NESTED_NOPROFILE(MIPSX(user_gen_exceptio PTR_ADDU k0, USPACE - TF_SIZ - CALLFRAME_SIZ REG_S AT, CALLFRAME_SIZ+TF_REG_AST(k0) REG_S v0, CALLFRAME_SIZ+TF_REG_V0(k0) -MIPSX(user_gen_exception_common): REG_S v1, CALLFRAME_SIZ+TF_REG_V1(k0) mflo v0 REG_S a0, CALLFRAME_SIZ+TF_REG_A0(k0)