+2021-03-24  Luis Machado  <luis.machado@linaro.org>
+
+       * aarch64-linux-nat.c (fetch_mteregs_from_thread): New function.
+       (store_mteregs_to_thread): New function.
+       (aarch64_linux_nat_target::fetch_registers): Update to call
+       fetch_mteregs_from_thread.
+       (aarch64_linux_nat_target::store_registers): Update to call
+       store_mteregs_to_thread.
+       * aarch64-tdep.c (aarch64_mte_register_names): New struct.
+       (aarch64_cannot_store_register): Handle MTE registers.
+       (aarch64_gdbarch_init): Initialize and setup MTE registers.
+       * aarch64-tdep.h (gdbarch_tdep) <mte_reg_base>: New field.
+       <has_mte>: New method.
+       * arch/aarch64-linux.h (AARCH64_LINUX_SIZEOF_MTE): Define.
+
 2021-03-24  Luis Machado  <luis.machado@linaro.org>
 
        * aarch64-linux-nat.c
 
                        &pauth_regset[1]);
 }
 
+/* Fill GDB's register array with the MTE register values from
+   the current thread.  */
+
+static void
+fetch_mteregs_from_thread (struct regcache *regcache)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (regcache->arch ());
+  int regno = tdep->mte_reg_base;
+
+  gdb_assert (regno != -1);
+
+  uint64_t tag_ctl = 0;
+  struct iovec iovec;
+
+  iovec.iov_base = &tag_ctl;
+  iovec.iov_len = sizeof (tag_ctl);
+
+  int tid = get_ptrace_pid (regcache->ptid ());
+  if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_TAGGED_ADDR_CTRL, &iovec) != 0)
+      perror_with_name (_("unable to fetch MTE registers."));
+
+  regcache->raw_supply (regno, &tag_ctl);
+}
+
+/* Store to the current thread the valid MTE register set in the GDB's
+   register array.  */
+
+static void
+store_mteregs_to_thread (struct regcache *regcache)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (regcache->arch ());
+  int regno = tdep->mte_reg_base;
+
+  gdb_assert (regno != -1);
+
+  uint64_t tag_ctl = 0;
+
+  if (REG_VALID != regcache->get_register_status (regno))
+    return;
+
+  regcache->raw_collect (regno, (char *) &tag_ctl);
+
+  struct iovec iovec;
+
+  iovec.iov_base = &tag_ctl;
+  iovec.iov_len = sizeof (tag_ctl);
+
+  int tid = get_ptrace_pid (regcache->ptid ());
+  if (ptrace (PTRACE_SETREGSET, tid, NT_ARM_TAGGED_ADDR_CTRL, &iovec) != 0)
+    perror_with_name (_("unable to store MTE registers."));
+}
+
 /* Implement the "fetch_registers" target_ops method.  */
 
 void
 
       if (tdep->has_pauth ())
        fetch_pauth_masks_from_thread (regcache);
+
+      if (tdep->has_mte ())
+       fetch_mteregs_from_thread (regcache);
     }
   else if (regno < AARCH64_V0_REGNUM)
     fetch_gregs_from_thread (regcache);
          || regno == AARCH64_PAUTH_CMASK_REGNUM (tdep->pauth_reg_base))
        fetch_pauth_masks_from_thread (regcache);
     }
+
+  /* Fetch individual MTE registers.  */
+  if (tdep->has_mte ()
+      && (regno == tdep->mte_reg_base))
+    fetch_mteregs_from_thread (regcache);
 }
 
 /* Implement the "store_registers" target_ops method.  */
        store_sveregs_to_thread (regcache);
       else
        store_fpregs_to_thread (regcache);
+
+      if (tdep->has_mte ())
+       store_mteregs_to_thread (regcache);
     }
   else if (regno < AARCH64_V0_REGNUM)
     store_gregs_to_thread (regcache);
     store_sveregs_to_thread (regcache);
   else
     store_fpregs_to_thread (regcache);
+
+  /* Store MTE registers.  */
+  if (tdep->has_mte ()
+      && (regno == tdep->mte_reg_base))
+    store_mteregs_to_thread (regcache);
 }
 
 /* Fill register REGNO (if it is a general-purpose register) in
 
   "pauth_cmask"
 };
 
+static const char *const aarch64_mte_register_names[] =
+{
+  /* Tag Control Register.  */
+  "tag_ctl"
+};
+
 /* AArch64 prologue cache structure.  */
 struct aarch64_prologue_cache
 {
   bool valid_p = true;
   int i, num_regs = 0, num_pseudo_regs = 0;
   int first_pauth_regnum = -1, pauth_ra_state_offset = -1;
+  int first_mte_regnum = -1;
 
   /* Use the vector length passed via the target info.  Here -1 is used for no
      SVE, and 0 is unset.  If unset then use the vector length from the existing
   feature_fpu = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.fpu");
   feature_sve = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.sve");
   feature_pauth = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.pauth");
+  const struct tdesc_feature *feature_mte
+    = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.mte");
 
   if (feature_core == nullptr)
     return nullptr;
       num_pseudo_regs += 1;    /* Count RA_STATE pseudo register.  */
     }
 
