binutils-gdb/gdb/arch/riscv.h
Andrew Burgess 4749b84b51 gdb/riscv: better support for fflags and frm registers
First, some background on the RISC-V registers fflags, frm, and fcsr.

These three registers all relate to the floating-point status and
control mechanism on RISC-V.  The fcsr is the floatint-point control
status register, and consists of two parts, the flags (bits 0 to 4)
and the rounding-mode (bits 5 to 7).

The fcsr register is just one of many control/status registers (or
CSRs) available on RISC-V.  The fflags and frm registers are also
CSRs.  These CSRs are aliases for the relevant parts of the fcsr
register.  So fflags is an alias for bits 0 to 4 of fcsr, and frm is
an alias for bits 5 to 7 of fcsr.

This means that a user can change the floating-point rounding mode
either, by writing a complete new value into fcsr, or by writing just
the rounding mode into frm.

How this impacts on GDB is like this: a target description could,
legitimately include all three registers, fcsr, fflags, and frm.  The
QEMU target currently does this, and this makes sense.  The target is
emulating the complete system, and has all three CSRs available, so
why not tell GDB about this.

In contrast, the RISC-V native Linux target only has access to the
fcsr.  This is because the ptrace data structure that the kernel uses
for reading and writing floating point state only contains a copy of
the fcsr, after all, this one field really contains both the fflags
and frm fields, so why carry around duplicate data.

So, we might expect that the target description for the RISC-V native
Linux GDB would only contain the fcsr register.  Unfortunately, this
is not the case.  The RISC-V native Linux target uses GDB's builtin
target descriptions by calling riscv_lookup_target_description, this
will then add an fpu feature from gdb/features/riscv, either
32bit-fpu.xml or 64bit-fpu.xml.  The problem, is that these features
include an entry for fcsr, fflags, and frm.  This means that GDB
expects the target to handle reading and writing these registers.  And
the RISC-V native Linux target currently doesn't.

In riscv_linux_nat_target::store_registers and
riscv_linux_nat_target::fetch_registers only the fcsr register is
handled, this means that, for RISC-V native Linux, the fflags and frm
registers always show up as <unavailable> - they are present in the
target description, but the target doesn't know how to access the
registers.

A final complication relating to these floating pointer CSRs is which
target description feature the registers appear in.

These registers are CSRs, so it would seem sensible that these
registers should appear in the CSR target description feature.

However, when I first added RISC-V target description support, I was
using a RISC-V simulator that didn't support any CSRs other than the
floating point related ones.  This simulator bundled all the float
related CSRs into the fpu target feature.  This didn't feel completely
unreasonable to me, and so I had GDB check for these registers in
either target feature.

In this commit I make some changes relating to how GDB handles the
three floating point CSR:

1. Remove fflags and frm from 32bit-fpu.xml and 64bit-fpu.xml.  This
means that the default RISC-V target description (which RISC-V native
FreeBSD), and the target descriptions created for RISC-V native Linux,
will not include these registers.  There's nothing stopping some other
target (e.g. QEMU) from continuing to include all three of these CSRs,
the code in riscv-tdep.c continues to check for all three of these
registers, and will handle them correctly if they are present.

2. If a target supplied fcsr, but does not supply fflags and/or frm,
then RISC-V GDB will now create two pseudo registers in order to
emulate the two missing CSRs.  These new pseudo-registers do the
obvious thing of just reading and writing the fcsr register.

3. With the new pseudo-registers we can no longer make use of the GDB
register numbers RISCV_CSR_FFLAGS_REGNUM and RISCV_CSR_FRM_REGNUM.
These will be the numbers used if the target supplies the registers in
its target description, but, if GDB falls back to using
pseudo-registers, then new, unique numbers will be used.  To handle
this I've added riscv_gdbarch_tdep::fflags_regnum and
riscv_gdbarch_tdep::frm_regnum, I've then updated the RISC-V code to
compare against these fields.

