2026-08-20 23:57:10 +02:00
|
|
|
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
|
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
|
|
|
|
|
|
//go:build linux && loong64
|
|
|
|
|
|
|
|
|
|
package debug
|
|
|
|
|
|
|
|
|
|
// Regs holds the full general-purpose register set of a traced process
|
|
|
|
|
// (the Linux loong64 user_pt_regs layout).
|
|
|
|
|
type Regs struct {
|
|
|
|
|
R0 uint64 // zero
|
|
|
|
|
R1 uint64 // RA (return address)
|
|
|
|
|
R2 uint64 // TP (thread pointer)
|
|
|
|
|
R3 uint64 // SP (stack pointer)
|
|
|
|
|
R4 uint64 // A0
|
|
|
|
|
R5 uint64 // A1
|
|
|
|
|
R6 uint64 // A2
|
|
|
|
|
R7 uint64 // A3
|
|
|
|
|
R8 uint64 // A4
|
|
|
|
|
R9 uint64 // A5
|
|
|
|
|
R10 uint64 // A6
|
|
|
|
|
R11 uint64 // A7
|
|
|
|
|
R12 uint64 // T0
|
|
|
|
|
R13 uint64 // T1
|
|
|
|
|
R14 uint64 // T2
|
|
|
|
|
R15 uint64 // T3
|
|
|
|
|
R16 uint64 // T4
|
|
|
|
|
R17 uint64 // T5
|
|
|
|
|
R18 uint64 // T6
|
|
|
|
|
R19 uint64 // T7
|
|
|
|
|
R20 uint64 // T8
|
|
|
|
|
R21 uint64 // FP (frame pointer)
|
|
|
|
|
R22 uint64 // S0
|
|
|
|
|
R23 uint64 // S1
|
|
|
|
|
R24 uint64 // S2
|
|
|
|
|
R25 uint64 // S3
|
|
|
|
|
R26 uint64 // S4
|
|
|
|
|
R27 uint64 // S5
|
|
|
|
|
R28 uint64 // S6
|
|
|
|
|
R29 uint64 // S7
|
|
|
|
|
R30 uint64 // S8
|
|
|
|
|
R31 uint64 // PC
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetPC returns the program counter.
|
|
|
|
|
func (r *Regs) GetPC() uint64 { return r.R31 }
|
|
|
|
|
|
|
|
|
|
// SetPC sets the program counter.
|
|
|
|
|
func (r *Regs) SetPC(pc uint64) { r.R31 = pc }
|
|
|
|
|
|
|
|
|
|
// GetSP returns the stack pointer.
|
|
|
|
|
func (r *Regs) GetSP() uint64 { return r.R3 }
|
|
|
|
|
|
|
|
|
|
// RegValue returns the value of the named register, or false if unknown.
|
|
|
|
|
func (r *Regs) RegValue(name string) (uint64, bool) {
|
|
|
|
|
switch name {
|
|
|
|
|
case "r0", "zero":
|
|
|
|
|
return r.R0, true
|
|
|
|
|
case "r1", "ra":
|
|
|
|
|
return r.R1, true
|
|
|
|
|
case "r2", "tp":
|
|
|
|
|
return r.R2, true
|
|
|
|
|
case "r3", "sp":
|
|
|
|
|
return r.R3, true
|
|
|
|
|
case "r4", "a0":
|
|
|
|
|
return r.R4, true
|
|
|
|
|
case "r5", "a1":
|
|
|
|
|
return r.R5, true
|
|
|
|
|
case "r6", "a2":
|
|
|
|
|
return r.R6, true
|
|
|
|
|
case "r7", "a3":
|
|
|
|
|
return r.R7, true
|
|
|
|
|
case "r8", "a4":
|
|
|
|
|
return r.R8, true
|
|
|
|
|
case "r9", "a5":
|
|
|
|
|
return r.R9, true
|
|
|
|
|
case "r10", "a6":
|
|
|
|
|
return r.R10, true
|
|
|
|
|
case "r11", "a7":
|
|
|
|
|
return r.R11, true
|
|
|
|
|
case "r12", "t0":
|
|
|
|
|
return r.R12, true
|
|
|
|
|
case "r13", "t1":
|
|
|
|
|
return r.R13, true
|
|
|
|
|
case "r14", "t2":
|
|
|
|
|
return r.R14, true
|
|
|
|
|
case "r15", "t3":
|
|
|
|
|
return r.R15, true
|
|
|
|
|
case "r16", "t4":
|
|
|
|
|
return r.R16, true
|
|
|
|
|
case "r17", "t5":
|
|
|
|
|
return r.R17, true
|
|
|
|
|
case "r18", "t6":
|
|
|
|
|
return r.R18, true
|
|
|
|
|
case "r19", "t7":
|
|
|
|
|
return r.R19, true
|
|
|
|
|
case "r20", "t8":
|
|
|
|
|
return r.R20, true
|
|
|
|
|
case "r21", "fp":
|
|
|
|
|
return r.R21, true
|
|
|
|
|
case "r22", "s0":
|
|
|
|
|
return r.R22, true
|
|
|
|
|
case "r23", "s1":
|
|
|
|
|
return r.R23, true
|
|
|
|
|
case "r24", "s2":
|
|
|
|
|
return r.R24, true
|
|
|
|
|
case "r25", "s3":
|
|
|
|
|
return r.R25, true
|
|
|
|
|
case "r26", "s4":
|
|
|
|
|
return r.R26, true
|
|
|
|
|
case "r27", "s5":
|
|
|
|
|
return r.R27, true
|
|
|
|
|
case "r28", "s6":
|
|
|
|
|
return r.R28, true
|
|
|
|
|
case "r29", "s7":
|
|
|
|
|
return r.R29, true
|
|
|
|
|
case "r30", "s8":
|
|
|
|
|
return r.R30, true
|
|
|
|
|
case "r31", "pc":
|
|
|
|
|
return r.R31, true
|
|
|
|
|
default:
|
|
|
|
|
return 0, false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// breakpointInsn is the software breakpoint instruction (BRK $0).
|
|
|
|
|
var breakpointInsn = []byte{0x05, 0x00, 0x2a, 0x00} // break 0
|
|
|
|
|
|
2026-09-19 23:49:19 +02:00
|
|
|
// breakpointPCAdjust is how far PC is past the breakpoint instruction after
|
|
|
|
|
// a trap: 0, because the kernel delivers the break SIGTRAP with csr_era
|
|
|
|
|
// still on the break instruction. do_bp passes regs->csr_era straight to
|
|
|
|
|
// force_sig_fault(SIGTRAP, TRAP_BRKPT, ...) and never adjusts era on the
|
|
|
|
|
// signal path (arch/loongarch/kernel/traps.c).
|
|
|
|
|
const breakpointPCAdjust = 0
|