+  /* Add the MTE registers.  */
+  if (feature_mte != NULL)
+    {
+      first_mte_regnum = num_regs;
+      /* Validate the descriptor provides the mandatory MTE registers and
+        allocate their numbers.  */
+      for (i = 0; i < ARRAY_SIZE (aarch64_mte_register_names); i++)
+       valid_p &= tdesc_numbered_register (feature_mte, tdesc_data.get (),
+                                           first_mte_regnum + i,
+                                           aarch64_mte_register_names[i]);
+
+      num_regs += i;
+    }
+
   if (!valid_p)
     return nullptr;
 
   tdep->pauth_reg_base = first_pauth_regnum;
   tdep->pauth_ra_state_regnum = (feature_pauth == NULL) ? -1
                                : pauth_ra_state_offset + num_regs;
+  tdep->mte_reg_base = first_mte_regnum;
 
   set_gdbarch_push_dummy_call (gdbarch, aarch64_push_dummy_call);
   set_gdbarch_frame_align (gdbarch, aarch64_frame_align);
 
   {
     return pauth_reg_base != -1;
   }
+
+  /* First MTE register.  This is -1 if no MTE registers are available.  */
+  int mte_reg_base;
+
+  /* Returns true if the target supports MTE.  */
+  bool has_mte () const
+  {
+    return mte_reg_base != -1;
+  }
 };
 
 const target_desc *aarch64_read_description (uint64_t vq, bool pauth_p,
 
 #define HWCAP2_MTE  (1 << 18)
 #endif
 
+/* The MTE regset consists of a single 64-bit register.  */
+#define AARCH64_LINUX_SIZEOF_MTE 8
+
 #endif /* ARCH_AARCH64_LINUX_H */
 
+2021-03-24  Luis Machado  <luis.machado@linaro.org>
+
+       * linux-aarch64-low.cc (aarch64_fill_mteregset): New function.
+       (aarch64_store_mteregset): New function.
+       (aarch64_regsets): Add MTE register set entry.
+       (aarch64_sve_regsets): Add MTE register set entry.
+
 2021-03-24  Luis Machado  <luis.machado@linaro.org>
 
        * linux-aarch64-ipa.cc (get_ipa_tdesc): Update call to
 
                   &pauth_regset[1]);
 }
 
+/* Fill BUF with the MTE registers from the regcache.  */
+
+static void
+aarch64_fill_mteregset (struct regcache *regcache, void *buf)
+{
+  uint64_t *mte_regset = (uint64_t *) buf;
+  int mte_base = find_regno (regcache->tdesc, "tag_ctl");
+
+  collect_register (regcache, mte_base, mte_regset);
+}
+
+/* Store the MTE registers to regcache.  */
+
+static void
+aarch64_store_mteregset (struct regcache *regcache, const void *buf)
+{
+  uint64_t *mte_regset = (uint64_t *) buf;
+  int mte_base = find_regno (regcache->tdesc, "tag_ctl");
+
+  /* Tag Control register */
+  supply_register (regcache, mte_base, mte_regset);
+}
+
 bool
 aarch64_target::low_supports_breakpoints ()
 {
   { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_PAC_MASK,
     AARCH64_PAUTH_REGS_SIZE, OPTIONAL_REGS,
     NULL, aarch64_store_pauthregset },
+  { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_TAGGED_ADDR_CTRL,
+    AARCH64_LINUX_SIZEOF_MTE, OPTIONAL_REGS, aarch64_fill_mteregset,
+    aarch64_store_mteregset },
   NULL_REGSET
 };
 
   { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_PAC_MASK,
     AARCH64_PAUTH_REGS_SIZE, OPTIONAL_REGS,
     NULL, aarch64_store_pauthregset },
+  { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_TAGGED_ADDR_CTRL,
+    AARCH64_LINUX_SIZEOF_MTE, OPTIONAL_REGS, aarch64_fill_mteregset,
+    aarch64_store_mteregset },
   NULL_REGSET
 };
 
 
                                        /*   note name must be "LINUX".  */
 #define NT_ARM_PAC_MASK        0x406           /* AArch pointer authentication code masks */
                                        /*   note name must be "LINUX".  */
+#define NT_ARM_TAGGED_ADDR_CTRL        0x409   /* AArch64 tagged address control
+                                          (prctl()) */
+                                       /*   note name must be "LINUX".  */
 #define NT_ARC_V2      0x600           /* ARC HS accumulator/extra registers.  */
                                        /*   note name must be "LINUX".  */
 #define NT_RISCV_CSR    0x900          /* RISC-V Control and Status Registers */