// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: BSD-3-Clause //go:build freebsd && riscv64 package debug import ( "fmt" "golang.org/x/sys/unix" ) // GetRegs reads the general-purpose registers of the stopped debuggee and // converts the FreeBSD struct reg into the portable layout. Sstatus rides // the kernel's struct but the portable surface carries the GPRs and PC. func (s *Session) GetRegs() (Regs, error) { var ur unix.Reg if err := unix.PtraceGetRegs(s.pid, &ur); err != nil { return Regs{}, fmt.Errorf("debug: PT_GETREGS: %w", err) } return Regs{ PC: ur.Sepc, Ra: ur.Ra, Sp: ur.Sp, Gp: ur.Gp, Tp: ur.Tp, T0: ur.T[0], T1: ur.T[1], T2: ur.T[2], S0: ur.S[0], S1: ur.S[1], A0: ur.A[0], A1: ur.A[1], A2: ur.A[2], A3: ur.A[3], A4: ur.A[4], A5: ur.A[5], A6: ur.A[6], A7: ur.A[7], S2: ur.S[2], S3: ur.S[3], S4: ur.S[4], S5: ur.S[5], S6: ur.S[6], S7: ur.S[7], S8: ur.S[8], S9: ur.S[9], S10: ur.S[10], S11: ur.S[11], T3: ur.T[3], T4: ur.T[4], T5: ur.T[5], T6: ur.T[6], }, nil } // SetRegs writes the general-purpose registers of the stopped debuggee. // Read-modify-write keeps sstatus, which the kernel owns, intact. func (s *Session) SetRegs(regs *Regs) error { var ur unix.Reg if err := unix.PtraceGetRegs(s.pid, &ur); err != nil { return fmt.Errorf("debug: PT_GETREGS: %w", err) } ur.Sepc = regs.PC ur.Ra = regs.Ra ur.Sp = regs.Sp ur.Gp = regs.Gp ur.Tp = regs.Tp ur.T = [7]uint64{regs.T0, regs.T1, regs.T2, regs.T3, regs.T4, regs.T5, regs.T6} ur.S = [12]uint64{regs.S0, regs.S1, regs.S2, regs.S3, regs.S4, regs.S5, regs.S6, regs.S7, regs.S8, regs.S9, regs.S10, regs.S11} ur.A = [8]uint64{regs.A0, regs.A1, regs.A2, regs.A3, regs.A4, regs.A5, regs.A6, regs.A7} return unix.PtraceSetRegs(s.pid, &ur) } // FPRegs holds the RISC-V FP register state (32 64-bit FP registers plus // fcsr). type FPRegs struct { F [32]uint64 // F0-F31 (64-bit FP registers) FCSR uint32 } // GetFPRegs retrieves the FP register state via PT_GETFPREGS. The FreeBSD // struct fpreg carries each 64-bit FP register in a 128-bit slot (fp_x is // the flat [64]-word area the x/sys type renders as [32][2]); the low word // holds the register, and FCSR rides the tail. func (s *Session) GetFPRegs() (FPRegs, error) { var fp FPRegs var fr unix.FpReg if err := unix.PtraceGetFpRegs(s.pid, &fr); err != nil { return fp, fmt.Errorf("debug: PT_GETFPREGS: %w", err) } for i := range 32 { fp.F[i] = fr.X[i][0] } fp.FCSR = uint32(fr.Fcsr) return fp, nil } // VectorRegs holds the FP register state shown by the regs command // (riscv64 has 32 64-bit FP registers and fcsr). type VectorRegs struct { F [32]uint64 FCSR uint32 } // GetVectorRegs retrieves the FP registers. func (s *Session) GetVectorRegs() (VectorRegs, error) { fp, err := s.GetFPRegs() if err != nil { return VectorRegs{}, err } return VectorRegs{F: fp.F, FCSR: fp.FCSR}, nil }