From 2c77a52638cc7d7711b31b6e175478f9d5180d8f Mon Sep 17 00:00:00 2001 From: "max.ma" Date: Tue, 1 Nov 2022 19:50:38 -0700 Subject: [PATCH 3/6] add intial rvv support to display rvv registers --- gdb/arch/riscv.c | 12 ++- gdb/features/riscv/vpu.c | 66 +++++++++++++++++ gdb/nat/riscv-linux-tdesc.c | 46 +++++++++++- gdb/nat/riscv-linux-tdesc.h | 18 +++++ gdb/riscv-linux-nat.c | 139 +++++++++++++++++++++++++++++++++++ gdb/riscv-tdep.c | 45 +++++++++++- gdb/riscv-tdep.h | 5 ++ gdbserver/linux-riscv-low.cc | 58 +++++++++++++++ include/elf/common.h | 4 + 9 files changed, 388 insertions(+), 5 deletions(-) create mode 100644 gdb/features/riscv/vpu.c diff --git a/gdb/arch/riscv.c b/gdb/arch/riscv.c index 6f6fcb081e..813bbb1a82 100644 --- a/gdb/arch/riscv.c +++ b/gdb/arch/riscv.c @@ -25,6 +25,8 @@ #include "../features/riscv/32bit-fpu.c" #include "../features/riscv/64bit-fpu.c" #include "../features/riscv/rv32e-xregs.c" +#include "../features/riscv/vpu.c" + #ifndef GDBSERVER #define STATIC_IN_GDB static @@ -61,6 +63,9 @@ riscv_create_target_description (const struct riscv_gdbarch_features features) arch_name.append ("d"); else if (features.flen == 16) arch_name.append ("q"); + + if (features.vlen > 0) + arch_name.append ("v"); set_tdesc_architecture (tdesc.get (), arch_name.c_str ()); #endif @@ -88,7 +93,12 @@ riscv_create_target_description (const struct riscv_gdbarch_features features) targets. We don't support creating vector features on native targets (yet). */ if (features.vlen != 0) - error (_("unable to create vector feature")); + { + if (features.xlen == 4) + create_feature_riscv_vpu (tdesc.get (), 32, features.vlen); + else if (features.xlen == 8) + create_feature_riscv_vpu (tdesc.get (), 64, features.vlen); + } return tdesc; } diff --git a/gdb/features/riscv/vpu.c b/gdb/features/riscv/vpu.c new file mode 100644 index 0000000000..fe45f37b4c --- /dev/null +++ b/gdb/features/riscv/vpu.c @@ -0,0 +1,66 @@ +#include "gdbsupport/tdesc.h" + +static int +create_feature_riscv_vpu (struct target_desc *result, int arch, int vlen) +{ + struct tdesc_feature *feature; + tdesc_type *element_type, *field_type; + tdesc_type_with_fields *type_with_fields; + int i, regnum; + char regname[10]; + printf("create_feature_riscv_vpu\n "); + + feature = tdesc_create_feature (result, "org.gnu.gdb.riscv.vector"); + + if (vlen >= 16) + { + element_type = tdesc_named_type (feature, "uint128"); + tdesc_create_vector (feature, "v_quads", element_type, (vlen * 8)/128); + } + if (vlen >= 8) + { + element_type = tdesc_named_type (feature, "uint64"); + tdesc_create_vector (feature, "v_longs", element_type, (vlen * 8)/64); + } + element_type = tdesc_named_type (feature, "uint32"); + tdesc_create_vector (feature, "v_words", element_type, (vlen * 8)/32); + element_type = tdesc_named_type (feature, "uint16"); + tdesc_create_vector (feature, "v_shorts", element_type, (vlen * 8)/32); + element_type = tdesc_named_type (feature, "uint8"); + tdesc_create_vector (feature, "v_bytes", element_type, vlen); + + type_with_fields = tdesc_create_union (feature, "riscv_vector"); + if (vlen >= 16) + { + field_type = tdesc_named_type (feature, "v_quads"); + tdesc_add_field (type_with_fields, "q", field_type); + } + if (vlen >= 8) + { + field_type = tdesc_named_type (feature, "v_longs"); + tdesc_add_field (type_with_fields, "l", field_type); + } + field_type = tdesc_named_type (feature, "v_words"); + tdesc_add_field (type_with_fields, "w", field_type); + field_type = tdesc_named_type (feature, "v_shorts"); + tdesc_add_field (type_with_fields, "s", field_type); + field_type = tdesc_named_type (feature, "v_bytes"); + tdesc_add_field (type_with_fields, "b", field_type); + + tdesc_create_reg (feature, "vstart", 73, 1, NULL, arch, "int"); + tdesc_create_reg (feature, "vcsr", 80, 1, NULL, arch, "int"); + tdesc_create_reg (feature, "vl", 3169, 1, NULL, arch, "int"); + tdesc_create_reg (feature, "vtype", 3170, 1, NULL, arch, "int"); + + regnum = 4162; + + for (i = 0; i < 32; i++) + { + memset(regname, 0, sizeof(regname)); + sprintf(regname, "v%d", i); + tdesc_create_reg (feature, regname, regnum++, 1, + NULL, vlen * 8, "riscv_vector"); + } + + return regnum; +} diff --git a/gdb/nat/riscv-linux-tdesc.c b/gdb/nat/riscv-linux-tdesc.c index d676233cc3..b1c27b1f7e 100644 --- a/gdb/nat/riscv-linux-tdesc.c +++ b/gdb/nat/riscv-linux-tdesc.c @@ -23,7 +23,7 @@ #include "elf/common.h" #include "nat/gdb_ptrace.h" #include "nat/riscv-linux-tdesc.h" - +#include "nat/linux-ptrace.h" #include /* Work around glibc header breakage causing ELF_NFPREG not to be usable. */ @@ -31,6 +31,26 @@ # define NFPREG 33 #endif +#ifndef CSR_VLENB +#define CSR_VLENB 0xC22 +#endif + +#ifdef __ASSEMBLY__ +#define __ASM_STR(x) x +#else +#define __ASM_STR(x) #x +#endif + + +#define csr_read(csr) \ +({ \ + register unsigned long __v; \ + __asm__ __volatile__ ("csrr %0, " __ASM_STR(csr) \ + : "=r" (__v) : \ + : "memory"); \ + __v; \ +}) + /* See nat/riscv-linux-tdesc.h. */ struct riscv_gdbarch_features @@ -78,6 +98,30 @@ riscv_linux_read_features (int tid) features.flen = flen; break; } + + struct __riscv_v_state rv_state; + struct iovec iov; + iov.iov_base = &rv_state; + iov.iov_len = sizeof(rv_state); + + if (ptrace (PTRACE_GETREGSET, tid, NT_RISCV_VECTOR, + (PTRACE_TYPE_ARG3) &iov) == -1) + { + switch (errno) + { + case EINVAL: + break; + case EIO: + break; + default: + perror_with_name (_("Couldn't get registers")); + break; + } + } + else + { + features.vlen = csr_read(CSR_VLENB); + } return features; } diff --git a/gdb/nat/riscv-linux-tdesc.h b/gdb/nat/riscv-linux-tdesc.h index 8e8da41026..2f8d69e4a6 100644 --- a/gdb/nat/riscv-linux-tdesc.h +++ b/gdb/nat/riscv-linux-tdesc.h @@ -21,6 +21,24 @@ #include "arch/riscv.h" +/* Work around glibc header breakage causing ELF_NFPREG not to be usable. */ +#ifndef NFPREG +# define NFPREG 33 +#endif + +#ifndef NVREG +#define NVREG 32 +#endif + +#define csr_read(csr) \ +({ \ + register unsigned long __v; \ + __asm__ __volatile__ ("csrr %0, " csr \ + : "=r" (__v) : \ + : "memory"); \ + __v; \ +}) + /* Determine XLEN and FLEN for the LWP identified by TID, and return a corresponding features object. */ struct riscv_gdbarch_features riscv_linux_read_features (int tid); diff --git a/gdb/riscv-linux-nat.c b/gdb/riscv-linux-nat.c index 8be4a5ac3e..92dc1df3c1 100644 --- a/gdb/riscv-linux-nat.c +++ b/gdb/riscv-linux-nat.c @@ -34,6 +34,10 @@ # define NFPREG 33 #endif +#ifndef ELF_NVREG +#define ELF_NVREG 32 +#endif + /* RISC-V Linux native additions to the default linux support. */ class riscv_linux_nat_target final : public linux_nat_target @@ -196,6 +200,90 @@ fill_fpregset (const struct regcache *regcache, prfpregset_t *fpregs, } } +/* Copy vector register REGNUM (or all vector regs if REGNUM == -1) + from regset vregs into REGCACHE. */ + +static void +supply_vregset_regnum (struct regcache *regcache, char *vregs, + int regnum) +{ + int i; + int vlen = riscv_isa_vlen(regcache->arch()); + __riscv_v_state *vstate = (__riscv_v_state*)vregs; + vregs += sizeof(__riscv_v_state); + + if (regnum == -1) + { + regcache->raw_supply (RISCV_CSR_VSTART_REGNUM, &vstate->vstart); + regcache->raw_supply (RISCV_CSR_VCSR_REGNUM, &vstate->vcsr); + regcache->raw_supply (RISCV_CSR_VL_REGNUM, &vstate->vl); + regcache->raw_supply (RISCV_CSR_VTYPE_REGNUM, &vstate->vtype); + + for (i = RISCV_V0_REGNUM; + i <= RISCV_V31_REGNUM; + i++, vregs += vlen) + regcache->raw_supply (i, vregs); + } + else if (regnum >= RISCV_V0_REGNUM && regnum <= RISCV_V31_REGNUM) + { + vregs += (regnum - RISCV_V0_REGNUM) * vlen; + regcache->raw_supply (regnum, vregs); + } + else if (regnum == RISCV_CSR_VSTART_REGNUM) + regcache->raw_supply (regnum, &vstate->vstart); + else if (regnum == RISCV_CSR_VCSR_REGNUM) + regcache->raw_supply (regnum, &vstate->vcsr); + else if (regnum == RISCV_CSR_VL_REGNUM) + regcache->raw_supply (regnum, &vstate->vl); + else if (regnum == RISCV_CSR_VTYPE_REGNUM) + regcache->raw_supply (regnum, &vstate->vtype); +} + + +/* Copy all vector registers from regset vregs into REGCACHE. */ + +void +supply_vregset (struct regcache *regcache, const void *vregs) +{ + supply_vregset_regnum (regcache, (char *)vregs, -1); +} + +/* Copy vector register REGNUM (or all fp regs if REGNUM == -1) + from REGCACHE into regset FPREGS. */ +void +fill_vregset (struct regcache *regcache, char *vregs, + int regnum) +{ + int i; + int vlen = riscv_isa_vlen(regcache->arch()); + __riscv_v_state *vstate = (__riscv_v_state*)vregs; + if (regnum == -1) + { + regcache->raw_collect (RISCV_CSR_VSTART_REGNUM, &vstate->vstart); + regcache->raw_collect (RISCV_CSR_VCSR_REGNUM, &vstate->vcsr); + regcache->raw_collect (RISCV_CSR_VL_REGNUM, &vstate->vl); + regcache->raw_collect (RISCV_CSR_VTYPE_REGNUM, &vstate->vtype); + + for (i = RISCV_V0_REGNUM; + i <= RISCV_V31_REGNUM; + i++, vregs += vlen) + regcache->raw_collect (i, vregs); + } + else if (regnum >= RISCV_V0_REGNUM && regnum <= RISCV_V31_REGNUM) + { + vregs += (regnum - RISCV_V0_REGNUM) * vlen; + regcache->raw_collect (regnum, vregs); + } + else if (regnum == RISCV_CSR_VSTART_REGNUM) + regcache->raw_collect (regnum, &vstate->vstart); + else if (regnum == RISCV_CSR_VCSR_REGNUM) + regcache->raw_collect (regnum, &vstate->vcsr); + else if (regnum == RISCV_CSR_VL_REGNUM) + regcache->raw_supply (regnum, &vstate->vl); + else if (regnum == RISCV_CSR_VTYPE_REGNUM) + regcache->raw_supply (regnum, &vstate->vl); +} + /* Return a target description for the current target. */ const struct target_desc * @@ -252,6 +340,31 @@ riscv_linux_nat_target::fetch_registers (struct regcache *regcache, int regnum) supply_fpregset_regnum (regcache, ®s, regnum); } + if ((regnum >= RISCV_V0_REGNUM + && regnum <= RISCV_V31_REGNUM) + || (regnum == RISCV_CSR_VSTART_REGNUM) + || (regnum == RISCV_CSR_VCSR_REGNUM) + || (regnum == RISCV_CSR_VL_REGNUM) + || (regnum == RISCV_CSR_VTYPE_REGNUM) + || (regnum == -1)) + { + struct iovec iov; + iov.iov_len = sizeof(__riscv_v_state) + + ELF_NVREG * riscv_isa_vlen(regcache->arch()); + + iov.iov_base = malloc(iov.iov_len); + char * buf = (char *)iov.iov_base + sizeof(__riscv_v_state); + gdb_assert (iov.iov_base != NULL); + + if (ptrace (PTRACE_GETREGSET, tid, NT_RISCV_VECTOR, + (PTRACE_TYPE_ARG3) &iov) == -1) + perror_with_name (_("Couldn't get vector registers")); + else + supply_vregset_regnum (regcache, (char *)(iov.iov_base), regnum); + + free(iov.iov_base); + } + if ((regnum == RISCV_CSR_MISA_REGNUM) || (regnum == -1)) { @@ -321,6 +434,32 @@ riscv_linux_nat_target::store_registers (struct regcache *regcache, int regnum) } } + if ((regnum >= RISCV_V0_REGNUM + && regnum <= RISCV_V31_REGNUM) + || (regnum == RISCV_CSR_VSTART_REGNUM) + || (regnum == RISCV_CSR_VCSR_REGNUM) + || (regnum == RISCV_CSR_VL_REGNUM) + || (regnum == RISCV_CSR_VTYPE_REGNUM) + || (regnum == -1)) + { + struct iovec iov; + iov.iov_len = sizeof(__riscv_v_state) + + ELF_NVREG * riscv_isa_vlen(regcache->arch ()); + iov.iov_base = malloc(iov.iov_len); + gdb_assert (iov.iov_base != NULL); + + if (ptrace (PTRACE_GETREGSET, tid, NT_RISCV_VECTOR, + (PTRACE_TYPE_ARG3) &iov) == -1) + perror_with_name (_("Couldn't get vector registers")); + else + { + fill_vregset (regcache, (char *)(iov.iov_base), regnum); + if (ptrace (PTRACE_SETREGSET, tid, NT_FPREGSET, + (PTRACE_TYPE_ARG3) &iov) == -1) + perror_with_name (_("Couldn't set vector registers")); + } + free(iov.iov_base); + } /* Access to CSRs has potential security issues, don't support them for now. */ } diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c index 576d7d96d9..be0b109b49 100644 --- a/gdb/riscv-tdep.c +++ b/gdb/riscv-tdep.c @@ -619,16 +619,21 @@ struct riscv_vector_feature : public riscv_register_feature { RISCV_V0_REGNUM + 29, { "v29" } }, { RISCV_V0_REGNUM + 30, { "v30" } }, { RISCV_V0_REGNUM + 31, { "v31" } }, + + { RISCV_CSR_VSTART_REGNUM, { "vstart" } }, + { RISCV_CSR_VCSR_REGNUM, { "vcsr" } }, + { RISCV_CSR_VL_REGNUM, { "vl" } }, + { RISCV_CSR_VTYPE_REGNUM, { "vtype" } }, }; } /* Return the preferred name for the register with gdb register number REGNUM, which must be in the inclusive range RISCV_V0_REGNUM to - RISCV_V0_REGNUM + 31. */ + RISCV_V31_REGNUM. */ const char *register_name (int regnum) const { gdb_assert (regnum >= RISCV_V0_REGNUM - && regnum <= RISCV_V0_REGNUM + 31); + && regnum <= RISCV_V31_REGNUM); regnum -= RISCV_V0_REGNUM; return m_registers[regnum].names[0]; } @@ -663,6 +668,12 @@ struct riscv_vector_feature : public riscv_register_feature int vector_bitsize = -1; for (const auto ® : m_registers) { + if (reg.regnum == RISCV_CSR_VSTART_REGNUM || + reg.regnum == RISCV_CSR_VCSR_REGNUM || + reg.regnum == RISCV_CSR_VL_REGNUM || + reg.regnum == RISCV_CSR_VTYPE_REGNUM + ) + continue; int reg_bitsize = -1; for (const char *name : reg.names) { @@ -737,6 +748,16 @@ riscv_isa_xlen (struct gdbarch *gdbarch) return tdep->isa_features.xlen; } +/* See riscv-tdep.h. */ + +int +riscv_isa_vlen (struct gdbarch *gdbarch) +{ + riscv_gdbarch_tdep *tdep = (riscv_gdbarch_tdep *) gdbarch_tdep (gdbarch); + return tdep->isa_features.vlen; +} + + /* See riscv-tdep.h. */ int @@ -781,6 +802,13 @@ riscv_has_fp_regs (struct gdbarch *gdbarch) return (riscv_isa_flen (gdbarch) > 0); } +/* Return true if the target for GDBARCH has vector hardware. */ +static bool +riscv_has_v_regs (struct gdbarch *gdbarch) +{ + return (riscv_isa_vlen (gdbarch) > 0); +} + /* Return true if GDBARCH is using any of the floating point hardware ABIs. */ static bool @@ -873,7 +901,7 @@ riscv_sw_breakpoint_from_kind (struct gdbarch *gdbarch, int kind, int *size) /* Implement the register_name gdbarch method. This is used instead of the function supplied by calling TDESC_USE_REGISTERS so that we can - ensure the preferred names are offered for x-regs and f-regs. */ + ensure the preferred names are offered for x-regs,f-regs and v-regs. */ static const char * riscv_register_name (struct gdbarch *gdbarch, int regnum) @@ -903,6 +931,14 @@ riscv_register_name (struct gdbarch *gdbarch, int regnum) return riscv_freg_feature.register_name (regnum); } + if (regnum >= RISCV_V0_REGNUM && regnum <= RISCV_LAST_REGNUM) + { + if (riscv_has_v_regs (gdbarch)) + return riscv_vector_feature.register_name (regnum); + else + return NULL; + } + /* Some targets (QEMU) are reporting these three registers twice, once in the FPU feature, and once in the CSR feature. Both of these read the same underlying state inside the target, but naming the register @@ -3648,6 +3684,7 @@ riscv_gcc_target_options (struct gdbarch *gdbarch) { int isa_xlen = riscv_isa_xlen (gdbarch); int isa_flen = riscv_isa_flen (gdbarch); + int isa_vlen = riscv_isa_vlen (gdbarch); int abi_xlen = riscv_abi_xlen (gdbarch); int abi_flen = riscv_abi_flen (gdbarch); std::string target_options; @@ -3663,6 +3700,8 @@ riscv_gcc_target_options (struct gdbarch *gdbarch) target_options += "imafc"; else target_options += "imac"; + if (isa_vlen) + target_options += "v"; /* change to zvl zve later */ target_options += " -mabi="; if (abi_xlen == 8) diff --git a/gdb/riscv-tdep.h b/gdb/riscv-tdep.h index 4c3afb08e0..b4764b84ff 100644 --- a/gdb/riscv-tdep.h +++ b/gdb/riscv-tdep.h @@ -145,6 +145,11 @@ extern int riscv_abi_xlen (struct gdbarch *gdbarch); with RISCV_ISA_FLEN. */ extern int riscv_abi_flen (struct gdbarch *gdbarch); +/* Return the width in bytes of the hardware vector registers for + GDBARCH. If this architecture has no vector registers, then + return 0. Possible values are 32, 64, 128, ...2pow(16) */ +extern int riscv_isa_vlen(struct gdbarch *gdbarch); + /* Return true if GDBARCH is using the embedded x-regs abi, that is the target only has 16 x-registers, which includes a reduced number of argument registers. */ diff --git a/gdbserver/linux-riscv-low.cc b/gdbserver/linux-riscv-low.cc index 129bc3b138..89f7e41fd5 100644 --- a/gdbserver/linux-riscv-low.cc +++ b/gdbserver/linux-riscv-low.cc @@ -30,6 +30,17 @@ # define NFPREG 33 #endif +#ifndef NVREG +#define NVREG 32 +#endif + +/* In RVV spec, the register width must be no greater than 2exp(16). + We set the max width to 1024 for now. +*/ +#ifndef VREG_MAX_LEN +#define VREG_MAX_LEN 1024 +#endif + /* Linux target op definitions for the RISC-V architecture. */ class riscv_target : public linux_process_target @@ -158,6 +169,50 @@ riscv_store_fpregset (struct regcache *regcache, const void *buf) supply_register_by_name (regcache, "fcsr", regbuf); } + +/* Collect VRs from REGCACHE into BUF. */ + +static void +riscv_fill_vregset (struct regcache *regcache, void *buf) +{ + int i; + const struct target_desc *tdesc = regcache->tdesc; + int regno = find_regno (tdesc, "v0"); + int vlen = register_size (regcache->tdesc, regno); + __riscv_v_state *vstate = (__riscv_v_state *)buf; + buf += sizeof(__riscv_v_state); + + for (i = 0; i < NVREG; i++, buf += vlen) + collect_register (regcache, regno + i, buf); + + collect_register_by_name (regcache, "vstart", &vstate->vstart); + collect_register_by_name (regcache, "vl", &vstate->vl); + collect_register_by_name (regcache, "vtype", &vstate->vtype); + collect_register_by_name (regcache, "vcsr", &vstate->vcsr); +} + +/* Supply VECRs from BUF into REGCACHE. */ + +static void +riscv_store_vregset (struct regcache *regcache, const void *buf) +{ + const struct target_desc *tdesc = regcache->tdesc; + struct __riscv_v_state *vstate = (__riscv_v_state *)buf; + buf += sizeof(__riscv_v_state); + int regno = find_regno (tdesc, "v0"); + int vlen = register_size (regcache->tdesc, regno); + int i; + + for (i = 0; i < NVREG; i++, buf += vlen) + supply_register (regcache, regno + i, buf); + + supply_register_by_name (regcache, "vstart", &vstate->vstart); + supply_register_by_name (regcache, "vl", &vstate->vl); + supply_register_by_name (regcache, "vtype", &vstate->vtype); + supply_register_by_name (regcache, "vcsr", &vstate->vcsr); ; +} + + /* RISC-V/Linux regsets. FPRs are optional and come in different sizes, so define multiple regsets for them marking them all as OPTIONAL_REGS rather than FP_REGS, so that "regsets_fetch_inferior_registers" picks @@ -175,6 +230,9 @@ static struct regset_info riscv_regsets[] = { { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET, sizeof (struct __riscv_mc_f_ext_state), OPTIONAL_REGS, riscv_fill_fpregset, riscv_store_fpregset }, + { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_RISCV_VECTOR, + sizeof (struct __riscv_v_state) + NVREG * VREG_MAX_LEN, EXTENDED_REGS, + riscv_fill_vregset, riscv_store_vregset}, NULL_REGSET }; diff --git a/include/elf/common.h b/include/elf/common.h index 16587f6fb0..d9058fd40a 100644 --- a/include/elf/common.h +++ b/include/elf/common.h @@ -695,6 +695,10 @@ /* note name must be "LINUX". */ #define NT_ARM_PAC_ENABLED_KEYS 0x40a /* AArch64 pointer authentication enabled keys (prctl()) */ + + /* note name must be "LINUX". */ +#define NT_RISCV_VECTOR 0x900 /* RISC-V vector registers */ + /* note name must be "LINUX". */ #define NT_ARC_V2 0x600 /* ARC HS accumulator/extra registers. */ /* note name must be "LINUX". */ -- 2.25.1