]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb/aarch64: Enable FPMR for AArch64 in gdb on Linux
authorEzra Sitorus <ezra.sitorus@arm.com>
Mon, 17 Nov 2025 12:45:58 +0000 (12:45 +0000)
committerezra.sitorus <ezra.sitorus@arm.com>
Mon, 17 Nov 2025 12:47:09 +0000 (12:47 +0000)
The Floating Point Mode Register controls the behaviours of FP8
instructions. This patch add FPMR to GDB if it is enabled on the
target.

Approved-By: Luis Machado <luis.machado.foss@gmail.com>
gdb/aarch64-linux-nat.c
gdb/aarch64-linux-tdep.c
gdb/aarch64-tdep.c
gdb/aarch64-tdep.h
gdb/arch/aarch64.c
gdb/arch/aarch64.h
gdb/features/Makefile
gdb/features/aarch64-fpmr.c [new file with mode: 0644]
gdb/features/aarch64-fpmr.xml [new file with mode: 0644]

index 89ecedda57d480e84b5f6247488c56cdf82d5dc2..503a41c973d268546bb87e7ba9b7af9490e8236e 100644 (file)
@@ -604,6 +604,48 @@ store_gcsregs_to_thread (regcache *regcache)
     perror_with_name (_("Unable to store GCS registers"));
 }
 
+/* Fill GDB's REGCACHE with the FPMR register set content from the
+   thread associated with REGCACHE.  */
+
+static void
+fetch_fpmr_from_thread (struct regcache *regcache)
+{
+  aarch64_gdbarch_tdep *tdep
+    = gdbarch_tdep<aarch64_gdbarch_tdep> (regcache->arch ());
+
+    int tid = regcache->ptid ().lwp ();
+
+    struct iovec iov;
+    uint64_t val;
+    iov.iov_base = &val;
+    iov.iov_len = sizeof (val);
+
+    if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_FPMR, &iov) < 0)
+      perror_with_name (_("Unable to fetch FPMR register set"));
+    regcache->raw_supply (tdep->fpmr_regnum, &val);
+}
+
+/* Store the NT_ARM_FPMR register set contents from GDB's REGCACHE to the
+    thread associated with REGCACHE.  */
+
+static void
+store_fpmr_to_thread (struct regcache *regcache)
+{
+  aarch64_gdbarch_tdep *tdep
+    = gdbarch_tdep<aarch64_gdbarch_tdep> (regcache->arch ());
+
+  int tid = regcache->ptid ().lwp ();
+
+  struct iovec iov;
+  uint64_t val;
+  iov.iov_base = &val;
+  iov.iov_len = sizeof (val);
+
+  regcache->raw_collect (tdep->fpmr_regnum, (char *) &val);
+  if (ptrace (PTRACE_SETREGSET, tid, NT_ARM_FPMR, &iov) < 0)
+    perror_with_name (_("Unable to store FPMR register set"));
+}
+
 /* The AArch64 version of the "fetch_registers" target_ops method.  Fetch
    REGNO from the target and place the result into REGCACHE.  */
 
@@ -642,6 +684,9 @@ aarch64_fetch_registers (struct regcache *regcache, int regno)
 
       if (tdep->has_gcs_linux ())
        fetch_gcsregs_from_thread (regcache);
+
+      if (tdep->has_fpmr ())
+       fetch_fpmr_from_thread (regcache);
     }
   /* General purpose register?  */
   else if (regno < AARCH64_V0_REGNUM)
@@ -679,6 +724,9 @@ aarch64_fetch_registers (struct regcache *regcache, int regno)
           && (regno == tdep->gcs_reg_base || regno == tdep->gcs_linux_reg_base
               || regno == tdep->gcs_linux_reg_base + 1))
     fetch_gcsregs_from_thread (regcache);
+  /* FPMR?  */
+  else if (tdep->has_fpmr () && (regno == tdep->fpmr_regnum))
+    fetch_fpmr_from_thread (regcache);
 }
 
 /* A version of the "fetch_registers" target_ops method used when running
@@ -753,6 +801,9 @@ aarch64_store_registers (struct regcache *regcache, int regno)
 
       if (tdep->has_gcs_linux ())
        store_gcsregs_to_thread (regcache);
+
+      if (tdep->has_fpmr ())
+       store_fpmr_to_thread (regcache);
     }
   /* General purpose register?  */
   else if (regno < AARCH64_V0_REGNUM)