When adding the pseudo-register support, it is important that the
pseudo-register numbers are calculated after the call to
tdesc_use_registers.  This is because we don't know the total number
of physical registers until after this call, and the psuedo-register
numbers must follow on from the real (target supplied) registers.

I've updated some tests to include more testing of the fflags and frm
registers, as well as adding a new test.
2022-08-31 16:07:05 +01:00

122 lines
4.4 KiB
C++

/* Common target-dependent functionality for RISC-V
Copyright (C) 2018-2022 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#ifndef ARCH_RISCV_H
#define ARCH_RISCV_H
#include "gdbsupport/tdesc.h"
/* The set of RISC-V architectural features that we track that impact how
we configure the actual gdbarch instance. We hold one of these in the
gdbarch_tdep structure, and use it to distinguish between different
RISC-V gdbarch instances.
The information in here ideally comes from the target description,
however, if the target doesn't provide a target description then we will
create a default target description by first populating one of these
based on what we know about the binary being executed, and using that to
drive default target description creation. */
struct riscv_gdbarch_features
{
/* The size of the x-registers in bytes. This is either 4 (RV32), 8
(RV64), or 16 (RV128). No other value is valid. Initialise to the
invalid 0 value so we can spot if one of these is used
uninitialised. */
int xlen = 0;
/* The size of the f-registers in bytes. This is either 4 (RV32), 8
(RV64), or 16 (RV128). This can also hold the value 0 to indicate
that there are no f-registers. No other value is valid. */
int flen = 0;
/* The size of the v-registers in bytes. The value 0 indicates a target
with no vector registers. The minimum value for a standard compliant
target should be 16, but GDB doesn't currently mind, and will accept
any vector size. */
int vlen = 0;
/* When true this target is RV32E. */
bool embedded = false;
/* Track if the target description has an fcsr, fflags, and frm
registers. Some targets provide all these in their target
descriptions, while some only offer fcsr, while others don't even
offer that register. If a target provides fcsr but not fflags and/or
frm, then we can emulate these registers as pseudo registers. */
bool has_fcsr_reg = false;
bool has_fflags_reg = false;
bool has_frm_reg = false;
/* Equality operator. */
bool operator== (const struct riscv_gdbarch_features &rhs) const
{
return (xlen == rhs.xlen && flen == rhs.flen
&& embedded == rhs.embedded && vlen == rhs.vlen
&& has_fflags_reg == rhs.has_fflags_reg
&& has_frm_reg == rhs.has_frm_reg
&& has_fcsr_reg == rhs.has_fcsr_reg);
}
/* Inequality operator. */
bool operator!= (const struct riscv_gdbarch_features &rhs) const
{
return !((*this) == rhs);
}
/* Used by std::unordered_map to hash feature sets. */
std::size_t hash () const noexcept
{
std::size_t val = ((embedded ? 1 : 0) << 10
| (has_fflags_reg ? 1 : 0) << 11
| (has_frm_reg ? 1 : 0) << 12
| (has_fcsr_reg ? 1 : 0) << 13
| (xlen & 0x1f) << 5
| (flen & 0x1f) << 0
| (vlen & 0xfff) << 14);
return val;
}
};
#ifdef GDBSERVER
/* Create and return a target description that is compatible with FEATURES.
This is only used directly from the gdbserver where the created target
description is modified after it is return. */
target_desc_up riscv_create_target_description
(const struct riscv_gdbarch_features features);
#else
/* Lookup an already existing target description matching FEATURES, or
create a new target description if this is the first time we have seen
FEATURES. For the same FEATURES the same target_desc is always
returned. This is important when trying to lookup gdbarch objects as
GDBARCH_LIST_LOOKUP_BY_INFO performs a pointer comparison on target
descriptions to find candidate gdbarch objects. */
const target_desc *riscv_lookup_target_description
(const struct riscv_gdbarch_features features);
#endif /* GDBSERVER */
#endif /* ARCH_RISCV_H */