base/regdump.[ch], etc.: Fancy register dumping infrastructure.
[catacomb] / base / regdump-arm.S
diff --git a/base/regdump-arm.S b/base/regdump-arm.S
new file mode 100644 (file)
index 0000000..963a60e
--- /dev/null
@@ -0,0 +1,184 @@
+/// -*- mode: asm; asm-comment-char: ?/ -*-
+///
+/// Register dump and debugging for 32-bit ARM
+///
+/// (c) 2019 Straylight/Edgeware
+///
+
+///----- Licensing notice ---------------------------------------------------
+///
+/// This file is part of Catacomb.
+///
+/// Catacomb is free software: you can redistribute it and/or modify it
+/// under the terms of the GNU Library General Public License as published
+/// by the Free Software Foundation; either version 2 of the License, or
+/// (at your option) any later version.
+///
+/// Catacomb 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
+/// Library General Public License for more details.
+///
+/// You should have received a copy of the GNU Library General Public
+/// License along with Catacomb.  If not, write to the Free Software
+/// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+/// USA.
+
+///--------------------------------------------------------------------------
+/// Preliminaries.
+
+#include "config.h"
+#include "asm-common.h"
+#include "regdump.h"
+
+       .arch   armv7-a
+       .fpu    neon
+
+       .text
+
+///--------------------------------------------------------------------------
+/// Main code.
+
+FUNC(regdump_gpsave)
+  endprologue
+       // On entry, r13 should point to `REGDUMP_GPSIZE' bytes of
+       // word-aligned storage to be the general-purpose save area, with r12
+       // and r14 already saved.  On exit, the initial registers are saved
+       // in this space, and modified: r4 points to the general-purpose save
+       // area, r6 holds the focus address (possibly already saved), r0
+       // contains the number of bytes required in the extended save area,
+       // and other general-purpose registers are clobbered or used to
+       // communicate with `regdump_xtsave' below.  Doing anything other
+       // than lowering the stack pointer and calling `regdump_xtsave' is
+       // not recommended.
+
+       // Save the easy registers.
+       stmia   r13, {r0-r11}
+       mov     r4, r13
+
+       // Determine the previous stack pointer and save it.
+       add     r0, r4, #REGDUMP_GPSIZE
+       str     r0, [r4, #13*4]
+
+       // Capture the status flags and return address.  If the return
+       // address has its low bit set, then the caller was in Thumb state:
+       // clear the bit from the reconstructed PC, and set the corresponding
+       // CPSR bit.
+       mrs     r0, cpsr
+       tst     r14, #1
+       bic     r1, r14, #1
+       orrne   r0, r0, #0x00000020
+       str     r0, [r13, #4*REGIX_CPSR]
+       str     r1, [r13, #15*4]
+
+       // Load the focus address and save it as r6.
+       ldr     r6, [r4, #4*REGIX_ADDR]
+
+       // Determine the extended save area size.
+       ldgot
+       mov     r0, #8 + 8
+       leaext  r12, regdump__flags
+       ldr     r12, [r12]
+       tst     r12, #REGF_VFP
+       addne   r0, r0, #REGDUMP_FPSIZE_D16
+       tstne   r12, #REGF_D32
+       addne   r0, r0, #REGDUMP_FPSIZE_D32 - REGDUMP_FPSIZE_D16
+
+       // Done.
+       bx      r14
+
+ENDFUNC
+
+FUNC(regdump_gprstr)
+  endprologue
+       // On entry, r4 points to a general-purpose save area, established by
+       // `regdump_gpsave'.  On exit, the general-purpose registers (other
+       // than r13 and r14) are restored to their original values.
+
+       // Restore the processor flags.
+       ldr     r0, [r4, #4*REGIX_CPSR]
+       msr     cpsr_fs, r0
+
+       // Load the easy registers.
+       ldmia   r4, {r0-r12}
+
+       // Done.
+       bx      r14
+
+ENDFUNC
+
+FUNC(regdump_xtsave)
+  endprologue
+       // On entry, r13 points to an extended save area, of size determined
+       // by `regdump_gpsave' above.  On exit, the save area is filled in
+       // and a handy map placed at its base.
+
+       // Set up the map/extended save area pointer.
+       add     r5, r13, #7
+       bic     r5, r5, #7
+
+       // Start by filling in the easy part of the map.
+       str     r4, [r5, #regmap_gp]
+
+       // Fetch the flags explaining what to do.
+       ldgot
+       leaext  r12, regdump__flags
+       ldr     r12, [r12]
+
+       // Figure out whether there are VFP/NEON registers.
+       tst     r12, #REGF_VFP
+       moveq   r3, #0
+       addne   r3, r5, #regmap_size
+       str     r3, [r5, #regmap_fp]
+       beq     9f
+
+       // Get the FP status register.
+       vmrs    r0, fpscr
+       str     r0, [r3], #8
+
+       // At least the first 16.
+       vstmia  r3!, {d0-d15}
+
+       // Maybe the other 16 too.
+       tst     r12, #REGF_D32
+       vstmiane r3!, {d16-d31}
+
+       // Done.
+9:     bx      r14
+
+ENDFUNC
+
+FUNC(regdump_xtrstr)
+  endprologue
+       // On entry, r5 points to a register-save map.  On exit, the extended
+       // registers are restored from the save area, r4 (pointing to the
+       // general-purpose save area) is preserved, and the other general
+       // registers are clobbered.
+
+       // Fetch the flags explaining what to do.
+       ldgot
+       leaext  r12, regdump__flags
+       ldr     r12, [r12]
+
+       // Figure out if there are VFP/NEON registers.
+       tst     r12, #REGF_VFP
+       beq     9f
+       ldr     r3, [r5, #regmap_fp]
+
+       // Load the FP status register.
+       ldr     r0, [r3], #8
+       vmsr    fpscr, r0
+
+       // Load the first 16 registers.
+       vldmia  r3!, {d0-d15}
+
+       // And maybe the other 16.
+       tst     r12, #REGF_D32
+       vldmiane r3!, {d16-d31}
+
+       // Done.
+9:     bx      r14
+
+ENDFUNC
+
+///----- That's all, folks --------------------------------------------------