@@ -784,6 +835,9 @@ aarch64_store_registers (struct regcache *regcache, int regno)
           && (regno == tdep->gcs_reg_base || regno == tdep->gcs_linux_reg_base
               || regno == tdep->gcs_linux_reg_base + 1))
     store_gcsregs_to_thread (regcache);
+  /* FPMR?  */
+  else if (tdep->has_fpmr () && regno == tdep->fpmr_regnum)
+    store_fpmr_to_thread (regcache);
 
   /* PAuth registers are read-only.  */
 }
@@ -969,6 +1023,9 @@ aarch64_linux_nat_target::read_description ()
   if ((hwcap2 & HWCAP2_SME2) || (hwcap2 & HWCAP2_SME2P1))
     features.sme2 = supports_zt_registers (tid);
 
+  /* Check for FPMR.  */
+  features.fpmr = hwcap2 & HWCAP2_FPMR;
+
   return aarch64_read_description (features);
 }
 
index 048be4f3532083f13138898cdb881f494dd2d904..10b44d978af18efd9e474e36e5cc9e6d8c17660e 100644 (file)
@@ -1712,6 +1712,7 @@ aarch64_linux_core_read_description (struct gdbarch *gdbarch,
   features.pauth = hwcap & AARCH64_HWCAP_PACA;
   features.gcs = features.gcs_linux = hwcap & HWCAP_GCS;
   features.mte = hwcap2 & HWCAP2_MTE;
+  features.fpmr = hwcap2 & HWCAP2_FPMR;
 
   /* Handle the TLS section.  */
   asection *tls = bfd_get_section_by_name (abfd, ".reg-aarch-tls");
index 89142e0a2149fd3992f0e0a4e505917018c4ac0b..0bb23944297ea6a7ce6ac0e519fec249f8189f41 100644 (file)
@@ -4140,6 +4140,10 @@ aarch64_features_from_target_desc (const struct target_desc *tdesc)
   features.gcs_linux = (tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.gcs.linux")
                        != nullptr);
 
+  /* Check for FPMR feature.  */
+  features.fpmr = (tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.fpmr")
+                  != nullptr);
+
   return features;
 }
 
@@ -4550,6 +4554,16 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
       num_pseudo_regs += 32;   /* add the Bn scalar register pseudos */
     }
 
+  int fpmr_regnum = -1;
+  const struct tdesc_feature *feature_fpmr
+      = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.fpmr");
+  if (feature_fpmr != nullptr)
+    {
+      fpmr_regnum = num_regs++;
+      valid_p &= tdesc_numbered_register (feature_fpmr, tdesc_data.get (),
+                                         fpmr_regnum, "fpmr");
+    }
+
   int first_sme_regnum = -1;
   int first_sme2_regnum = -1;
   int first_sme_pseudo_regnum = -1;
@@ -4749,6 +4763,7 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   tdep->tls_register_count = tls_register_count;
   tdep->gcs_reg_base = first_gcs_regnum;
   tdep->gcs_linux_reg_base = first_gcs_linux_regnum;
+  tdep->fpmr_regnum = fpmr_regnum;
 
   /* Set the SME register set details.  The pseudo-registers will be adjusted
      later.  */
index 99e7d26ce4ab35a9f0f78ea41e5d3c675f737da7..9acd29b2d88dd414566134df572f22e8d5cf83a7 100644 (file)
@@ -207,6 +207,15 @@ struct aarch64_gdbarch_tdep : gdbarch_tdep_base
   {
     return gcs_linux_reg_base != -1;
   }
+
+  /* First FPMR register.  This is -1 if FPMR is not supported.  */
+  int fpmr_regnum = -1;
+
+  bool
+  has_fpmr () const
+  {
+    return fpmr_regnum != -1;
+  }
 };
 
 const target_desc *aarch64_read_description (const aarch64_features &features);
