@@ -772,6 +772,7 @@ ALL_64_TARGET_OBS = \
ia64-tdep.o \
ia64-vms-tdep.o \
mips64-obsd-tdep.o \
+ sparc64-adi-tdep.o \
sparc64-fbsd-tdep.o \
sparc64-linux-tdep.o \
sparc64-nbsd-tdep.o \
@@ -2660,6 +2661,7 @@ ALLDEPFILES = \
sparc-sol2-nat.c \
sparc-sol2-tdep.c \
sparc-tdep.c \
+ sparc64-adi-tdep.c \
sparc64-fbsd-nat.c \
sparc64-fbsd-tdep.c \
sparc64-linux-nat.c \
@@ -548,6 +548,7 @@ sparc64-*-linux*)
gdb_target_obs="sparc64-tdep.o sparc64-sol2-tdep.o sol2-tdep.o \
sparc64-linux-tdep.o sparc-tdep.o sparc-sol2-tdep.o \
sparc-linux-tdep.o solib-svr4.o linux-tdep.o \
+ sparc64-adi-tdep.o \
ravenscar-thread.o sparc-ravenscar-thread.o"
build_gdbserver=yes
;;
@@ -22465,6 +22465,7 @@ all uses of @value{GDBN} with the architecture, both native and cross.
* SPU:: Cell Broadband Engine SPU architecture
* PowerPC::
* Nios II::
+* Sparc64::
@end menu
@node AArch64
@@ -22849,6 +22850,89 @@ target code in @value{GDBN}.
Show the current setting of Nios II debugging messages.
@end table
+@node Sparc64
+@subsection Sparc64
+@cindex Sparc64 support
+@subsubsection ADI Support
+
+The M7 processor supports an Application Data Integrity (ADI) feature that
+detects invalid data accesses. When software allocates memory and enables ADI on the
+allocated memory, it chooses a 4-bit version number, sets the version in the upper 4 bits
+of the 64-bit pointer to that data, and stores the 4-bit version in every cacheline of
+that data. Hardware saves the latter in spare bits in the cache and memory hierarchy.
+On each load and store, the processor compares the upper 4 VA (virtual address) bits to
+the cacheline's version. If there is a mismatch, the processor generates a version
+mismatch trap which can be either precise or disrupting. The trap is an error condition
+which the kernel delivers to the process as a SIGSEGV signal.
+
+Note that only 64-bit applications can use ADI and need to be built with
+ADI-enabled.
+
+Values of the ADI version tags, which are in granularity of a
+cacheline (64 bytes), can be viewed or modified.
+
+
+@table @code
+@item adi examine Command
+
+The adi examine command displays the value of one ADI version tag per cacheline.
+It has the following command syntax:
+
+@cindex examining memory
+@table @code
+@kindex x @r{(examine memory)}
+@itemx adi (examine | x) [ / @var{n} ] @var{addr}
+@end table
+
+@table @r
+@item @var{n}, the byte count
+The count is a decimal integer specifying the number in bytes; the default is 1. It specifies
+how much ADI version information, at the ratio of 1:ADI block size, to display.
+
+@item @var{addr}, starting display address
+@var{addr} is the address in user address space where you want @value{GDBN} to begin displaying
+the ADI version tags.
+@end table
+
+Below is an example of displaying ADI versions of variable "shmaddr".
+
+@smallexample
+(@value{GDBP}) adi x/100 shmaddr
+ 0xfff800010002c000: 0 0
+@end smallexample
+
+@item adi assign Command
+
+The adi assign command is used to assign new ADI version tag
+to an address. It has the following command syntax:
+
+@cindex examining memory
+@table @code
+@kindex x @r{(examine memory)}
+@itemx adi (assign | a) [ / @var{n} ] @var{addr} = @var{tag}
+@end table
+
+@table @r
+@item @var{n}, the byte count
+The count is a decimal integer specifying the number in bytes; the default is 1. It specifies
+how much ADI version information, at the ratio of 1:ADI block size, to modify.
+
+@item @var{addr}, starting display address
+@var{addr} is the address in user address space where you want @value{GDBN}
+to begin modifying the ADI version tags.
+@item @var{tag}, the new ADI version tag
+@end table
+
+For example, do the following to modify then verify ADI versions of variable "shmaddr":
+
+@smallexample
+(@value{GDBP}) adi a/100 shmaddr = 7
+(@value{GDBP}) adi x/100 shmaddr
+ 0xfff800010002c000: 7 7
+@end smallexample
+
+@end table
+
@node Controlling GDB
@chapter Controlling @value{GDBN}
new file mode 100644
@@ -0,0 +1,476 @@
+/* Target-dependent code for Sparc64.
+
+ Copyright (C) 2017 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/>. */
+
+#include <adi.h>
+#include "defs.h"
+#include "inferior.h"
+#include "gdbcmd.h"
+#include "auxv.h"
+
+/* The M7 processor supports an Application Data Integrity (ADI) feature
+ that detects invalid data accesses. When software allocates memory and
+ enables ADI on the allocated memory, it chooses a 4-bit version number,
+ sets the version in the upper 4 bits of the 64-bit pointer to that data,
+ and stores the 4-bit version in every cacheline of the object. Hardware
+ saves the latter in spare bits in the cache and memory hierarchy. On each
+ load and store, the processor compares the upper 4 VA (virtual address) bits
+ to the cacheline's version. If there is a mismatch, the processor generates
+ a version mismatch trap which can be either precise or disrupting.
+ The trap is an error condition which the kernel delivers to the process
+ as a SIGSEGV signal.
+
+ The upper 4 bits of the VA represent a version and are not part of the
+ true address. The processor clears these bits and sign extends bit 59
+ to generate the true address.
+
+ Note that 32-bit applications cannot use ADI. */
+
+
+#define MAX_PROC_NAME_SIZE sizeof("/proc/99999/lwp/9999/adi/lstatus")
+
+/* ADI command list. */
+static struct cmd_list_element *sparc64adilist = NULL;
+
+/* Current ADI stat settings. */
+static struct
+{
+ /* The ADI block size. */
+ unsigned long blksize;
+
+ /* Number of bits used for an ADI version tag which can be
+ * used together with the shift value for an ADI version tag
+ * to encode or extract the ADI version value in a pointer. */
+ unsigned long nbits;
+
+ /* The maximum ADI version tag value supported. */
+ int max_version;
+
+ /* ADI version tag file. */
+ int tag_fd;
+
+ /* Last ADI address examined. */
+ CORE_ADDR last_vaddr;
+
+ /* Last specified examination count. */
+ int last_cnt;
+
+ /* ADI availability check has been done. */
+ bool checked_avail;
+
+ /* ADI is available. */
+ bool is_avail;
+
+} adi_stat;
+
+
+static void
+info_adi_command (char *args, int from_tty)
+{
+ printf_unfiltered ("\"adi\" must be followed by \"examine\" "
+ "or \"assign\".\n");
+ help_list (sparc64adilist, "adi ", all_commands, gdb_stdout);
+}
+
+static bool
+arch_is_64bit (void)
+{
+ struct gdbarch *gdbarch = get_frame_arch (get_current_frame ());
+ return gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 64;
+
+}
+
+static int
+parse_byte_count (const char **sptr)
+{
+ const char *p = *sptr;
+ int cnt = 1;
+ if (*p == '-')
+ {
+ cnt = -1;
+ p++;
+ }
+ if (*p >= '0' && *p <= '9')
+ cnt *= atoi (p);
+ while (*p >= '0' && *p <= '9')
+ p++;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ *sptr = p;
+
+ if (cnt <= 0)
+ error (_("Byte count should have a positive value."));
+
+ return cnt;
+}
+
+static CORE_ADDR
+parse_address (const char *exp)
+{
+ expression_up expr = parse_expression (exp);
+ struct value *val = evaluate_expression (expr.get ());
+ if (TYPE_CODE (value_type (val)) == TYPE_CODE_REF)
+ val = coerce_ref (val);
+ if (TYPE_CODE (value_type (val)) == TYPE_CODE_FUNC
+ && VALUE_LVAL (val) == lval_memory)
+ return value_address (val);
+ else
+ return value_as_address (val);
+}
+
+/* Read attributes of a maps entry in /proc/[pid]/adi/maps. */
+
+static void
+read_maps_entry (const char *line,
+ ULONGEST *addr, ULONGEST *endaddr)
+{
+ const char *p = line;
+
+ *addr = strtoulst (p, &p, 16);
+ if (*p == '-')
+ p++;
+
+ *endaddr = strtoulst (p, &p, 16);
+}
+
+/* Check if ADI is available. */
+
+static bool
+adi_available (void)
+{
+ if (adi_stat.checked_avail)
+ return adi_stat.is_avail;
+
+ adi_stat.checked_avail = true;
+ if (!arch_is_64bit ())
+ return false;
+ if (target_auxv_search (¤t_target, AT_ADI_BLKSZ, &adi_stat.blksize) <= 0)
+ return false;
+ target_auxv_search (¤t_target, AT_ADI_NBITS, &adi_stat.nbits);
+ adi_stat.max_version = (1 << adi_stat.nbits) - 2;
+ adi_stat.is_avail = true;
+
+ return adi_stat.is_avail;
+}
+
+#if 0
+/* Extract version tag from a versioned address. */
+
+int adi_extract_version (CORE_ADDR versioned_addr)
+{
+ return ((unsigned long)versioned_addr) >> (64 - adi_stat.nbits);
+}
+#endif
+
+/* Normalize a versioned address - a VA with ADI bits (63-60) set. */
+
+CORE_ADDR
+adi_normalize_address (CORE_ADDR addr)
+{
+ if (adi_stat.nbits)
+ return ((CORE_ADDR)(((long)addr << adi_stat.nbits) >> adi_stat.nbits));
+ return addr;
+}
+
+/* Align a normalized address - a VA with bit 59 sign extended into ADI bits. */
+
+static CORE_ADDR
+adi_align_address (CORE_ADDR naddr)
+{
+ return (naddr - (naddr % adi_stat.blksize)) / adi_stat.blksize;
+}
+
+/* Convert a byte count to count at a ratio of 1:adi_blksz. */
+
+static int
+adi_convert_byte_count (CORE_ADDR naddr, int nbytes, CORE_ADDR locl)
+{
+ return ((naddr + nbytes + adi_stat.blksize - 1) / adi_stat.blksize) - locl;
+}
+
+/* The /proc/[pid]/adi/tag file, which allows gdb to get/set ADI
+ version in a target process, maps linearly to the address space
+ of the target process at a ratio of 1:adi_blksz.
+
+ A read (or write) at offset K in the file returns (or modifies)
+ the ADI version tag stored in the cacheline containing address
+ K * adi_blksz, encoded as 1 version tag per byte. The allowed
+ version tag values are between 0 and adi_stat.max_version. */
+
+static int
+adi_tag_fd (void)
+{
+ if (adi_stat.tag_fd != 0)
+ return adi_stat.tag_fd;
+
+ char cl_name[MAX_PROC_NAME_SIZE];
+ pid_t pid = ptid_get_pid (inferior_ptid);
+ snprintf (cl_name, sizeof(cl_name), "/proc/%d/adi/tag", pid);
+ adi_stat.tag_fd = open (cl_name, O_RDWR|O_EXCL|O_CLOEXEC);
+
+ return adi_stat.tag_fd;
+}
+
+/* Check if an address set is ADI enabled, using /proc/[pid]/adi/maps
+ which was exported by the kernel and contains the currently ADI
+ mapped memory regions and their access permissions. */
+
+static bool
+adi_is_addr_mapped (CORE_ADDR vaddr, size_t cnt)
+{
+ char filename[MAX_PROC_NAME_SIZE];
+ size_t i = 0;
+
+ pid_t pid = ptid_get_pid (inferior_ptid);
+ snprintf (filename, sizeof filename, "/proc/%d/adi/maps", pid);
+ char *data = target_fileio_read_stralloc (NULL, filename);
+ if (data)
+ {
+ struct cleanup *cleanup = make_cleanup (xfree, data);
+ char *line;
+ for (line = strtok (data, "\n"); line; line = strtok (NULL, "\n"))
+ {
+ ULONGEST addr, endaddr;
+
+ read_maps_entry (line, &addr, &endaddr);
+
+ while (((vaddr + i) * adi_stat.blksize) >= addr
+ && ((vaddr + i) * adi_stat.blksize) < endaddr)
+ {
+ if (++i == cnt)
+ {
+ do_cleanups (cleanup);
+ return true;
+ }
+ }
+ }
+ do_cleanups (cleanup);
+ }
+ else
+ warning (_("unable to open /proc file '%s'"), filename);
+
+ return false;
+}
+
+/* Read ADI version tag value for memory locations starting at "vaddr"
+ for "size" number of bytes. */
+
+static int
+adi_read_versions (CORE_ADDR vaddr, size_t size, unsigned char *tags)
+{
+ int fd = adi_tag_fd ();
+ if (fd == -1)
+ return -1;
+
+ if (!adi_is_addr_mapped (vaddr, size))
+ error(_("Address at 0x%lx is not in ADI maps"), vaddr*adi_stat.blksize);
+
+ return pread64 (fd, tags, size, vaddr);
+}
+
+/* Write ADI version tag for memory locations starting at "vaddr" for
+ "size" number of bytes to "tags". */
+
+static int
+adi_write_versions (CORE_ADDR vaddr, size_t size, unsigned char *tags)
+{
+ int fd = adi_tag_fd ();
+ if (fd == -1)
+ return -1;
+
+ if (!adi_is_addr_mapped (vaddr, size))
+ error(_("Address at 0x%lx is not in ADI maps"), vaddr*adi_stat.blksize);
+
+ return pwrite64 (fd, tags, size, vaddr);
+}
+
+/* Print ADI version tag value in "tags" for memory locations starting
+ at "vaddr" with number of "cnt". */
+
+static void
+adi_print_versions (CORE_ADDR vaddr, size_t cnt, unsigned char *tags)
+{
+ int v_idx = 0;
+ const int maxelts = 8; /* # of elements per line */
+
+ while (cnt > 0)
+ {
+ QUIT;
+ printf_filtered ("0x%016lx:\t", vaddr * adi_stat.blksize);
+ for (int i = maxelts; i > 0 && cnt > 0; i--, cnt--)
+ {
+ if (tags[v_idx] == 0xff) /* no version tag */
+ printf_filtered ("- ");
+ else
+ printf_filtered ("%1X ", tags[v_idx]);
+ ++v_idx;
+ }
+ printf_filtered ("\n");
+ gdb_flush (gdb_stdout);
+ vaddr += maxelts;
+ }
+}
+
+static void
+do_examine (CORE_ADDR start, int bcnt)
+{
+ CORE_ADDR vaddr = adi_normalize_address (start);
+ struct cleanup *cleanup;
+
+ CORE_ADDR vstart = adi_align_address (vaddr);
+ int cnt = adi_convert_byte_count (vaddr, bcnt, vstart);
+ unsigned char *buf = (unsigned char *) xmalloc (cnt);
+ cleanup = make_cleanup (xfree, buf);
+ int read_cnt = adi_read_versions (vstart, cnt, buf);
+ if (read_cnt == -1)
+ error (_("No ADI information"));
+ else if (read_cnt < cnt)
+ error(_("No ADI information at 0x%lx"), vaddr);
+
+ adi_print_versions(vstart, cnt, buf);
+
+ do_cleanups (cleanup);
+}
+
+static void
+do_assign (CORE_ADDR start, size_t bcnt, int version)
+{
+ CORE_ADDR vaddr = adi_normalize_address (start);
+
+ CORE_ADDR vstart = adi_align_address (vaddr);
+ int cnt = adi_convert_byte_count (vaddr, bcnt, vstart);
+ unsigned char *buf = (unsigned char *) xmalloc (cnt);
+ memset(buf, version, cnt);
+
+ int set_cnt = adi_write_versions (vstart, cnt, buf);
+ xfree (buf);
+
+ if (set_cnt == -1)
+ error (_("No ADI information"));
+ else if (set_cnt < cnt)
+ error(_("No ADI information at 0x%lx"), vaddr);
+
+}
+
+/* ADI examine version tag command.
+
+ Command syntax:
+
+ adi (examine|x)/count <addr> */
+
+static void
+adi_examine_command (char *args, int from_tty)
+{
+ /* make sure program is active and adi is available */
+ if (!target_has_execution)
+ error (_("ADI command requires a live process/thread"));
+
+ if (!adi_available ())
+ error (_("No ADI information"));
+
+ int cnt = adi_stat.last_cnt? adi_stat.last_cnt : 1;
+ const char *p = args;
+ if (p && *p == '/')
+ {
+ p++;
+ cnt = parse_byte_count (&p);
+ }
+
+ CORE_ADDR next_address = adi_stat.last_vaddr ? adi_stat.last_vaddr : 0;
+ if (p != 0 && *p != 0)
+ next_address = parse_address (p);
+ else if (!adi_stat.last_cnt)
+ error (_("Usage: adi examine|x[/count] <addr>"));
+
+ if (next_address)
+ do_examine(next_address, cnt);
+
+ adi_stat.last_cnt = cnt;
+ adi_stat.last_vaddr = next_address + adi_stat.blksize * cnt;
+}
+
+/* ADI assign version tag command.
+
+ Command syntax:
+
+ adi (assign|a)/count <addr> = <version> */
+
+static void
+adi_assign_command (char *args, int from_tty)
+{
+ /* make sure program is active and adi is available */
+ if (!target_has_execution)
+ error (_("ADI command requires a live process/thread"));
+
+ if (!adi_available ())
+ error (_("No ADI information"));
+
+ const char *exp = args;
+ if (exp == 0)
+ error_no_arg (_("Usage: adi assign|a[/count] <addr> = <version>"));
+
+ char *q = (char *) strchr (exp, '=');
+ if (q)
+ *q++ = 0;
+ else
+ error (_("Usage: adi assign|a[/count] <addr> = <version>"));
+
+ size_t cnt = 1;
+ const char *p = exp;
+ if (exp && *exp == '/')
+ {
+ p = exp + 1;
+ cnt = parse_byte_count (&p);
+ }
+
+ CORE_ADDR next_address = 0;
+ if (p != 0 && *p != 0)
+ next_address = parse_address (p);
+ else
+ error (_("Usage: adi assign|a[/count] <addr> = <version>"));
+
+ int version = 0;
+ if (q) /* parse version tag */
+ {
+ expression_up expr = parse_expression (q);
+ struct value *val = evaluate_expression (expr.get ());
+ version = value_as_long (val);
+ if (version < 0 || version > adi_stat.max_version)
+ error (_("Invalid ADI version tag %d"), version);
+ }
+
+ do_assign(next_address, cnt, version);
+}
+
+extern initialize_file_ftype _initialize_sparc64_adi_tdep; /* -Wmissing-prototypes */
+
+void
+_initialize_sparc64_adi_tdep (void)
+{
+
+ add_prefix_cmd ("adi", class_support, info_adi_command,
+ _("ADI version related commands."),
+ &sparc64adilist, "adi ", 0, &cmdlist);
+ add_cmd ("examine", class_support, adi_examine_command,
+ _("Examine ADI versions."), &sparc64adilist);
+ add_alias_cmd ("x", "examine", no_class, 1, &sparc64adilist);
+ add_cmd ("assign", class_support, adi_assign_command,
+ _("Assign ADI versions."), &sparc64adilist);
+
+}
+
@@ -69,6 +69,18 @@ fill_fpregset (const struct regcache *regcache,
sparc64_collect_fpregset (&sparc64_bsd_fpregmap, regcache, regnum, fpregs);
}
+/* Implement the "to_watchpoint_addr_within_range" target_ops method. */
+
+static int
+sparc64_linux_watchpoint_addr_within_range (struct target_ops *target,
+ CORE_ADDR addr,
+ CORE_ADDR start, int length)
+{
+ start = adi_normalize_address (start);
+ addr = adi_normalize_address (addr);
+ return (start <= addr) && (start + length - 1 >= addr);
+}
+
/* Provide a prototype to silence -Wmissing-prototypes. */
void _initialize_sparc64_linux_nat (void);
@@ -86,6 +98,9 @@ _initialize_sparc64_linux_nat (void)
t->to_fetch_registers = sparc_fetch_inferior_registers;
t->to_store_registers = sparc_store_inferior_registers;
+ t->to_watchpoint_addr_within_range =
+ sparc64_linux_watchpoint_addr_within_range;
+
/* Register the target. */
linux_nat_add_target (t);
@@ -32,6 +32,7 @@
#include "tramp-frame.h"
#include "xml-syscall.h"
#include "linux-tdep.h"
+#include <adi.h>
/* The syscall's XML filename for sparc 64-bit. */
#define XML_SYSCALL_FILENAME_SPARC64 "syscalls/sparc64-linux.xml"
@@ -104,6 +105,52 @@ sparc64_linux_sigframe_init (const struct tramp_frame *self,
}
trad_frame_set_id (this_cache, frame_id_build (base, func));
}
+
+/* sparc64 GNU/Linux implementation of the handle_segmentation_fault
+ gdbarch hook.
+ Displays information related to ADI memory corruptions. */
+
+void
+sparc64_linux_handle_segmentation_fault (struct gdbarch *gdbarch,
+ struct ui_out *uiout)
+{
+ if (gdbarch_bfd_arch_info (gdbarch)->bits_per_word != 64)
+ return;
+
+ CORE_ADDR addr = 0;
+ long si_code = 0;
+
+ TRY
+ {
+ /* Evaluate si_code to see if the segfault is ADI related. */
+ si_code = parse_and_eval_long ("$_siginfo.si_code\n");
+
+ if (si_code >= SEGV_ACCADI && si_code <= SEGV_ADIPERR)
+ addr = parse_and_eval_long ("$_siginfo._sifields._sigfault.si_addr");
+ }
+ CATCH (exception, RETURN_MASK_ALL)
+ {
+ return;
+ }
+ END_CATCH
+
+ /* Print out ADI event based on sig_code value */
+ switch (si_code)
+ {
+ case SEGV_ACCADI: /* adi not enabled */
+ printf_unfiltered (" (ADI disabled at address 0x%lx)\n", addr);
+ break;
+ case SEGV_ADIDERR: /* disrupting mismatch */
+ printf_unfiltered (" (ADI deferred mismatch at address 0x%lx)\n", addr);
+ break;
+ case SEGV_ADIPERR: /* precise mismatch */
+ printf_unfiltered (" (ADI precise mismatch at address 0x%lx)\n", addr);
+ break;
+ default:
+ break;
+ }
+}
+
/* Return the address of a system call's alternative return
address. */
@@ -338,6 +385,8 @@ sparc64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
set_xml_syscall_file_name (gdbarch, XML_SYSCALL_FILENAME_SPARC64);
set_gdbarch_get_syscall_number (gdbarch,
sparc64_linux_get_syscall_number);
+ set_gdbarch_handle_segmentation_fault (gdbarch,
+ sparc64_linux_handle_segmentation_fault);
}
@@ -165,7 +165,7 @@ sparc64_pstate_type (struct gdbarch *gdbarch)
append_flags_type_flag (type, 8, "TLE");
append_flags_type_flag (type, 9, "CLE");
append_flags_type_flag (type, 10, "PID0");
- append_flags_type_flag (type, 11, "PID1");
+ append_flags_type_flag (type, 11, "MCDE");
tdep->sparc64_pstate_type = type;
}
@@ -113,6 +113,8 @@ extern void sparc64_collect_fpregset (const struct sparc_fpregmap *fpregmap,
const struct regcache *regcache,
int regnum, void *fpregs);
+extern CORE_ADDR adi_normalize_address (CORE_ADDR addr);
+
/* Functions and variables exported from sparc64-sol2-tdep.c. */
/* Register offsets for Solaris 2. */