Commit | Line | Data |
---|---|---|
122394f1 AH |
1 | /* Common target dependent for AArch64 systems. |
2 | ||
3666a048 | 3 | Copyright (C) 2018-2021 Free Software Foundation, Inc. |
122394f1 AH |
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> | |
268a13a5 | 22 | #include "gdbsupport/common-defs.h" |
122394f1 AH |
23 | #include "elf/external.h" |
24 | #include "elf/common.h" | |
25 | #include "aarch64-sve-linux-ptrace.h" | |
26 | #include "arch/aarch64.h" | |
268a13a5 TT |
27 | #include "gdbsupport/common-regcache.h" |
28 | #include "gdbsupport/byte-vector.h" | |
6afcd2d4 | 29 | #include <endian.h> |
e9902bfc | 30 | |
122394f1 AH |
31 | /* See nat/aarch64-sve-linux-ptrace.h. */ |
32 | ||
39bfb937 | 33 | uint64_t |
122394f1 AH |
34 | aarch64_sve_get_vq (int tid) |
35 | { | |
36 | struct iovec iovec; | |
37 | struct user_sve_header header; | |
38 | ||
39 | iovec.iov_len = sizeof (header); | |
40 | iovec.iov_base = &header; | |
41 | ||
42 | /* Ptrace gives the vector length in bytes. Convert it to VQ, the number of | |
43 | 128bit chunks in a Z register. We use VQ because 128bits is the minimum | |
44 | a Z register can increase in size. */ | |
45 | ||
46 | if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_SVE, &iovec) < 0) | |
47 | { | |
48 | /* SVE is not supported. */ | |
49 | return 0; | |
50 | } | |
51 | ||
e9902bfc | 52 | uint64_t vq = sve_vq_from_vl (header.vl); |
122394f1 AH |
53 | |
54 | if (!sve_vl_valid (header.vl)) | |
55 | { | |
56 | warning (_("Invalid SVE state from kernel; SVE disabled.")); | |
57 | return 0; | |
58 | } | |
59 | ||
60 | return vq; | |
61 | } | |
e9902bfc AH |
62 | |
63 | /* See nat/aarch64-sve-linux-ptrace.h. */ | |
64 | ||
48574d91 AH |
65 | bool |
66 | aarch64_sve_set_vq (int tid, uint64_t vq) | |
67 | { | |
68 | struct iovec iovec; | |
69 | struct user_sve_header header; | |
70 | ||
71 | iovec.iov_len = sizeof (header); | |
72 | iovec.iov_base = &header; | |
73 | ||
74 | if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_SVE, &iovec) < 0) | |
75 | { | |
76 | /* SVE is not supported. */ | |
77 | return false; | |
78 | } | |
79 | ||
80 | header.vl = sve_vl_from_vq (vq); | |
81 | ||
82 | if (ptrace (PTRACE_SETREGSET, tid, NT_ARM_SVE, &iovec) < 0) | |
83 | { | |
84 | /* Vector length change failed. */ | |
85 | return false; | |
86 | } | |
87 | ||
88 | return true; | |
89 | } | |
90 | ||
91 | /* See nat/aarch64-sve-linux-ptrace.h. */ | |
92 | ||
93 | bool | |
94 | aarch64_sve_set_vq (int tid, struct reg_buffer_common *reg_buf) | |
95 | { | |
2d07da27 LM |
96 | uint64_t reg_vg = 0; |
97 | ||
98 | /* The VG register may not be valid if we've not collected any value yet. | |
99 | This can happen, for example, if we're restoring the regcache after an | |
100 | inferior function call, and the VG register comes after the Z | |
101 | registers. */ | |
48574d91 | 102 | if (reg_buf->get_register_status (AARCH64_SVE_VG_REGNUM) != REG_VALID) |
2d07da27 LM |
103 | { |
104 | /* If vg is not available yet, fetch it from ptrace. The VG value from | |
105 | ptrace is likely the correct one. */ | |
106 | uint64_t vq = aarch64_sve_get_vq (tid); | |
48574d91 | 107 | |
2d07da27 LM |
108 | /* If something went wrong, just bail out. */ |
109 | if (vq == 0) | |
110 | return false; | |
111 | ||
112 | reg_vg = sve_vg_from_vq (vq); | |
113 | } | |
114 | else | |
115 | reg_buf->raw_collect (AARCH64_SVE_VG_REGNUM, ®_vg); | |
48574d91 AH |
116 | |
117 | return aarch64_sve_set_vq (tid, sve_vq_from_vg (reg_vg)); | |
118 | } | |
119 | ||
120 | /* See nat/aarch64-sve-linux-ptrace.h. */ | |
121 | ||
e9902bfc AH |
122 | std::unique_ptr<gdb_byte[]> |
123 | aarch64_sve_get_sveregs (int tid) | |
124 | { | |
125 | struct iovec iovec; | |
e9902bfc AH |
126 | uint64_t vq = aarch64_sve_get_vq (tid); |
127 | ||
128 | if (vq == 0) | |
129 | perror_with_name (_("Unable to fetch SVE register header")); | |
130 | ||
131 | /* A ptrace call with NT_ARM_SVE will return a header followed by either a | |
132 | dump of all the SVE and FP registers, or an fpsimd structure (identical to | |
133 | the one returned by NT_FPREGSET) if the kernel has not yet executed any | |
134 | SVE code. Make sure we allocate enough space for a full SVE dump. */ | |
135 | ||
136 | iovec.iov_len = SVE_PT_SIZE (vq, SVE_PT_REGS_SVE); | |
137 | std::unique_ptr<gdb_byte[]> buf (new gdb_byte[iovec.iov_len]); | |
138 | iovec.iov_base = buf.get (); | |
139 | ||
140 | if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_SVE, &iovec) < 0) | |
141 | perror_with_name (_("Unable to fetch SVE registers")); | |
142 | ||
143 | return buf; | |
144 | } | |
145 | ||
6afcd2d4 LM |
146 | /* If we are running in BE mode, byteswap the contents |
147 | of SRC to DST for SIZE bytes. Other, just copy the contents | |
148 | from SRC to DST. */ | |
149 | ||
150 | static void | |
151 | aarch64_maybe_swab128 (gdb_byte *dst, const gdb_byte *src, size_t size) | |
152 | { | |
153 | gdb_assert (src != nullptr && dst != nullptr); | |
154 | gdb_assert (size > 1); | |
155 | ||
156 | #if (__BYTE_ORDER == __BIG_ENDIAN) | |
157 | for (int i = 0; i < size - 1; i++) | |
158 | dst[i] = src[size - i]; | |
159 | #else | |
160 | memcpy (dst, src, size); | |
161 | #endif | |
162 | } | |
163 | ||
e9902bfc AH |
164 | /* See nat/aarch64-sve-linux-ptrace.h. */ |
165 | ||
166 | void | |
167 | aarch64_sve_regs_copy_to_reg_buf (struct reg_buffer_common *reg_buf, | |
168 | const void *buf) | |
169 | { | |
170 | char *base = (char *) buf; | |
171 | struct user_sve_header *header = (struct user_sve_header *) buf; | |
e9902bfc | 172 | |
48574d91 AH |
173 | uint64_t vq = sve_vq_from_vl (header->vl); |
174 | uint64_t vg = sve_vg_from_vl (header->vl); | |
e9902bfc AH |
175 | |
176 | /* Sanity check the data in the header. */ | |
177 | if (!sve_vl_valid (header->vl) | |
178 | || SVE_PT_SIZE (vq, header->flags) != header->size) | |
179 | error (_("Invalid SVE header from kernel.")); | |
180 | ||
48574d91 AH |
181 | /* Update VG. Note, the registers in the regcache will already be of the |
182 | correct length. */ | |
183 | reg_buf->raw_supply (AARCH64_SVE_VG_REGNUM, &vg); | |
e9902bfc AH |
184 | |
185 | if (HAS_SVE_STATE (*header)) | |
186 | { | |
187 | /* The register dump contains a set of SVE registers. */ | |
188 | ||
189 | for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) | |
190 | reg_buf->raw_supply (AARCH64_SVE_Z0_REGNUM + i, | |
191 | base + SVE_PT_SVE_ZREG_OFFSET (vq, i)); | |
192 | ||
193 | for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++) | |
194 | reg_buf->raw_supply (AARCH64_SVE_P0_REGNUM + i, | |
195 | base + SVE_PT_SVE_PREG_OFFSET (vq, i)); | |
196 | ||
197 | reg_buf->raw_supply (AARCH64_SVE_FFR_REGNUM, | |
198 | base + SVE_PT_SVE_FFR_OFFSET (vq)); | |
199 | reg_buf->raw_supply (AARCH64_FPSR_REGNUM, | |
200 | base + SVE_PT_SVE_FPSR_OFFSET (vq)); | |
201 | reg_buf->raw_supply (AARCH64_FPCR_REGNUM, | |
202 | base + SVE_PT_SVE_FPCR_OFFSET (vq)); | |
203 | } | |
204 | else | |
205 | { | |
6afcd2d4 LM |
206 | /* WARNING: SIMD state is laid out in memory in target-endian format, |
207 | while SVE state is laid out in an endianness-independent format (LE). | |
208 | ||
209 | So we have a couple cases to consider: | |
210 | ||
211 | 1 - If the target is big endian, then SIMD state is big endian, | |
212 | requiring a byteswap. | |
213 | ||
214 | 2 - If the target is little endian, then SIMD state is little endian, | |
215 | which matches the SVE format, so no byteswap is needed. */ | |
216 | ||
e9902bfc AH |
217 | /* There is no SVE state yet - the register dump contains a fpsimd |
218 | structure instead. These registers still exist in the hardware, but | |
219 | the kernel has not yet initialised them, and so they will be null. */ | |
220 | ||
6afcd2d4 | 221 | gdb_byte *reg = (gdb_byte *) alloca (SVE_PT_SVE_ZREG_SIZE (vq)); |
e9902bfc AH |
222 | struct user_fpsimd_state *fpsimd |
223 | = (struct user_fpsimd_state *)(base + SVE_PT_FPSIMD_OFFSET); | |
224 | ||
6afcd2d4 LM |
225 | /* Make sure we have a zeroed register buffer. We will need the zero |
226 | padding below. */ | |
227 | memset (reg, 0, SVE_PT_SVE_ZREG_SIZE (vq)); | |
228 | ||
e9902bfc AH |
229 | /* Copy across the V registers from fpsimd structure to the Z registers, |
230 | ensuring the non overlapping state is set to null. */ | |
231 | ||
e9902bfc AH |
232 | for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) |
233 | { | |
6afcd2d4 LM |
234 | /* Handle big endian/little endian SIMD/SVE conversion. */ |
235 | aarch64_maybe_swab128 (reg, (const gdb_byte *) &fpsimd->vregs[i], | |
236 | V_REGISTER_SIZE); | |
237 | reg_buf->raw_supply (AARCH64_SVE_Z0_REGNUM + i, reg); | |
e9902bfc AH |
238 | } |
239 | ||
240 | reg_buf->raw_supply (AARCH64_FPSR_REGNUM, &fpsimd->fpsr); | |
241 | reg_buf->raw_supply (AARCH64_FPCR_REGNUM, &fpsimd->fpcr); | |
242 | ||
243 | /* Clear the SVE only registers. */ | |
6afcd2d4 | 244 | memset (reg, 0, SVE_PT_SVE_ZREG_SIZE (vq)); |
e9902bfc AH |
245 | |
246 | for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++) | |
6afcd2d4 | 247 | reg_buf->raw_supply (AARCH64_SVE_P0_REGNUM + i, reg); |
e9902bfc | 248 | |
6afcd2d4 | 249 | reg_buf->raw_supply (AARCH64_SVE_FFR_REGNUM, reg); |
e9902bfc AH |
250 | } |
251 | } | |
252 | ||
253 | /* See nat/aarch64-sve-linux-ptrace.h. */ | |
254 | ||
255 | void | |
256 | aarch64_sve_regs_copy_from_reg_buf (const struct reg_buffer_common *reg_buf, | |
257 | void *buf) | |
258 | { | |
259 | struct user_sve_header *header = (struct user_sve_header *) buf; | |
260 | char *base = (char *) buf; | |
48574d91 | 261 | uint64_t vq = sve_vq_from_vl (header->vl); |
e9902bfc AH |
262 | |
263 | /* Sanity check the data in the header. */ | |
264 | if (!sve_vl_valid (header->vl) | |
265 | || SVE_PT_SIZE (vq, header->flags) != header->size) | |
266 | error (_("Invalid SVE header from kernel.")); | |
267 | ||
e9902bfc AH |
268 | if (!HAS_SVE_STATE (*header)) |
269 | { | |
270 | /* There is no SVE state yet - the register dump contains a fpsimd | |
271 | structure instead. Where possible we want to write the reg_buf data | |
272 | back to the kernel using the fpsimd structure. However, if we cannot | |
273 | then we'll need to reformat the fpsimd into a full SVE structure, | |
274 | resulting in the initialization of SVE state written back to the | |
275 | kernel, which is why we try to avoid it. */ | |
276 | ||
277 | bool has_sve_state = false; | |
6afcd2d4 | 278 | gdb_byte *reg = (gdb_byte *) alloca (SVE_PT_SVE_ZREG_SIZE (vq)); |
e9902bfc AH |
279 | struct user_fpsimd_state *fpsimd |
280 | = (struct user_fpsimd_state *)(base + SVE_PT_FPSIMD_OFFSET); | |
281 | ||
6afcd2d4 | 282 | memset (reg, 0, SVE_PT_SVE_ZREG_SIZE (vq)); |
e9902bfc AH |
283 | |
284 | /* Check in the reg_buf if any of the Z registers are set after the | |
285 | first 128 bits, or if any of the other SVE registers are set. */ | |
286 | ||
287 | for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) | |
288 | { | |
289 | has_sve_state |= reg_buf->raw_compare (AARCH64_SVE_Z0_REGNUM + i, | |
6afcd2d4 | 290 | reg, sizeof (__int128_t)); |
e9902bfc AH |
291 | if (has_sve_state) |
292 | break; | |
293 | } | |
294 | ||
295 | if (!has_sve_state) | |
296 | for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++) | |
297 | { | |
298 | has_sve_state |= reg_buf->raw_compare (AARCH64_SVE_P0_REGNUM + i, | |
6afcd2d4 | 299 | reg, 0); |
e9902bfc AH |
300 | if (has_sve_state) |
301 | break; | |
302 | } | |
303 | ||
304 | if (!has_sve_state) | |
305 | has_sve_state |= reg_buf->raw_compare (AARCH64_SVE_FFR_REGNUM, | |
6afcd2d4 | 306 | reg, 0); |
e9902bfc AH |
307 | |
308 | /* If no SVE state exists, then use the existing fpsimd structure to | |
309 | write out state and return. */ | |
310 | if (!has_sve_state) | |
311 | { | |
6afcd2d4 LM |
312 | /* WARNING: SIMD state is laid out in memory in target-endian format, |
313 | while SVE state is laid out in an endianness-independent format | |
314 | (LE). | |
315 | ||
316 | So we have a couple cases to consider: | |
317 | ||
318 | 1 - If the target is big endian, then SIMD state is big endian, | |
319 | requiring a byteswap. | |
320 | ||
321 | 2 - If the target is little endian, then SIMD state is little | |
322 | endian, which matches the SVE format, so no byteswap is needed. */ | |
323 | ||
e9902bfc AH |
324 | /* The collects of the Z registers will overflow the size of a vreg. |
325 | There is enough space in the structure to allow for this, but we | |
326 | cannot overflow into the next register as we might not be | |
327 | collecting every register. */ | |
328 | ||
329 | for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) | |
330 | { | |
331 | if (REG_VALID | |
332 | == reg_buf->get_register_status (AARCH64_SVE_Z0_REGNUM + i)) | |
333 | { | |
6afcd2d4 LM |
334 | reg_buf->raw_collect (AARCH64_SVE_Z0_REGNUM + i, reg); |
335 | /* Handle big endian/little endian SIMD/SVE conversion. */ | |
336 | aarch64_maybe_swab128 ((gdb_byte *) &fpsimd->vregs[i], reg, | |
337 | V_REGISTER_SIZE); | |
e9902bfc AH |
338 | } |
339 | } | |
340 | ||
341 | if (REG_VALID == reg_buf->get_register_status (AARCH64_FPSR_REGNUM)) | |
342 | reg_buf->raw_collect (AARCH64_FPSR_REGNUM, &fpsimd->fpsr); | |
343 | if (REG_VALID == reg_buf->get_register_status (AARCH64_FPCR_REGNUM)) | |
344 | reg_buf->raw_collect (AARCH64_FPCR_REGNUM, &fpsimd->fpcr); | |
345 | ||
346 | return; | |
347 | } | |
348 | ||
349 | /* Otherwise, reformat the fpsimd structure into a full SVE set, by | |
350 | expanding the V registers (working backwards so we don't splat | |
351 | registers before they are copied) and using null for everything else. | |
352 | Note that enough space for a full SVE dump was originally allocated | |
353 | for base. */ | |
354 | ||
355 | header->flags |= SVE_PT_REGS_SVE; | |
356 | header->size = SVE_PT_SIZE (vq, SVE_PT_REGS_SVE); | |
357 | ||
358 | memcpy (base + SVE_PT_SVE_FPSR_OFFSET (vq), &fpsimd->fpsr, | |
359 | sizeof (uint32_t)); | |
360 | memcpy (base + SVE_PT_SVE_FPCR_OFFSET (vq), &fpsimd->fpcr, | |
361 | sizeof (uint32_t)); | |
362 | ||
363 | for (int i = AARCH64_SVE_Z_REGS_NUM; i >= 0 ; i--) | |
364 | { | |
365 | memcpy (base + SVE_PT_SVE_ZREG_OFFSET (vq, i), &fpsimd->vregs[i], | |
366 | sizeof (__int128_t)); | |
367 | } | |
368 | } | |
369 | ||
370 | /* Replace the kernel values with those from reg_buf. */ | |
371 | ||
372 | for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++) | |
373 | if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_Z0_REGNUM + i)) | |
374 | reg_buf->raw_collect (AARCH64_SVE_Z0_REGNUM + i, | |
375 | base + SVE_PT_SVE_ZREG_OFFSET (vq, i)); | |
376 | ||
377 | for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++) | |
378 | if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_P0_REGNUM + i)) | |
379 | reg_buf->raw_collect (AARCH64_SVE_P0_REGNUM + i, | |
380 | base + SVE_PT_SVE_PREG_OFFSET (vq, i)); | |
381 | ||
382 | if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_FFR_REGNUM)) | |
383 | reg_buf->raw_collect (AARCH64_SVE_FFR_REGNUM, | |
384 | base + SVE_PT_SVE_FFR_OFFSET (vq)); | |
385 | if (REG_VALID == reg_buf->get_register_status (AARCH64_FPSR_REGNUM)) | |
386 | reg_buf->raw_collect (AARCH64_FPSR_REGNUM, | |
387 | base + SVE_PT_SVE_FPSR_OFFSET (vq)); | |
388 | if (REG_VALID == reg_buf->get_register_status (AARCH64_FPCR_REGNUM)) | |
389 | reg_buf->raw_collect (AARCH64_FPCR_REGNUM, | |
390 | base + SVE_PT_SVE_FPCR_OFFSET (vq)); | |
391 | ||
392 | } |