index dff2bc16003adab6d127c026d61178fbcd92cbbc..622138f43b5b367aab8bf714158daadc09539c8d 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "../features/aarch64-core.c"
 #include "../features/aarch64-fpu.c"
+#include "../features/aarch64-fpmr.c"
 #include "../features/aarch64-sve.c"
 #include "../features/aarch64-pauth.c"
 #include "../features/aarch64-mte.c"
@@ -73,6 +74,9 @@ aarch64_create_target_description (const aarch64_features &features)
   if (features.gcs_linux)
     regnum = create_feature_aarch64_gcs_linux (tdesc.get (), regnum);
 
+  if (features.fpmr)
+    regnum = create_feature_aarch64_fpmr (tdesc.get (), regnum);
+
   return tdesc.release ();
 }
 
index 0fcdba7fb7d6bb8a3f2084d46e53cbc6133e7429..bcbf47dc3a35bb4a70d1b49a27411a0f3431c5dc 100644 (file)
@@ -34,6 +34,7 @@ struct aarch64_features
   uint64_t vq = 0;
   bool pauth = false;
   bool mte = false;
+  bool fpmr = false;
 
   /* A positive TLS value indicates the number of TLS registers available.  */
   uint8_t tls = 0;
@@ -68,7 +69,8 @@ inline bool operator==(const aarch64_features &lhs, const aarch64_features &rhs)
     && lhs.svq == rhs.svq
     && lhs.sme2 == rhs.sme2
     && lhs.gcs == rhs.gcs
-    && lhs.gcs_linux == rhs.gcs_linux;
+    && lhs.gcs_linux == rhs.gcs_linux
+    && lhs.fpmr == rhs.fpmr;
 }
 
 namespace std
@@ -98,6 +100,8 @@ namespace std
       h = h << 1 | features.gcs;
       h = h << 1 | features.gcs_linux;
 
+      /* FPMR feature.  */
+      h = h << 1 | features.fpmr;
       return h;
     }
   };
@@ -242,4 +246,9 @@ enum aarch64_regnum
 /* Size of the SME2 ZT0 register in bytes.  */
 #define AARCH64_SME2_ZT0_SIZE 64
 
+/* Feature check for Floating Point Mode Register.  */
+#ifndef HWCAP2_FPMR
+#define HWCAP2_FPMR (1ULL << 48)
+#endif /* HWCAP2_FPMR */
+
 #endif /* GDB_ARCH_AARCH64_H */
index c98e31bc9b6c01ac52409bdf569a568e48d17267..74a027d931fe70fafb46b21ec977b8dc70baf537 100644 (file)
@@ -202,6 +202,7 @@ $(outdir)/%.dat: %.xml number-regs.xsl sort-regs.xsl gdbserver-regs.xsl
 # For targets with feature based target descriptions,
 # the set of xml files we'll generate .c files for GDB from.
 FEATURE_XMLFILES = aarch64-core.xml \
+       aarch64-fpmr.xml \
        aarch64-fpu.xml \
        aarch64-pauth.xml \
        aarch64-mte.xml \
