progs/perftest.c: Use from Glibc syscall numbers.
[catacomb] / base / regdump.h
CommitLineData
4bc8424a
MW
1/* -*-c-*-
2 *
3 * Register dump and debugging support
4 *
5 * (c) 2019 Straylight/Edgeware
6 */
7
8/*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of Catacomb.
11 *
12 * Catacomb is free software: you can redistribute it and/or modify it
13 * under the terms of the GNU Library General Public License as published
14 * by the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * Catacomb is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with Catacomb. If not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
25 * USA.
26 */
27
28#ifndef CATACOMB_REGDUMP_H
29#define CATACOMB_REGDUMP_H
30
31#ifdef __cplusplus
32 extern "C" {
33#endif
34
35/*----- Header files ------------------------------------------------------*/
36
37#include "config.h"
38
39#ifndef ENABLE_ASM_DEBUG
40# error "Assembler-level debug disabled by `configure' script."
41#endif
42
43#if __ASSEMBLER__
44# include "asm-common.h"
45#else
46# include <float.h>
47# include <mLib/bits.h>
48#endif
49
50/*----- Random utilities --------------------------------------------------*/
51
52#define DO8(_) \
53 _(0) _(1) _(2) _(3) _(4) _(5) _(6) _(7)
54#define DOHI8(_) \
55 _(8) _(9) _(10) _(11) _(12) _(13) _(14) _(15)
56
57#define DO16(_) DO8(_) DOHI8(_)
58
59#define DO32(_) \
60 DO16(_) \
61 _(16) _(17) _(18) _(19) _(20) _(21) _(22) _(23) \
62 _(24) _(25) _(26) _(27) _(28) _(29) _(30) _(31)
63
64/*----- Common data structures --------------------------------------------*/
65
66#if !__ASSEMBLER__
67
68/* The following are good on our assembler targets. */
69typedef signed char int8;
70typedef short int16;
71typedef int int32;
72#if LONG_MAX >> 31 > 0x7fffffff
73 typedef long int64;
74#else
75 typedef long long int64;
76#endif
77typedef float float32;
78typedef double float64;
79typedef long double float80;
80
81#if CPUFAM_X86 || CPUFAM_ARMEL
82# define PTR32 void *p;
83# define PTR64
84#endif
85#if CPUFAM_AMD64 || CPUFAM_ARM64
86# define PTR32
87# define PTR64 void *p;
88#endif
89
90#define SIMD_COMMON(wd) \
91 uint8 u8[wd/8]; \
92 int8 i8[wd/8]; \
93 uint16 u16[wd/16]; \
94 int16 i16[wd/16]; \
95 uint32 u32[wd/32]; \
96 int32 i32[wd/32]; \
97 uint64 u64[wd/64]; \
98 int64 i64[wd/64]; \
99 float32 f32[wd/32]; \
100 float64 f64[wd/64]
101
102union gp32 { uint32 u32; int32 i32; PTR32 };
103union gp64 { uint64 u64; int64 i64; PTR64 };
104
105#endif
106
107/*----- Format word layout ------------------------------------------------*/
108
109#define REGF_IXMASK 0x000000ff
110#define REGF_IXSHIFT 0
111/* The index into the vector indicated by `REGF_SRCMASK', if applicable. */
112
113#define REGF_FMTMASK 0x0000ff00
114#define REGF_FMTSHIFT 8
115#define REGF_HEX 0x00000100
116#define REGF_CHR 0x00000200
117#define REGF_FLT 0x00000400
118#define REGF_UNSGN 0x00000800
119#define REGF_SGN 0x00001000
120/* How to format the value(s) found. */
121
122#define REGF_TYMASK 0x00ff0000
123#define REGF_TYSHIFT 16
124#define REGF_80 0x00010000
125#define REGF_64 0x00020000
126#define REGF_32 0x00040000
127#define REGF_16 0x00080000
128#define REGF_8 0x00100000
129/* Size of the value(s) to dump. */
130
131#define REGF_SRCMASK 0x0f000000
132#define REGSRC_ABS 0x01000000 /* absolute address */
133#define REGSRC_GP 0x02000000 /* general-purpose register */
134#define REGSRC_FP 0x03000000 /* floating-point register */
135#define REGSRC_SIMD 0x04000000 /* SIMD vector register */
136#define REGSRC_STMMX 0x05000000 /* x86-specific: x87/MMX register */
137#define REGSRC_SEG 0x06000000 /* x86-specific: segment register */
cd303963 138#define REGSRC_NONE 0x0f000000 /* just a message */
4bc8424a
MW
139/* Where to find the values. */
140
141#define REGF_WDMASK 0xf0000000
142#define REGF_WDSHIFT 28
143/* If we're to print a scalar, this is zero; otherwise, log_2 of the vector
144 * register width, in bits.
145 */
146
147/*----- x86 and AMD64 -----------------------------------------------------*/
148
149#if CPUFAM_X86 || CPUFAM_AMD64
150
151#define REGIX_FLAGS 0
152#define REGIX_IP 1
153#define REGIX_ADDR 2
154#define REGIX_AX 3
155#define REGIX_BX 4
156#define REGIX_CX 5
157#define REGIX_DX 6
158#define REGIX_SI 7
159#define REGIX_DI 8
160#define REGIX_BP 9
161#define REGIX_SP 10
162#if CPUFAM_X86
163# define REGIX_GPLIM 11
164#endif
165#if CPUFAM_AMD64
166# define REGIX_R8 11
167# define REGIX_R9 12
168# define REGIX_R10 13
169# define REGIX_R11 14
170# define REGIX_R12 15
171# define REGIX_R13 16
172# define REGIX_R14 17
173# define REGIX_R15 18
174# define REGIX_GPLIM 19
175#endif
176
177#define REGIX_CS 0
178#define REGIX_DS 1
179#define REGIX_SS 2
180#define REGIX_ES 3
181#define REGIX_FS 4
182#define REGIX_GS 5
183#define REGIX_SEGLIM 6
184
185#define REGIX_FPFLAGS 255
186
187#if !__ASSEMBLER__
188
189#if CPUFAM_X86
c85d1829 190 typedef union gp32 gpreg;
4bc8424a
MW
191#endif
192#if CPUFAM_AMD64
c85d1829 193 typedef union gp64 gpreg;
4bc8424a
MW
194#endif
195
196struct gpsave {
197 gpreg gp[REGIX_GPLIM];
198 uint16 seg[REGIX_SEGLIM];
199};
200
201union stmmx {
202 SIMD_COMMON(64);
203#if FLT_RADIX == 2 && LDBL_MANT_DIG == 64
204 long double f80;
205#endif
c85d1829 206 unsigned char _pad[16];
4bc8424a
MW
207};
208
209union xmm { SIMD_COMMON(128); };
210union ymm { SIMD_COMMON(256); };
211union vreg { union xmm v128[2]; union ymm v256; };
212
213struct fxsave {
214 unsigned short fcw;
215 unsigned short fsw;
216 unsigned char ftw;
217 unsigned char _res0;
218 unsigned short fop;
219#if CPUFAM_X86
220 unsigned int fpu_ip;
221 unsigned short fpu_cs;
222 unsigned short _res1;
223 unsigned int fpu_dp;
224 unsigned short fpu_ds;
225 unsigned short _res2;
226#endif
227#if CPUFAM_AMD64
228 unsigned long long fpu_ip;
229 unsigned long long fpu_dp;
230#endif
231 unsigned int mxcsr;
232 unsigned int mxcsr_mask;
233
234 union stmmx stmmx[8];
235
236#if CPUFAM_X86
237 union xmm xmm[8];
238 unsigned char _pad0[8*16];
239#endif
240#if CPUFAM_AMD64
241 union xmm xmm[16];
242#endif
243
244 unsigned char _pad1[96];
245};
246
247struct xsave_avx {
248#if CPUFAM_X86
249 union xmm ymmh[8];
250 unsigned char _pad0[8*16];
251#endif
252#if CPUFAM_AMD64
253 union xmm ymmh[16];
254#endif
255};
256
257struct regmap {
258 struct gpsave *gp;
259 struct fxsave *fx;
260 struct xsave_avx *avx;
261};
262
263#else
264
265 .extern regdump_gpsave
c85d1829
MW
266 // Save general-purpose registers at r/esp; flags, r/eax, and, on
267 // x86, ebx, should have been filled in already, together with the
268 // focus address, in `REGIX_ADDR', if relevant. Return required
269 // extended save area size in ecx, and leave r/ebp pointing to the
270 // save area.
271
4bc8424a 272 .extern regdump_xtsave
c85d1829
MW
273 // Save extended registers at r/esp, leaving r/ebp pointing to the
274 // register map.
275
4bc8424a 276 .extern regdump_xtrstr
c85d1829
MW
277 // Restore extended registers from register map in r/ebp, leaving
278 // r/ebp pointing to general-purpose save area.
279
4bc8424a 280 .extern regdump_gprstr
c85d1829
MW
281 // Restore general-purpose registers, except r/esp, from save area
282 // at r/ebp.
4bc8424a
MW
283
284 regmap_gp = 0*WORDSZ
285 regmap_fx = 1*WORDSZ
286 regmap_avx = 2*WORDSZ
287 regmap_size = 3*WORDSZ
288
e275090f
MW
289#define REGDEF_GPX86_COMMON(rn, ix) \
290 regsrc.e##rn = REGSRC_GP | ix; \
4bc8424a
MW
291 regty.e##rn = REGF_32; \
292 regfmt.e##rn = REGF_HEX; \
e275090f 293 regsrc.r##rn = REGSRC_GP | ix; \
4bc8424a
MW
294 regty.r##rn = REGF_64; \
295 regfmt.r##rn = REGF_HEX
296
297#define REGDEF_GPX86_ABCD(rn, RN) \
298 regsrc.rn##hl = (4 << REGF_WDSHIFT) | REGSRC_GP | REGIX_##RN##X; \
299 regty.rn##hl = REGF_8; \
300 regfmt.rn##hl = REGF_HEX; \
301 regsrc.rn##l = REGSRC_GP | REGIX_##RN##X; \
302 regty.rn##l = REGF_8; \
303 regfmt.rn##l = REGF_HEX; \
304 regsrc.rn##x = REGSRC_GP | REGIX_##RN##X; \
305 regty.rn##x = REGF_16; \
306 regfmt.rn##x = REGF_HEX; \
e275090f 307 REGDEF_GPX86_COMMON(rn##x, REGIX_##RN##X)
4bc8424a
MW
308REGDEF_GPX86_ABCD(a, A)
309REGDEF_GPX86_ABCD(b, B)
310REGDEF_GPX86_ABCD(c, C)
311REGDEF_GPX86_ABCD(d, D)
312
313 regsrc.eflags = REGSRC_GP | REGIX_FLAGS
314 regty.eflags = REGF_32
82d064a1 315 regfmt.eflags = 0
4bc8424a
MW
316
317#if CPUFAM_AMD64
318 regsrc.rflags = REGSRC_GP | REGIX_FLAGS
319 regty.rflags = REGF_64
82d064a1 320 regfmt.rflags = 0
4bc8424a
MW
321#endif
322
323#define REGDEF_GPX86_XP(rn, RN) \
324 regsrc.rn##l = REGSRC_GP | REGIX_##RN; \
325 regty.rn##l = REGF_8; \
326 regfmt.rn##l = REGF_HEX; \
327 regsrc.rn = REGSRC_GP | REGIX_##RN; \
328 regty.rn = REGF_16; \
329 regfmt.rn = REGF_HEX; \
e275090f 330 REGDEF_GPX86_COMMON(rn, REGIX_##RN)
4bc8424a
MW
331REGDEF_GPX86_XP(ip, IP)
332REGDEF_GPX86_XP(si, SI)
333REGDEF_GPX86_XP(di, DI)
334REGDEF_GPX86_XP(bp, BP)
335REGDEF_GPX86_XP(sp, SP)
336
337#if CPUFAM_AMD64
338# define REGDEF_GPAMD64(i) \
339 regsrc.r##i##b = REGSRC_GP | REGIX_R##i; \
340 regty.r##i##b = REGF_8; \
341 regfmt.r##i##b = REGF_HEX; \
342 regsrc.r##i##w = REGSRC_GP | REGIX_R##i; \
343 regty.r##i##w = REGF_16; \
344 regfmt.r##i##w = REGF_HEX; \
345 regsrc.r##i##d = REGSRC_GP | REGIX_R##i; \
346 regty.r##i##d = REGF_32; \
347 regfmt.r##i##d = REGF_HEX; \
348 regsrc.r##i = REGSRC_GP | REGIX_R##i; \
349 regty.r##i = REGF_64; \
350 regfmt.r##i = REGF_HEX;
351 DOHI8(REGDEF_GPAMD64)
352#endif
353
354#define REGDEF_SEG(rn, RN) \
355 regsrc.rn = REGSRC_SEG | REGIX_##RN; \
356 regty.rn = REGF_16; \
357 regfmt.rn = REGF_HEX
358REGDEF_SEG(ss, SS)
359REGDEF_SEG(cs, CS)
360REGDEF_SEG(ds, DS)
361REGDEF_SEG(es, ES)
362REGDEF_SEG(fs, FS)
363REGDEF_SEG(gs, GS)
364
365#define REGDEF_STMMX(i) \
366 regsrc.st##i = REGSRC_STMMX | i; \
367 regty.st##i = REGF_80; \
368 regfmt.st##i = REGF_FLT; \
369 regsrc.mm##i = (6 << REGF_WDSHIFT) | REGSRC_STMMX | i; \
370 regty.mm##i = REGF_16; \
371 regfmt.mm##i = REGF_HEX;
372DO8(REGDEF_STMMX)
373
374#define REGDEF_SIMD(i) \
375 regsrc.xmm##i = (7 << REGF_WDSHIFT) | REGSRC_SIMD | i; \
376 regty.xmm##i = REGF_32; \
377 regfmt.xmm##i = REGF_HEX; \
378 regsrc.ymm##i = (8 << REGF_WDSHIFT) | REGSRC_SIMD | i; \
379 regty.ymm##i = REGF_32; \
380 regfmt.ymm##i = REGF_HEX;
381DO8(REGDEF_SIMD)
382#if CPUFAM_AMD64
383 DOHI8(REGDEF_SIMD)
384#endif
385
386 REGDUMP_GPSIZE = REGIX_GPLIM*WORDSZ + REGIX_SEGLIM*2
387
388# if CPUFAM_AMD64 && ABI_SYSV
389 REGDUMP_SPADJ = REGDUMP_GPSIZE + WORDSZ + 128
390# else
391 REGDUMP_SPADJ = REGDUMP_GPSIZE + WORDSZ
392# endif
393
394.macro _saveregs addr=nil
395 // Save the registers, leaving r/ebp pointing to the register map.
396
397 // Stash r/eax. This is bletcherous: hope we don't get a signal in
398 // the next few instructions.
a90d420c 399 mov [SP - REGDUMP_SPADJ + (REGIX_AX - 1)*WORDSZ], AX
4bc8424a
MW
400
401 .ifnes "\addr", "nil"
c85d1829
MW
402 // Collect the focus address for the following dump, leaving it in
403 // the `addr' slot of the dump.
a90d420c
MW
404 lea AX, \addr
405 mov [SP - REGDUMP_SPADJ + (REGIX_ADDR - 1)*WORDSZ], AX
4bc8424a
MW
406 .endif
407
408 // Make space for the register save area. On AMD64 with System/V
409 // ABI, also skip the red zone. Use `lea' here to preserve the
410 // flags.
a90d420c 411 lea SP, [SP - REGDUMP_SPADJ]
4bc8424a
MW
412
413 // Save flags and general-purpose registers. On 32-bit x86, we save
414 // ebx here and establish a GOT pointer here for the benefit of the
415 // PLT-indirect calls made later on.
416 pushf
417# if CPUFAM_X86
a90d420c 418 mov [SP + 4*REGIX_BX], ebx
4bc8424a
MW
419 ldgot
420# endif
a752c20f 421 callext F(regdump_gpsave)
4bc8424a
MW
422
423 // Make space for the extended registers.
a90d420c 424 sub SP, CX
a752c20f 425 callext F(regdump_xtsave)
4bc8424a
MW
426
427 // Prepare for calling back into C. On 32-bit x86, leave space for
428 // the arguments and set up the GOT pointer; on AMD64 Windows, leave
429 // the `shadow space' for the called-function's arguments. Also,
430 // forcibly align the stack pointer to a 16-byte boundary.
431# if CPUFAM_X86
a90d420c 432 sub SP, 16
4bc8424a 433# elif ABI_WIN
a90d420c 434 sub SP, 32
4bc8424a 435# endif
a90d420c 436 and SP, ~15
4bc8424a
MW
437.endm
438
439.macro _rstrregs
440 // Restore registers.
441
442 // We assume r/ebp still points to the register map.
a752c20f 443 callext F(regdump_xtrstr)
a90d420c 444 mov SP, BP
a752c20f 445 callext F(regdump_gprstr)
4bc8424a 446 popf
a90d420c 447 lea SP, [SP + REGDUMP_SPADJ]
4bc8424a
MW
448.endm
449
cd303963
MW
450.macro _nilbase
451# if CPUFAM_X86
452 xor eax, eax
453 mov [SP + 0], eax
454# elif ABI_SYSV
455 xor edi, edi
456# elif ABI_WIN
457 xor ecx, ecx
458# endif
459.endm
460
4bc8424a
MW
461.macro _regbase
462# if CPUFAM_X86
a90d420c 463 mov [SP + 0], BP
4bc8424a 464# elif ABI_SYSV
a90d420c 465 mov rdi, BP
4bc8424a 466# elif ABI_WIN
a90d420c 467 mov rcx, BP
4bc8424a
MW
468# endif
469.endm
470
471.macro _membase
a90d420c 472 mov AX, [BP + regmap_gp]
4bc8424a
MW
473# if CPUFAM_X86
474 mov eax, [eax + REGIX_ADDR*WORDSZ]
a90d420c 475 mov [SP + 0], eax
4bc8424a
MW
476# elif ABI_SYSV
477 mov rdi, [rax + REGIX_ADDR*WORDSZ]
478# elif ABI_WIN
479 mov rcx, [rax + REGIX_ADDR*WORDSZ]
480# endif
481.endm
482
483.macro _reglbl msg
484 .ifeqs "\msg", ""
485# if CPUFAM_X86
a90d420c 486 mov dword ptr [SP + 4], 0
4bc8424a
MW
487# elif ABI_SYSV
488 xor esi, esi
489# elif ABI_WIN
490 xor edx, edx
491# endif
492 .else
493# if CPUFAM_X86
494 lea eax, [INTADDR(.L$_reglbl$\@)]
a90d420c 495 mov [SP + 4], eax
4bc8424a
MW
496# elif ABI_SYSV
497 lea rsi, [INTADDR(.L$_reglbl$\@)]
498# elif ABI_WIN
499 lea rdx, [INTADDR(.L$_reglbl$\@)]
500# endif
501 _LIT
502.L$_reglbl$\@:
503 .asciz "\msg"
504 _ENDLIT
505 .endif
506.endm
507
508.macro _regfmt arg
509# if CPUFAM_X86
a90d420c 510 mov dword ptr [SP + 8], \arg
4bc8424a
MW
511# elif ABI_SYSV
512 mov edx, \arg
513# elif ABI_WIN
514 mov r8d, \arg
515# endif
516.endm
517
518#endif
519
520#endif
521
522/*----- ARM32 -------------------------------------------------------------*/
523
524#if CPUFAM_ARMEL
525
526#if !__ASSEMBLER__
527extern unsigned regdump__flags;
528#endif
529#define REGF_VFP 1u
530#define REGF_D32 2u
531
532#define REGIX_CPSR 16
533#define REGIX_ADDR 17
534#define REGIX_GPLIM 18
535
536#define REGIX_FPSCR 255
537
538#if !__ASSEMBLER__
539
540union neon64 { SIMD_COMMON(64); };
541union neon128 { SIMD_COMMON(128); };
542
543struct gpsave { union gp32 r[REGIX_GPLIM]; };
544
545struct fpsave {
546 unsigned fpscr;
547 unsigned _pad0;
548 union {
549 float32 s[32];
550 union neon64 d[32];
551 union neon128 q[16];
552 } u;
553};
554
555struct regmap {
556 struct gpsave *gp;
557 struct fpsave *fp;
558};
559
560#else
561
562 .extern regdump_gpsave
c85d1829
MW
563 // Save general-purpose registers at r13; r12 and r14 should have
564 // been filled in already, along with the focus address in
565 // `REGIX_ADDR', if relevant. Return required extended save area
566 // size in r0, leave r4 pointing to the save area, and set r6 to
567 // the focus address.
568
4bc8424a 569 .extern regdump_xtsave
c85d1829
MW
570 // Save extended registers at r13, leaving r5 pointing to the
571 // register map.
572
4bc8424a 573 .extern regdump_xtrstr
c85d1829
MW
574 // Restore extended registers from register map at r5.
575
4bc8424a 576 .extern regdump_gprstr
c85d1829
MW
577 // Restore general-purpose registers, except r13 and r14, from save
578 // area at r4.
4bc8424a
MW
579
580 regmap_gp = 0
581 regmap_fp = 4
582 regmap_size = 8
583
584#define REGDEF_GP(i) \
585 regsrc.r##i = REGSRC_GP | i; \
586 regty.r##i = REGF_32; \
587 regfmt.r##i = REGF_HEX;
588DO16(REGDEF_GP)
589
590 regsrc.cpsr = REGSRC_GP | REGIX_CPSR
591 regty.cpsr = REGF_32
592 regfmt.cpsr = 0
593
594#define REGDEF_NEONS(i) \
595 regsrc.s##i = REGSRC_FP | i; \
596 regty.s##i = REGF_32; \
597 regfmt.s##i = REGF_FLT;
598DO32(REGDEF_NEONS)
599
600#define REGDEF_NEOND(i) \
601 regsrc.d##i = (6 << REGF_WDSHIFT) | REGSRC_FP | i; \
602 regty.d##i = REGF_32; \
603 regfmt.d##i = REGF_HEX;
604DO32(REGDEF_NEOND)
605
606#define REGDEF_NEONQ(i) \
607 regsrc.q##i = (7 << REGF_WDSHIFT) | REGSRC_FP | i; \
608 regty.q##i = REGF_32; \
609 regfmt.q##i = REGF_HEX;
610DO16(REGDEF_NEONQ)
611
612 regsrc.fpscr = REGSRC_FP | REGIX_FPSCR
613 regty.fpscr = REGF_32
614 regfmt.fpscr = 0
615
616 REGDUMP_GPSIZE = 4*REGIX_GPLIM
617 REGDUMP_FPSIZE_D16 = 8 + 16*8
618 REGDUMP_FPSIZE_D32 = 8 + 32*8
619
620.macro _saveregs base=nil, off=#0
621 // Save the registers, leaving r4 pointing to the register map.
622
623 // Stash r14. This is bletcherous: hope we don't get a signal in
624 // the next few instructions.
625 str r14, [r13, #-REGDUMP_GPSIZE + 14*4]
626
627 .ifnes "\base,\off", "nil,#0"
c85d1829
MW
628 // Collect the focus address for the following dump, leaving it in
629 // the `addr' slot of the dump.
4bc8424a
MW
630 .ifeqs "\base", "nil"
631 adrl r14, \off
632 .else
633 add r14, \base, \off
634 .endif
635 str r14, [r13, #-REGDUMP_GPSIZE + 4*REGIX_ADDR]
636 .endif
637
638 // Make space for the register save area.
639 sub r13, r13, #REGDUMP_GPSIZE
640
641 // Save flags and general-purpose registers.
6a3d653c
MW
642 mrs r14, cpsr
643 str r14, [r13, #4*REGIX_CPSR]
4bc8424a
MW
644 str r12, [r13, #4*12]
645 bl regdump_gpsave
646
647 // Make space for the extended registers.
648 sub r13, r13, r0
649 bl regdump_xtsave
650
651 // Prepare for calling back into C.
652 ldgot
653 mov r0, r13
654 bic r0, r0, #15
655 mov r13, r0
656.endm
657
658.macro _rstrregs
659 // Restore registers.
660
661 // We assume r4 still points to the register map.
662 bl regdump_xtrstr
663 mov r13, r4
664 bl regdump_gprstr
665 ldr r14, [r13, #14*4]
666 add r13, r13, #REGDUMP_GPSIZE
667.endm
668
cd303963
MW
669.macro _nilbase
670 mov r0, #0
671.endm
672
4bc8424a
MW
673.macro _regbase
674 mov r0, r5
675.endm
676
677.macro _membase
678 mov r0, r6
679.endm
680
681.macro _reglbl msg
682 adrl r1, .L$_reglbl$\@
683 _LIT
684.L$_reglbl$\@:
685 .asciz "\msg"
686 .balign 4
687 _ENDLIT
688.endm
689
690.macro _regfmt arg
19d642aa
MW
691 movw r2, #(\arg)&0xffff
692 movt r2, #((\arg) >> 16)&0xffff
4bc8424a
MW
693.endm
694
695#endif
696
697#endif
698
699/*----- ARM64 -------------------------------------------------------------*/
700
701#if CPUFAM_ARM64
702
703#define REGIX_NZCV 32
704#define REGIX_PC 33
705#define REGIX_ADDR 34
706#define REGIX_GPLIM 36
707
708#define REGIX_FPFLAGS 255
709
710#if !__ASSEMBLER__
711
712union v128 { SIMD_COMMON(128); };
713
714struct gpsave { union gp64 r[REGIX_GPLIM]; };
715
716struct fpsave {
717 unsigned fpsr, fpcr;
718 union v128 v[32];
719};
720
721struct regmap {
722 struct gpsave *gp;
723 struct fpsave *fp;
724};
725
726#else
727
728 .extern regdump_gpsave
c85d1829
MW
729 // Save general-purpose registers at sp; x16, x17, and x30, should
730 // have been filled in already, along with the focus address in
731 // `REGIX_ADDR', if relevant. Return required extended save area
732 // size in x0, leave x20 pointing to the save area, and set x22 to
733 // the focus address.
734
4bc8424a 735 .extern regdump_xtsave
c85d1829
MW
736 // Save extended registers at sp, leaving x21 pointing to the
737 // register map.
738
4bc8424a 739 .extern regdump_xtrstr
c85d1829
MW
740 // Restore extended registers from register map at x21.
741
4bc8424a 742 .extern regdump_gprstr
c85d1829
MW
743 // Restore general-purpose registers, except sp and x30, from save
744 // area at x20.
4bc8424a
MW
745
746 regmap_gp = 0
747 regmap_fp = 8
748 regmap_size = 16
749
750#define REGDEF_GP(i) \
751 regsrc.x##i = REGSRC_GP | i; \
752 regty.x##i = REGF_64; \
753 regfmt.x##i = REGF_HEX; \
754 regsrc.w##i = REGSRC_GP | i; \
755 regty.w##i = REGF_32; \
756 regfmt.w##i = REGF_HEX;
757DO32(REGDEF_GP)
758
759 regsrc.sp = REGSRC_GP | 31
760 regty.sp = REGF_64
761 regfmt.sp = REGF_HEX
762
763 regsrc.pc = REGSRC_GP | REGIX_PC
764 regty.pc = REGF_64
765 regfmt.pc = REGF_HEX
766
767 regsrc.nzcv = REGSRC_GP | REGIX_NZCV
768 regty.nzcv = REGF_32
769 regfmt.nzcv = 0
770
771#define REGDEF_FP(i) \
772 regsrc.b##i = REGSRC_FP | i; \
773 regty.b##i = REGF_8; \
774 regfmt.b##i = REGF_HEX; \
775 regsrc.h##i = REGSRC_FP | i; \
776 regty.h##i = REGF_16; \
777 regfmt.h##i = REGF_HEX; \
778 regsrc.s##i = REGSRC_FP | i; \
779 regty.s##i = REGF_32; \
780 regfmt.s##i = REGF_FLT; \
781 regsrc.d##i = REGSRC_FP | i; \
782 regty.d##i = REGF_64; \
783 regfmt.d##i = REGF_FLT; \
784 regsrc.v##i = (7 << REGF_WDSHIFT) | REGSRC_FP | i; \
785 regty.v##i = REGF_32; \
786 regfmt.v##i = REGF_HEX;
787DO32(REGDEF_FP)
788
789 regsrc.fpflags = REGSRC_FP | REGIX_FPFLAGS
790 regty.fpflags = REGF_32
791 regfmt.fpflags = 0
792
793 REGDUMP_GPSIZE = 8*REGIX_GPLIM
794 REGDUMP_FPSIZE = 16 + 16 + 32*16
795
796.macro _saveregs base=nil, off=#0
797 // Save the registers, leaving x20 pointing to the register map.
798
799 // Stash x30. This is bletcherous: hope we don't get a signal in
800 // the next few instructions.
801 str x30, [sp, #-REGDUMP_GPSIZE + 30*8]
802
803 .ifnes "\base,\off", "nil,#0"
c85d1829
MW
804 // Collect the focus address for the following dump, leaving it in
805 // the `addr' slot of the dump.
4bc8424a
MW
806 .ifeqs "\base", "nil"
807 adr x30, \off
808 .else
809 add x30, \base, \off
810 .endif
811 str x30, [sp, #-REGDUMP_GPSIZE + 8*REGIX_ADDR]
812 .endif
813
814 // Make space for the register save area.
815 sub sp, sp, #REGDUMP_GPSIZE
816
818bad9b
MW
817 // Save flags and general-purpose registers. The PLT linkage code
818 // makes free with x8--x17, so we must save those here.
819 mrs x30, nzcv
820 str x30, [sp, #8*REGIX_NZCV]
821 stp x8, x9, [sp, #64]
822 stp x10, x11, [sp, #80]
823 stp x12, x13, [sp, #96]
824 stp x14, x15, [sp, #112]
825 stp x16, x17, [sp, #128]
4bc8424a
MW
826 bl regdump_gpsave
827
828 // Make space for the extended registers.
829 sub sp, sp, x0
830 bl regdump_xtsave
831.endm
832
833.macro _rstrregs
834 // Restore registers.
835
836 // We assume x21 still points to the register map.
837 bl regdump_xtrstr
838 mov sp, x20
839 bl regdump_gprstr
840 ldr x30, [sp, #30*8]
841 add sp, sp, #REGDUMP_GPSIZE
842.endm
843
cd303963
MW
844.macro _nilbase
845 mov x0, #0
846.endm
847
4bc8424a
MW
848.macro _regbase
849 mov x0, x21
850.endm
851
852.macro _membase
853 mov x0, x22
854.endm
855
856.macro _reglbl msg
857 adr x1, .L$_reglbl$\@
858 _LIT
859.L$_reglbl$\@:
860 .asciz "\msg"
861 .balign 4
862 _ENDLIT
863.endm
864
865.macro _regfmt arg
19d642aa
MW
866 movz w2, #(\arg)&0xffff
867 movk w2, #((\arg) >> 16)&0xffff, lsl #16
4bc8424a
MW
868.endm
869
870#endif
871
872#endif
873
874/*----- Functions provided ------------------------------------------------*/
875
876/* --- @regdump_init@ --- *
877 *
878 * Arguments: ---
879 *
880 * Returns: ---
881 *
882 * Use: Performs one-time initialization for register dumping. In
883 * particular, this performs CPU feature detection on platforms
884 * where that is a difficult task: without it, registers
885 * corresponding to optional architectural features can be
886 * neither printed nor preserved by the register-dump machinery.
887 */
888
889#if !__ASSEMBLER__
890extern void regdump_init(void);
891#endif
892
893/* --- @regdump@ --- *
894 *
895 * Arguments: @const void *base@ = pointer to base structure, corresponding
896 * to the @REGF_SRCMASK@ part of @f@
897 * @const char *lbl@ = label to print
898 * @uint32 f@ = format control word; see @REGF_...@
899 *
900 * Returns: ---
901 *
902 * Use: Dump a register value, or chunk of memory.
903 *
904 * This function is not usually called directly; instead, use
905 * the `reg' or `mem' assembler macros.
906 */
907
908#if !__ASSEMBLER__
909extern void regdump(const void *base, const char *lbl, uint32 f);
910#else
911 .extern regdump
912#endif
913
914/* --- @regdump_gp@, @regdump_fp@, @regdump_simd@ --- *
915 *
916 * Arguments: @const struct regmap *map@ = pointer to register map
917 *
918 * Returns: ---
919 *
920 * Use: Dump the general-purpose/floating-point/SIMD registers.
921 *
922 * This function is not usually called directly; instead, use
923 * the `regdump' assembler macro.
924 */
925
926#if !__ASSEMBLER__
927extern void regdump_gp(const struct regmap */*map*/);
928extern void regdump_fp(const struct regmap */*map*/);
929extern void regdump_simd(const struct regmap */*map*/);
930#else
931 .extern regdump_gp
932 .extern regdump_fp
933 .extern regdump_simd
934#endif
935
936/* --- @regdump_freshline@ --- *
937 *
938 * Arguments: ---
939 *
940 * Returns: ---
941 *
942 * Use: Begin a fresh line of output.
943 */
944
945#if !__ASSEMBLER__
946extern void regdump_freshline(void);
947#else
948 .extern regdump_freshline
949#endif
950
951/*----- Main user interface macros ----------------------------------------*/
952
953#if __ASSEMBLER__
954
955.macro terpri
956 _saveregs
a752c20f 957 callext F(regdump_freshline)
4bc8424a
MW
958 _rstrregs
959.endm
960
cd303963
MW
961.macro msg lbl
962 _saveregs
963 _nilbase
964 _reglbl "\lbl"
965 _regfmt REGSRC_NONE | (1 << REGF_WDSHIFT)
966 callext F(regdump)
967 _rstrregs
968.endm
969
4bc8424a
MW
970.macro reg lbl, rn, fmt=0
971 _saveregs
972 _regbase
973 _reglbl "\lbl"
974 .L$reg.fmt$\@ = regsrc.\rn | \fmt | \
975 (((\fmt&REGF_TYMASK) == 0)&regty.\rn) | \
976 (((\fmt&REGF_FMTMASK) == 0)&regfmt.\rn)
977 _regfmt .L$reg.fmt$\@
a752c20f 978 callext F(regdump)
4bc8424a
MW
979 _rstrregs
980.endm
981
982.macro mem lbl, addr, fmt=0
983 _saveregs \addr
984 _membase
985 _reglbl "\lbl"
986 .L$mem.fmt$\@ = REGSRC_ABS | \fmt | \
987 (((\fmt&REGF_TYMASK) == 0)&REGF_32) | \
988 (((\fmt&REGF_FMTMASK) == 0)&REGF_HEX)
989 _regfmt .L$mem.fmt$\@
a752c20f 990 callext F(regdump)
4bc8424a
MW
991 _rstrregs
992.endm
993
994.macro regdump gp=nil, fp=nil, simd=nil
995 _saveregs
996 .ifnes "\gp", "nil"
997 _regbase
a752c20f 998 callext F(regdump_gp)
4bc8424a
MW
999 .endif
1000 .ifnes "\fp", "nil"
1001 _regbase
a752c20f 1002 callext F(regdump_fp)
4bc8424a
MW
1003 .endif
1004 .ifnes "\simd", "nil"
1005 _regbase
a752c20f 1006 callext F(regdump_simd)
4bc8424a
MW
1007 .endif
1008 _rstrregs
1009.endm
1010
1011#endif
1012
1013/*----- That's all, folks -------------------------------------------------*/
1014
1015#ifdef __cplusplus
1016 }
1017#endif
1018
1019#endif