Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/external/cddl/osnet Implement DTrace FBT provider for ARM



details:   https://anonhg.NetBSD.org/src/rev/99538b091aaf
branches:  trunk
changeset: 794449:99538b091aaf
user:      ozaki-r <ozaki-r%NetBSD.org@localhost>
date:      Sat Mar 15 08:00:19 2014 +0000

description:
Implement DTrace FBT provider for ARM

- Finding probable functions
- Replacing instructions
- Emulating instructions

It is tested only on ARMv7 CPUs yet, for example,
-m evbarm (-a earm) kernel=BEAGLEBONE.

diffstat:

 external/cddl/osnet/dev/fbt/fbt.c                |  578 ++++++++++++++++++++++-
 external/cddl/osnet/dist/uts/common/sys/dtrace.h |   14 +
 2 files changed, 590 insertions(+), 2 deletions(-)

diffs (truncated from 700 to 300 lines):

diff -r 3d1d5011226d -r 99538b091aaf external/cddl/osnet/dev/fbt/fbt.c
--- a/external/cddl/osnet/dev/fbt/fbt.c Sat Mar 15 07:49:15 2014 +0000
+++ b/external/cddl/osnet/dev/fbt/fbt.c Sat Mar 15 08:00:19 2014 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: fbt.c,v 1.13 2014/03/05 20:14:15 tron Exp $    */
+/*     $NetBSD: fbt.c,v 1.14 2014/03/15 08:00:19 ozaki-r Exp $ */
 
 /*
  * CDDL HEADER START
@@ -58,12 +58,19 @@
 #include <sys/unistd.h>
 
 #include <machine/cpu.h>
+#if defined(__i386__) || defined(__amd64__)
 #include <machine/cpufunc.h>
 #include <machine/specialreg.h>
 #if 0
 #include <x86/cpuvar.h>
 #endif
 #include <x86/cputypes.h>
+#elif __arm__
+#include <machine/trap.h>
+#include <arm/cpufunc.h>
+#include <arm/armreg.h>
+#include <arm/frame.h>
+#endif
 
 #define ELFSIZE ARCH_ELFSIZE
 #include <sys/exec_elf.h>
@@ -77,6 +84,7 @@
 
 MALLOC_DEFINE(M_FBT, "fbt", "Function Boundary Tracing");
 
+#if defined(__i386__) || defined(__amd64__)
 #define        FBT_PUSHL_EBP           0x55
 #define        FBT_MOVL_ESP_EBP0_V0    0x8b
 #define        FBT_MOVL_ESP_EBP1_V0    0xec
@@ -88,11 +96,43 @@
 #define        FBT_RET                 0xc3
 #define        FBT_RET_IMM16           0xc2
 #define        FBT_LEAVE               0xc9
+#endif
 
 #ifdef __amd64__
 #define        FBT_PATCHVAL            0xcc
+#elif defined(__i386__)
+#define        FBT_PATCHVAL            0xf0
+
+#elif defined(__arm__)
+#define        FBT_PATCHVAL            DTRACE_BREAKPOINT
+
+/* entry and return */
+#define        FBT_BX_LR_P(insn)       (((insn) & ~INSN_COND_MASK) == 0x012fff1e)
+#define        FBT_B_LABEL_P(insn)     (((insn) & 0xff000000) == 0xea000000)
+/* entry */
+#define        FBT_MOV_IP_SP_P(insn)   ((insn) == 0xe1a0c00d)
+/* index=1, add=1, wback=0 */
+#define        FBT_LDR_IMM_P(insn)     (((insn) & 0xfff00000) == 0xe5900000)
+#define        FBT_MOVW_P(insn)        (((insn) & 0xfff00000) == 0xe3000000)
+#define        FBT_MOV_IMM_P(insn)     (((insn) & 0xffff0000) == 0xe3a00000)
+#define        FBT_CMP_IMM_P(insn)     (((insn) & 0xfff00000) == 0xe3500000)
+#define        FBT_PUSH_P(insn)        (((insn) & 0xffff0000) == 0xe92d0000)
+/* return */
+/* cond=always, writeback=no, rn=sp and register_list includes pc */
+#define        FBT_LDM_P(insn) (((insn) & 0x0fff8000) == 0x089d8000)
+#define        FBT_LDMIB_P(insn)       (((insn) & 0x0fff8000) == 0x099d8000)
+#define        FBT_MOV_PC_LR_P(insn)   (((insn) & ~INSN_COND_MASK) == 0x01a0f00e)
+/* cond=always, writeback=no, rn=sp and register_list includes lr, but not pc */
+#define        FBT_LDM_LR_P(insn)      (((insn) & 0xffffc000) == 0xe89d4000)
+#define        FBT_LDMIB_LR_P(insn)    (((insn) & 0xffffc000) == 0xe99d4000)
+
+/* rval = insn | invop_id (overwriting cond with invop ID) */
+#define        BUILD_RVAL(insn, id)    (((insn) & ~INSN_COND_MASK) | __SHIFTIN((id), INSN_COND_MASK))
+/* encode cond in the first byte */
+#define        PATCHVAL_ENCODE_COND(insn)      (FBT_PATCHVAL | __SHIFTOUT((insn), INSN_COND_MASK))
+
 #else
