xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/nat/aarch64-sve-linux-ptrace.c (revision 122b5006ee1bd67145794b4cde92f4fe4781a5ec)
1 /* Common target dependent for AArch64 systems.
2 
3    Copyright (C) 2018-2019 Free Software Foundation, Inc.
4 
5    This file is part of GDB.
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19 
20 #include <sys/utsname.h>
21 #include <sys/uio.h>
22 #include "common/common-defs.h"
23 #include "elf/external.h"
24 #include "elf/common.h"
25 #include "aarch64-sve-linux-ptrace.h"
26 #include "arch/aarch64.h"
27 #include "common/common-regcache.h"
28 #include "common/byte-vector.h"
29 
30 static bool vq_change_warned = false;
31 
32 /* See nat/aarch64-sve-linux-ptrace.h.  */
33 
34 uint64_t
35 aarch64_sve_get_vq (int tid)
36 {
37   struct iovec iovec;
38   struct user_sve_header header;
39 
40   iovec.iov_len = sizeof (header);
41   iovec.iov_base = &header;
42 
43   /* Ptrace gives the vector length in bytes.  Convert it to VQ, the number of
44      128bit chunks in a Z register.  We use VQ because 128bits is the minimum
45      a Z register can increase in size.  */
46 
47   if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_SVE, &iovec) < 0)
48     {
49       /* SVE is not supported.  */
50       return 0;
51     }
52 
53   uint64_t vq = sve_vq_from_vl (header.vl);
54 
55   if (!sve_vl_valid (header.vl))
56     {
57       warning (_("Invalid SVE state from kernel; SVE disabled."));
58       return 0;
59     }
60 
61   return vq;
62 }
63 
64 /* See nat/aarch64-sve-linux-ptrace.h.  */
65 
66 std::unique_ptr<gdb_byte[]>
67 aarch64_sve_get_sveregs (int tid)
68 {
69   struct iovec iovec;
70   uint64_t vq = aarch64_sve_get_vq (tid);
71 
72   if (vq == 0)
73     perror_with_name (_("Unable to fetch SVE register header"));
74 
75   /* A ptrace call with NT_ARM_SVE will return a header followed by either a
76      dump of all the SVE and FP registers, or an fpsimd structure (identical to
77      the one returned by NT_FPREGSET) if the kernel has not yet executed any
78      SVE code.  Make sure we allocate enough space for a full SVE dump.  */
79 
80   iovec.iov_len = SVE_PT_SIZE (vq, SVE_PT_REGS_SVE);
81   std::unique_ptr<gdb_byte[]> buf (new gdb_byte[iovec.iov_len]);
82   iovec.iov_base = buf.get ();
83 
84   if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_SVE, &iovec) < 0)
85     perror_with_name (_("Unable to fetch SVE registers"));
86 
87   return buf;
88 }
89 
90 /* See nat/aarch64-sve-linux-ptrace.h.  */
91 
92 void
93 aarch64_sve_regs_copy_to_reg_buf (struct reg_buffer_common *reg_buf,
94 				  const void *buf)
95 {
96   char *base = (char *) buf;
97   struct user_sve_header *header = (struct user_sve_header *) buf;
98   uint64_t vq, vg_reg_buf = 0;
99 
100   vq = sve_vq_from_vl (header->vl);
101 
102   /* Sanity check the data in the header.  */
103   if (!sve_vl_valid (header->vl)
104       || SVE_PT_SIZE (vq, header->flags) != header->size)
105     error (_("Invalid SVE header from kernel."));
106 
107   if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_VG_REGNUM))
108     reg_buf->raw_collect (AARCH64_SVE_VG_REGNUM, &vg_reg_buf);
109 
110   if (vg_reg_buf == 0)
111     {
112       /* VG has not been set.  */
113       vg_reg_buf = sve_vg_from_vl (header->vl);
114       reg_buf->raw_supply (AARCH64_SVE_VG_REGNUM, &vg_reg_buf);
115     }
116   else if (vg_reg_buf != sve_vg_from_vl (header->vl) && !vq_change_warned)
117     {
118       /* Vector length on the running process has changed.  GDB currently does
119 	 not support this and will result in GDB showing incorrect partially
120 	 incorrect data for the vector registers.  Warn once and continue.  We
121 	 do not expect many programs to exhibit this behaviour.  To fix this
122 	 we need to spot the change earlier and generate a new target
123 	 descriptor.  */
124       warning (_("SVE Vector length has changed (%ld to %d). "
125 		 "Vector registers may show incorrect data."),
126 	       vg_reg_buf, sve_vg_from_vl (header->vl));
127       vq_change_warned = true;
128     }
129 
130   if (HAS_SVE_STATE (*header))
131     {
132       /* The register dump contains a set of SVE registers.  */
133 
134       for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++)
135 	reg_buf->raw_supply (AARCH64_SVE_Z0_REGNUM + i,
136 			     base + SVE_PT_SVE_ZREG_OFFSET (vq, i));
137 
138       for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++)
139 	reg_buf->raw_supply (AARCH64_SVE_P0_REGNUM + i,
140 			     base + SVE_PT_SVE_PREG_OFFSET (vq, i));
141 
142       reg_buf->raw_supply (AARCH64_SVE_FFR_REGNUM,
143 			   base + SVE_PT_SVE_FFR_OFFSET (vq));
144       reg_buf->raw_supply (AARCH64_FPSR_REGNUM,
145 			   base + SVE_PT_SVE_FPSR_OFFSET (vq));
146       reg_buf->raw_supply (AARCH64_FPCR_REGNUM,
147 			   base + SVE_PT_SVE_FPCR_OFFSET (vq));
148     }
149   else
150     {
151       /* There is no SVE state yet - the register dump contains a fpsimd
152 	 structure instead.  These registers still exist in the hardware, but
153 	 the kernel has not yet initialised them, and so they will be null.  */
154 
155       char *zero_reg = (char *) alloca (SVE_PT_SVE_ZREG_SIZE (vq));
156       struct user_fpsimd_state *fpsimd
157 	= (struct user_fpsimd_state *)(base + SVE_PT_FPSIMD_OFFSET);
158 
159       /* Copy across the V registers from fpsimd structure to the Z registers,
160 	 ensuring the non overlapping state is set to null.  */
161 
162       memset (zero_reg, 0, SVE_PT_SVE_ZREG_SIZE (vq));
163 
164       for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++)
165 	{
166 	  memcpy (zero_reg, &fpsimd->vregs[i], sizeof (__int128_t));
167 	  reg_buf->raw_supply (AARCH64_SVE_Z0_REGNUM + i, zero_reg);
168 	}
169 
170       reg_buf->raw_supply (AARCH64_FPSR_REGNUM, &fpsimd->fpsr);
171       reg_buf->raw_supply (AARCH64_FPCR_REGNUM, &fpsimd->fpcr);
172 
173       /* Clear the SVE only registers.  */
174 
175       for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++)
176 	reg_buf->raw_supply (AARCH64_SVE_P0_REGNUM + i, zero_reg);
177 
178       reg_buf->raw_supply (AARCH64_SVE_FFR_REGNUM, zero_reg);
179     }
180 }
181 
182 /* See nat/aarch64-sve-linux-ptrace.h.  */
183 
184 void
185 aarch64_sve_regs_copy_from_reg_buf (const struct reg_buffer_common *reg_buf,
186 				    void *buf)
187 {
188   struct user_sve_header *header = (struct user_sve_header *) buf;
189   char *base = (char *) buf;
190   uint64_t vq, vg_reg_buf = 0;
191 
192   vq = sve_vq_from_vl (header->vl);
193 
194   /* Sanity check the data in the header.  */
195   if (!sve_vl_valid (header->vl)
196       || SVE_PT_SIZE (vq, header->flags) != header->size)
197     error (_("Invalid SVE header from kernel."));
198 
199   if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_VG_REGNUM))
200     reg_buf->raw_collect (AARCH64_SVE_VG_REGNUM, &vg_reg_buf);
201 
202   if (vg_reg_buf != 0 && vg_reg_buf != sve_vg_from_vl (header->vl))
203     {
204       /* Vector length on the running process has changed.  GDB currently does
205 	 not support this and will result in GDB writing invalid data back to
206 	 the vector registers.  Error and exit.  We do not expect many programs
207 	 to exhibit this behaviour.  To fix this we need to spot the change
208 	 earlier and generate a new target descriptor.  */
209       error (_("SVE Vector length has changed (%ld to %d). "
210 	       "Cannot write back registers."),
211 	     vg_reg_buf, sve_vg_from_vl (header->vl));
212     }
213 
214   if (!HAS_SVE_STATE (*header))
215     {
216       /* There is no SVE state yet - the register dump contains a fpsimd
217 	 structure instead.  Where possible we want to write the reg_buf data
218 	 back to the kernel using the fpsimd structure.  However, if we cannot
219 	 then we'll need to reformat the fpsimd into a full SVE structure,
220 	 resulting in the initialization of SVE state written back to the
221 	 kernel, which is why we try to avoid it.  */
222 
223       bool has_sve_state = false;
224       char *zero_reg = (char *) alloca (SVE_PT_SVE_ZREG_SIZE (vq));
225       struct user_fpsimd_state *fpsimd
226 	= (struct user_fpsimd_state *)(base + SVE_PT_FPSIMD_OFFSET);
227 
228       memset (zero_reg, 0, SVE_PT_SVE_ZREG_SIZE (vq));
229 
230       /* Check in the reg_buf if any of the Z registers are set after the
231 	 first 128 bits, or if any of the other SVE registers are set.  */
232 
233       for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++)
234 	{
235 	  has_sve_state |= reg_buf->raw_compare (AARCH64_SVE_Z0_REGNUM + i,
236 						 zero_reg, sizeof (__int128_t));
237 	  if (has_sve_state)
238 	    break;
239 	}
240 
241       if (!has_sve_state)
242 	for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++)
243 	  {
244 	    has_sve_state |= reg_buf->raw_compare (AARCH64_SVE_P0_REGNUM + i,
245 						   zero_reg, 0);
246 	    if (has_sve_state)
247 	      break;
248 	  }
249 
250       if (!has_sve_state)
251 	  has_sve_state |= reg_buf->raw_compare (AARCH64_SVE_FFR_REGNUM,
252 						 zero_reg, 0);
253 
254       /* If no SVE state exists, then use the existing fpsimd structure to
255 	 write out state and return.  */
256       if (!has_sve_state)
257 	{
258 	  /* The collects of the Z registers will overflow the size of a vreg.
259 	     There is enough space in the structure to allow for this, but we
260 	     cannot overflow into the next register as we might not be
261 	     collecting every register.  */
262 
263 	  for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++)
264 	    {
265 	      if (REG_VALID
266 		  == reg_buf->get_register_status (AARCH64_SVE_Z0_REGNUM + i))
267 		{
268 		  reg_buf->raw_collect (AARCH64_SVE_Z0_REGNUM + i, zero_reg);
269 		  memcpy (&fpsimd->vregs[i], zero_reg, sizeof (__int128_t));
270 		}
271 	    }
272 
273 	  if (REG_VALID == reg_buf->get_register_status (AARCH64_FPSR_REGNUM))
274 	    reg_buf->raw_collect (AARCH64_FPSR_REGNUM, &fpsimd->fpsr);
275 	  if (REG_VALID == reg_buf->get_register_status (AARCH64_FPCR_REGNUM))
276 	    reg_buf->raw_collect (AARCH64_FPCR_REGNUM, &fpsimd->fpcr);
277 
278 	  return;
279 	}
280 
281       /* Otherwise, reformat the fpsimd structure into a full SVE set, by
282 	 expanding the V registers (working backwards so we don't splat
283 	 registers before they are copied) and using null for everything else.
284 	 Note that enough space for a full SVE dump was originally allocated
285 	 for base.  */
286 
287       header->flags |= SVE_PT_REGS_SVE;
288       header->size = SVE_PT_SIZE (vq, SVE_PT_REGS_SVE);
289 
290       memcpy (base + SVE_PT_SVE_FPSR_OFFSET (vq), &fpsimd->fpsr,
291 	      sizeof (uint32_t));
292       memcpy (base + SVE_PT_SVE_FPCR_OFFSET (vq), &fpsimd->fpcr,
293 	      sizeof (uint32_t));
294 
295       for (int i = AARCH64_SVE_Z_REGS_NUM; i >= 0 ; i--)
296 	{
297 	  memcpy (base + SVE_PT_SVE_ZREG_OFFSET (vq, i), &fpsimd->vregs[i],
298 		  sizeof (__int128_t));
299 	}
300     }
301 
302   /* Replace the kernel values with those from reg_buf.  */
303 
304   for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++)
305     if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_Z0_REGNUM + i))
306       reg_buf->raw_collect (AARCH64_SVE_Z0_REGNUM + i,
307 			    base + SVE_PT_SVE_ZREG_OFFSET (vq, i));
308 
309   for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++)
310     if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_P0_REGNUM + i))
311       reg_buf->raw_collect (AARCH64_SVE_P0_REGNUM + i,
312 			    base + SVE_PT_SVE_PREG_OFFSET (vq, i));
313 
314   if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_FFR_REGNUM))
315     reg_buf->raw_collect (AARCH64_SVE_FFR_REGNUM,
316 			  base + SVE_PT_SVE_FFR_OFFSET (vq));
317   if (REG_VALID == reg_buf->get_register_status (AARCH64_FPSR_REGNUM))
318     reg_buf->raw_collect (AARCH64_FPSR_REGNUM,
319 			  base + SVE_PT_SVE_FPSR_OFFSET (vq));
320   if (REG_VALID == reg_buf->get_register_status (AARCH64_FPCR_REGNUM))
321     reg_buf->raw_collect (AARCH64_FPCR_REGNUM,
322 			  base + SVE_PT_SVE_FPCR_OFFSET (vq));
323 
324 }
325