| /*--------------------------------------------------------------------*/ |
| /*--- The core dispatch loop, for jumping to a code address. ---*/ |
| /*--- dispatch-arm-linux.S ---*/ |
| /*--------------------------------------------------------------------*/ |
| |
| /* |
| This file is part of Valgrind, a dynamic binary instrumentation |
| framework. |
| |
| Copyright (C) 2008-2010 Evan Geller |
| gaze@bea.ms |
| |
| This program is free software; you can redistribute it and/or |
| modify it under the terms of the GNU General Public License as |
| published by the Free Software Foundation; either version 2 of the |
| License, or (at your option) any later version. |
| |
| This program 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 |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 02111-1307, USA. |
| |
| The GNU General Public License is contained in the file COPYING. |
| */ |
| |
| #if defined(VGP_arm_linux) |
| .fpu vfp |
| |
| #include "pub_core_basics_asm.h" |
| #include "pub_core_dispatch_asm.h" |
| #include "pub_core_transtab_asm.h" |
| #include "libvex_guest_offsets.h" /* for OFFSET_arm_R* */ |
| |
| |
| /*------------------------------------------------------------*/ |
| /*--- ---*/ |
| /*--- The dispatch loop. VG_(run_innerloop) is used to ---*/ |
| /*--- run all translations except no-redir ones. ---*/ |
| /*--- ---*/ |
| /*------------------------------------------------------------*/ |
| |
| /*----------------------------------------------------*/ |
| /*--- Preamble (set everything up) ---*/ |
| /*----------------------------------------------------*/ |
| |
| /* signature: |
| UWord VG_(run_innerloop) ( void* guest_state, UWord do_profiling ); |
| */ |
| .text |
| .globl VG_(run_innerloop) |
| VG_(run_innerloop): |
| push {r0, r1, r4, r5, r6, r7, r8, r9, fp, lr} |
| |
| /* set FPSCR to vex-required default value */ |
| mov r4, #0 |
| fmxr fpscr, r4 |
| |
| /* r0 (hence also [sp,#0]) holds guest_state */ |
| /* r1 holds do_profiling */ |
| mov r8, r0 |
| ldr r0, [r8, #OFFSET_arm_R15T] |
| |
| /* fall into main loop (the right one) */ |
| cmp r1, #0 /* do_profiling */ |
| beq VG_(run_innerloop__dispatch_unprofiled) |
| b VG_(run_innerloop__dispatch_profiled) |
| |
| |
| /*----------------------------------------------------*/ |
| /*--- NO-PROFILING (standard) dispatcher ---*/ |
| /*----------------------------------------------------*/ |
| |
| /* Pairing of insns below is my guesstimate of how dual dispatch would |
| work on an A8. JRS, 2011-May-28 */ |
| |
| .global VG_(run_innerloop__dispatch_unprofiled) |
| VG_(run_innerloop__dispatch_unprofiled): |
| |
| /* AT ENTRY: r0 is next guest addr, r8 is possibly |
| modified guest state ptr */ |
| |
| /* Has the guest state pointer been messed with? If yes, exit. */ |
| movw r3, #:lower16:VG_(dispatch_ctr) |
| tst r8, #1 |
| |
| movt r3, #:upper16:VG_(dispatch_ctr) |
| |
| bne gsp_changed |
| |
| /* save the jump address in the guest state */ |
| str r0, [r8, #OFFSET_arm_R15T] |
| |
| /* Are we out of timeslice? If yes, defer to scheduler. */ |
| ldr r2, [r3] |
| |
| subs r2, r2, #1 |
| |
| str r2, [r3] |
| |
| beq counter_is_zero |
| |
| /* try a fast lookup in the translation cache */ |
| // r0 = next guest, r1,r2,r3,r4 scratch |
| movw r1, #VG_TT_FAST_MASK // r1 = VG_TT_FAST_MASK |
| movw r4, #:lower16:VG_(tt_fast) |
| |
| and r2, r1, r0, LSR #1 // r2 = entry # |
| movt r4, #:upper16:VG_(tt_fast) // r4 = &VG_(tt_fast) |
| |
| add r1, r4, r2, LSL #3 // r1 = &tt_fast[entry#] |
| |
| ldrd r4, r5, [r1, #0] // r4 = .guest, r5 = .host |
| |
| cmp r4, r0 |
| |
| bne fast_lookup_failed |
| // r5: next-host r8: live, gsp |
| // r4: next-guest |
| // r2: entry # |
| // LIVE: r5, r8; all others dead |
| |
| /* Found a match. Jump to .host. */ |
| blx r5 |
| b VG_(run_innerloop__dispatch_unprofiled) |
| .ltorg |
| /*NOTREACHED*/ |
| |
| /*----------------------------------------------------*/ |
| /*--- PROFILING dispatcher (can be much slower) ---*/ |
| /*----------------------------------------------------*/ |
| |
| .global VG_(run_innerloop__dispatch_profiled) |
| VG_(run_innerloop__dispatch_profiled): |
| |
| /* AT ENTRY: r0 is next guest addr, r8 is possibly |
| modified guest state ptr */ |
| |
| /* Has the guest state pointer been messed with? If yes, exit. */ |
| movw r3, #:lower16:VG_(dispatch_ctr) |
| tst r8, #1 |
| |
| movt r3, #:upper16:VG_(dispatch_ctr) |
| |
| bne gsp_changed |
| |
| /* save the jump address in the guest state */ |
| str r0, [r8, #OFFSET_arm_R15T] |
| |
| /* Are we out of timeslice? If yes, defer to scheduler. */ |
| ldr r2, [r3] |
| |
| subs r2, r2, #1 |
| |
| str r2, [r3] |
| |
| beq counter_is_zero |
| |
| /* try a fast lookup in the translation cache */ |
| // r0 = next guest, r1,r2,r3,r4 scratch |
| movw r1, #VG_TT_FAST_MASK // r1 = VG_TT_FAST_MASK |
| movw r4, #:lower16:VG_(tt_fast) |
| |
| and r2, r1, r0, LSR #1 // r2 = entry # |
| movt r4, #:upper16:VG_(tt_fast) // r4 = &VG_(tt_fast) |
| |
| add r1, r4, r2, LSL #3 // r1 = &tt_fast[entry#] |
| |
| ldrd r4, r5, [r1, #0] // r4 = .guest, r5 = .host |
| |
| cmp r4, r0 |
| |
| bne fast_lookup_failed |
| // r5: next-host r8: live, gsp |
| // r4: next-guest |
| // r2: entry # |
| // LIVE: r5, r8; all others dead |
| |
| /* increment bb profile counter */ |
| movw r0, #:lower16:VG_(tt_fastN) |
| movt r0, #:upper16:VG_(tt_fastN) // r0 = &tt_fastN[0] |
| ldr r0, [r0, r2, LSL #2] // r0 = tt_fast[entry #] |
| ldr r3, [r0] // *r0 ++ |
| add r3, r3, #1 |
| str r3, [r0] |
| |
| /* Found a match. Jump to .host. */ |
| blx r5 |
| b VG_(run_innerloop__dispatch_profiled) |
| /*NOTREACHED*/ |
| |
| /*----------------------------------------------------*/ |
| /*--- exit points ---*/ |
| /*----------------------------------------------------*/ |
| |
| gsp_changed: |
| // r0 = next guest addr (R15T), r8 = modified gsp |
| /* Someone messed with the gsp. Have to |
| defer to scheduler to resolve this. dispatch ctr |
| is not yet decremented, so no need to increment. */ |
| /* R15T is NOT up to date here. First, need to write |
| r0 back to R15T, but without trashing r8 since |
| that holds the value we want to return to the scheduler. |
| Hence use r1 transiently for the guest state pointer. */ |
| ldr r1, [sp, #0] |
| str r0, [r1, #OFFSET_arm_R15T] |
| mov r0, r8 // "return modified gsp" |
| b run_innerloop_exit |
| /*NOTREACHED*/ |
| |
| counter_is_zero: |
| /* R15T is up to date here */ |
| /* Back out increment of the dispatch ctr */ |
| ldr r1, =VG_(dispatch_ctr) |
| ldr r2, [r1] |
| add r2, r2, #1 |
| str r2, [r1] |
| mov r0, #VG_TRC_INNER_COUNTERZERO |
| b run_innerloop_exit |
| /*NOTREACHED*/ |
| |
| fast_lookup_failed: |
| /* R15T is up to date here */ |
| /* Back out increment of the dispatch ctr */ |
| ldr r1, =VG_(dispatch_ctr) |
| ldr r2, [r1] |
| add r2, r2, #1 |
| str r2, [r1] |
| mov r0, #VG_TRC_INNER_FASTMISS |
| b run_innerloop_exit |
| /*NOTREACHED*/ |
| |
| /* All exits from the dispatcher go through here. %r0 holds |
| the return value. |
| */ |
| run_innerloop_exit: |
| /* We're leaving. Check that nobody messed with |
| FPSCR in ways we don't expect. */ |
| fmrx r4, fpscr |
| bic r4, #0xF8000000 /* mask out NZCV and QC */ |
| bic r4, #0x0000009F /* mask out IDC,IXC,UFC,OFC,DZC,IOC */ |
| cmp r4, #0 |
| bne invariant_violation |
| b run_innerloop_exit_REALLY |
| |
| invariant_violation: |
| mov r0, #VG_TRC_INVARIANT_FAILED |
| b run_innerloop_exit_REALLY |
| |
| run_innerloop_exit_REALLY: |
| add sp, sp, #8 |
| pop {r4, r5, r6, r7, r8, r9, fp, pc} |
| |
| .size VG_(run_innerloop), .-VG_(run_innerloop) |
| |
| |
| /*------------------------------------------------------------*/ |
| /*--- ---*/ |
| /*--- A special dispatcher, for running no-redir ---*/ |
| /*--- translations. Just runs the given translation once. ---*/ |
| /*--- ---*/ |
| /*------------------------------------------------------------*/ |
| |
| /* signature: |
| void VG_(run_a_noredir_translation) ( UWord* argblock ); |
| */ |
| |
| /* Run a no-redir translation. argblock points to 4 UWords, 2 to carry args |
| and 2 to carry results: |
| 0: input: ptr to translation |
| 1: input: ptr to guest state |
| 2: output: next guest PC |
| 3: output: guest state pointer afterwards (== thread return code) |
| */ |
| .global VG_(run_a_noredir_translation) |
| VG_(run_a_noredir_translation): |
| push {r0,r1 /* EABI compliance */, r4-r12, lr} |
| ldr r8, [r0, #4] |
| mov lr, pc |
| ldr pc, [r0, #0] |
| |
| pop {r1} |
| str r0, [r1, #8] |
| str r8, [r1, #12] |
| pop {r1/*EABI compliance*/,r4-r12, pc} |
| |
| .size VG_(run_a_noredir_translation), .-VG_(run_a_noredir_translation) |
| |
| /* Let the linker know we don't need an executable stack */ |
| .section .note.GNU-stack,"",%progbits |
| |
| #endif // defined(VGP_arm_linux) |
| |
| /*--------------------------------------------------------------------*/ |
| /*--- end dispatch-arm-linux.S ---*/ |
| /*--------------------------------------------------------------------*/ |