-#define        FBT_PATCHVAL            0xf0
+#error "architecture not supported"
 #endif
 
 static dev_type_open(fbt_open);
@@ -140,10 +180,17 @@
 
 typedef struct fbt_probe {
        struct fbt_probe *fbtp_hashnext;
+#if defined(__i386__) || defined(__amd64__)
        uint8_t         *fbtp_patchpoint;
        int8_t          fbtp_rval;
        uint8_t         fbtp_patchval;
        uint8_t         fbtp_savedval;
+#elif __arm__
+       uint32_t        *fbtp_patchpoint;
+       int32_t         fbtp_rval;
+       uint32_t        fbtp_patchval;
+       uint32_t        fbtp_savedval;
+#endif
        uintptr_t       fbtp_roffset;
        dtrace_id_t     fbtp_id;
        const char      *fbtp_name;
@@ -164,6 +211,226 @@
 static int                     fbt_probetab_size;
 static int                     fbt_probetab_mask;
 
+#ifdef __arm__
+extern void (* dtrace_emulation_jump_addr)(int, struct trapframe *);
+
+static uint32_t
+expand_imm(uint32_t imm12)
+{
+       uint32_t unrot = imm12 & 0xff;
+       int amount = 2 * (imm12 >> 8);
+
+       if (amount)
+               return (unrot >> amount) | (unrot << (32 - amount));
+       else
+               return unrot;
+}
+
+static uint32_t
+add_with_carry(uint32_t x, uint32_t y, int carry_in,
+       int *carry_out, int *overflow)
+{
+       uint32_t result;
+       uint64_t unsigned_sum = x + y + (uint32_t)carry_in;
+       int64_t signed_sum = (int32_t)x + (int32_t)y + (int32_t)carry_in;
+       KASSERT(carry_in == 1);
+
+       result = (uint32_t)(unsigned_sum & 0xffffffff);
+       *carry_out = ((uint64_t)result == unsigned_sum) ? 1 : 0;
+       *overflow = ((int64_t)result == signed_sum) ? 0 : 1;
+       
+       return result;
+}
+
+static void
+fbt_emulate(int _op, struct trapframe *frame)
+{
+       uint32_t op = _op;
+
+       switch (op >> 28) {
+       case DTRACE_INVOP_MOV_IP_SP:
+               /* mov ip, sp */
+               frame->tf_ip = frame->tf_svc_sp;
+               frame->tf_pc += 4;
+               break;
+       case DTRACE_INVOP_BX_LR:
+               /* bx lr */
+               frame->tf_pc = frame->tf_svc_lr;
+               break;
+       case DTRACE_INVOP_MOV_PC_LR:
+               /* mov pc, lr */
+               frame->tf_pc = frame->tf_svc_lr;
+               break;
+       case DTRACE_INVOP_LDM:
+               /* ldm sp, {..., pc} */
+               /* FALLTHRU */
+       case DTRACE_INVOP_LDMIB: {
+               /* ldmib sp, {..., pc} */
+               uint32_t register_list = (op & 0xffff);
+               uint32_t *sp = (uint32_t *)(intptr_t)frame->tf_svc_sp;
+               uint32_t *regs = &frame->tf_r0;
+               int i;
+
+               /* IDMIB */
+               if ((op >> 28) == 5)
+                       sp++;
+
+               for (i=0; i <= 12; i++) {
+                       if (register_list & (1 << i))
+                               regs[i] = *sp++;
+               }
+               if (register_list & (1 << 13))
+                       frame->tf_svc_sp = *sp++;
+               if (register_list & (1 << 14))
+                       frame->tf_svc_lr = *sp++;
+               frame->tf_pc = *sp;
+               break;
+       }
+       case DTRACE_INVOP_LDR_IMM: {
+               /* ldr r?, [{pc,r?}, #?] */
+               uint32_t rt = (op >> 12) & 0xf;
+               uint32_t rn = (op >> 16) & 0xf;
+               uint32_t imm = op & 0xfff;
+               uint32_t *regs = &frame->tf_r0;
+               KDASSERT(rt <= 12);
+               KDASSERT(rn == 15 || rn =< 12);
+               if (rn == 15)
+                       regs[rt] = *((uint32_t *)(intptr_t)(frame->tf_pc + 8 + imm));
+               else
+                       regs[rt] = *((uint32_t *)(intptr_t)(regs[rn] + imm));
+               frame->tf_pc += 4;
+               break;
+       }
+       case DTRACE_INVOP_MOVW: {
+               /* movw r?, #? */
+               uint32_t rd = (op >> 12) & 0xf;
+               uint32_t imm = (op & 0xfff) | ((op & 0xf0000) >> 4);
+               uint32_t *regs = &frame->tf_r0;
+               KDASSERT(rd <= 12);
+               regs[rd] = imm;
+               frame->tf_pc += 4;
+               break;
+       }
+       case DTRACE_INVOP_MOV_IMM: {
+               /* mov r?, #? */
+               uint32_t rd = (op >> 12) & 0xf;
+               uint32_t imm = expand_imm(op & 0xfff);
+               uint32_t *regs = &frame->tf_r0;
+               KDASSERT(rd <= 12);
+               regs[rd] = imm;
+               frame->tf_pc += 4;
+               break;
+       }
+       case DTRACE_INVOP_CMP_IMM: {
+               /* cmp r?, #? */
+               uint32_t rn = (op >> 16) & 0xf;
+               uint32_t *regs = &frame->tf_r0;
+               uint32_t imm = expand_imm(op & 0xfff);
+               uint32_t spsr = frame->tf_spsr;
+               uint32_t result;
+               int carry;
+               int overflow;
+               /*
+                * (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), â??1â??);
+                * APSR.N = result<31>;
+                * APSR.Z = IsZeroBit(result);
+                * APSR.C = carry;
+                * APSR.V = overflow; 
+                */
+               KDASSERT(rn <= 12);
+               result = add_with_carry(regs[rn], ~imm, 1, &carry, &overflow);
+               if (result & 0x80000000)
+                       spsr |= PSR_N_bit;
+               else
+                       spsr &= ~PSR_N_bit;
+               if (result == 0)
+                       spsr |= PSR_Z_bit;
+               else
+                       spsr &= ~PSR_Z_bit;
+               if (carry)
+                       spsr |= PSR_C_bit;
+               else
+                       spsr &= ~PSR_C_bit;
+               if (overflow)
+                       spsr |= PSR_V_bit;
+               else
+                       spsr &= ~PSR_V_bit;
+
+#if 0
+               aprint_normal("pc=%x Rn=%x imm=%x %c%c%c%c\n", frame->tf_pc, regs[rn], imm,
+                   (spsr & PSR_N_bit) ? 'N' : 'n',
+                   (spsr & PSR_Z_bit) ? 'Z' : 'z',
+                   (spsr & PSR_C_bit) ? 'C' : 'c',
+                   (spsr & PSR_V_bit) ? 'V' : 'v');
+#endif
+               frame->tf_spsr = spsr;
+               frame->tf_pc += 4;
+               break;
+       }
+       case DTRACE_INVOP_B_LABEL: {
+               /* b ??? */
+               uint32_t imm = (op & 0x00ffffff) << 2;
+               int32_t diff;
+               /* SignExtend(imm26, 32) */
+               if (imm & 0x02000000)
+                       imm |= 0xfc000000;
+               diff = (int32_t)imm;
+               frame->tf_pc += 8 + diff;
+               break;
+       }
+       /* FIXME: push will overwrite trapframe... */
+       case DTRACE_INVOP_PUSH: {
+               /* push {...} */
+               uint32_t register_list = (op & 0xffff);
+               uint32_t *sp = (uint32_t *)(intptr_t)frame->tf_svc_sp;
+               uint32_t *regs = &frame->tf_r0;
+               int i;
+               int count = 0;
+
+#if 0
+               if ((op & 0x0fff0fff) == 0x052d0004) {
+                       /* A2: str r4, [sp, #-4]! */
+                       *(sp - 1) = regs[4];
+                       frame->tf_pc += 4;
+                       break;
+               }
+#endif
+
+               for (i=0; i < 16; i++) {
+                       if (register_list & (1 << i))
+                               count++;
+               }
+               sp -= count;
+
+               for (i=0; i <= 12; i++) {
+                       if (register_list & (1 << i))
+                               *sp++ = regs[i];
+               }
+               if (register_list & (1 << 13))



Home | Main Index | Thread Index | Old Index