diff --git a/gdb/features/aarch64-fpmr.c b/gdb/features/aarch64-fpmr.c
new file mode 100644 (file)
index 0000000..a372b12
--- /dev/null
@@ -0,0 +1,44 @@
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: aarch64-fpmr.xml */
+
+#include "gdbsupport/tdesc.h"
+
+static int
+create_feature_aarch64_fpmr (struct target_desc *result, long regnum)
+{
+  struct tdesc_feature *feature;
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.aarch64.fpmr");
+  tdesc_type_with_fields *type_with_fields;
+  type_with_fields = tdesc_create_enum (feature, "fp8_fmt", 3);
+  tdesc_add_enum_value (type_with_fields, 0, "E5M2");
+  tdesc_add_enum_value (type_with_fields, 1, "E4M3");
+
+  type_with_fields = tdesc_create_enum (feature, "osc", 1);
+  tdesc_add_enum_value (type_with_fields, 0, "Inf/NaN");
+  tdesc_add_enum_value (type_with_fields, 1, "MaxNormal");
+
+  type_with_fields = tdesc_create_enum (feature, "osm", 1);
+  tdesc_add_enum_value (type_with_fields, 0, "Inf");
+  tdesc_add_enum_value (type_with_fields, 1, "MaxNormal");
+
+  type_with_fields = tdesc_create_flags (feature, "fpmr_flags", 8);
+  tdesc_type *field_type;
+  field_type = tdesc_named_type (feature, "fp8_fmt");
+  tdesc_add_typed_bitfield (type_with_fields, "F8S1", 0, 2, field_type);
+  field_type = tdesc_named_type (feature, "fp8_fmt");
+  tdesc_add_typed_bitfield (type_with_fields, "F8S2", 3, 5, field_type);
+  field_type = tdesc_named_type (feature, "fp8_fmt");
+  tdesc_add_typed_bitfield (type_with_fields, "F8D", 6, 8, field_type);
+  field_type = tdesc_named_type (feature, "osm");
+  tdesc_add_typed_bitfield (type_with_fields, "OSM", 14, 14, field_type);
+  field_type = tdesc_named_type (feature, "osc");
+  tdesc_add_typed_bitfield (type_with_fields, "OSC", 15, 15, field_type);
+  tdesc_add_bitfield (type_with_fields, "LSCALE", 16, 22);
+  field_type = tdesc_named_type (feature, "int8");
+  tdesc_add_typed_bitfield (type_with_fields, "NSCALE", 24, 31, field_type);
+  tdesc_add_bitfield (type_with_fields, "LSCALE2", 32, 37);
+
+  tdesc_create_reg (feature, "fpmr", regnum++, 1, NULL, 64, "fpmr_flags");
+  return regnum;
+}
diff --git a/gdb/features/aarch64-fpmr.xml b/gdb/features/aarch64-fpmr.xml
new file mode 100644 (file)
index 0000000..3e88843
--- /dev/null
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2025 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.aarch64.fpmr">
+
+  <!-- FP8 format for F8S1, F8S2 and F8D fields.  This is either E5M2 or
+       E4M3.  -->
+  <enum id="fp8_fmt" size="3">
+    <evalue name="E5M2" value="0"/>
+    <evalue name="E4M3" value="1"/>
+  </enum>
+
+  <!-- Overflow saturation for FP8 convert instructions.  Specifies the result
+       when a floating-point overflow exception is detected.  -->
+  <enum id="osc" size="1">
+    <!-- Infinity or NaN is generated.  -->
+    <evalue name="Inf/NaN" value="0"/>
+    <!-- Maximum normal number is generated.  -->
+    <evalue name="MaxNormal" value="1"/>
+  </enum>
+
+  <!-- Overflow saturation for FP8 multiplication instructions.  Specifies the
+       result when a floating-point overflow exception is detected.  -->
+    <enum id="osm" size="1">
+    <!-- Infinity generated.  -->
+    <evalue name="Inf" value="0"/>
+    <!-- Maximum normal number is generated.  -->
+    <evalue name="MaxNormal" value="1"/>
+  </enum>
+
+  <flags id="fpmr_flags" size="8">
+    <!-- SRC1 Format.  -->
+    <field name="F8S1" start="0" end="2" type="fp8_fmt"/>
+    <!-- SRC2 Format.  -->
+    <field name="F8S2" start="3" end="5" type="fp8_fmt"/>
+    <!-- F8D Format.  -->
+    <field name="F8D" start="6" end="8" type="fp8_fmt"/>
+    <!-- OSM.  -->
+    <field name="OSM" start="14" end="14" type="osm"/>
+    <!-- OSC.  -->
+    <field name="OSC" start="15" end="15" type="osc"/>
+    <!-- LSCALE.  -->
+    <field name="LSCALE" start="16" end="22"/>
+    <!-- NSCALE.  -->
+    <field name="NSCALE" start="24" end="31" type="int8"/>
+    <!-- LSCALE2.  -->
+    <field name="LSCALE2" start="32" end="37"/>
+  </flags>
+
+  <reg name="fpmr" bitsize="64" type="fpmr_flags"/>
+
+</feature>