static inline int handle_cpu_signal(uintptr_t pc, unsigned long address,

                                    int is_write, sigset_t *old_set)

{

    CPUState *cpu = current_cpu;

    CPUClass *cc;

    int ret;



    /* We must handle PC addresses from two different sources:

     * a call return address and a signal frame address.

     *

     * Within cpu_restore_state_from_tb we assume the former and adjust

     * the address by -GETPC_ADJ so that the address is within the call

     * insn so that addr does not accidentally match the beginning of the

     * next guest insn.

     *

     * However, when the PC comes from the signal frame, it points to

     * the actual faulting host insn and not a call insn.  Subtracting

     * GETPC_ADJ in that case may accidentally match the previous guest insn.

     *

     * So for the later case, adjust forward to compensate for what

     * will be done later by cpu_restore_state_from_tb.

     */

    if (helper_retaddr) {

        pc = helper_retaddr;

    } else {

        pc += GETPC_ADJ;

    }



    /* For synchronous signals we expect to be coming from the vCPU

     * thread (so current_cpu should be valid) and either from running

     * code or during translation which can fault as we cross pages.

     *

     * If neither is true then something has gone wrong and we should

     * abort rather than try and restart the vCPU execution.

     */

    if (!cpu || !cpu->running) {

        printf("qemu:%s received signal outside vCPU context @ pc=0x%"

               PRIxPTR "\n",  __func__, pc);

        abort();

    }



#if defined(DEBUG_SIGNAL)

    printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",

           pc, address, is_write, *(unsigned long *)old_set);

#endif

    /* XXX: locking issue */

    if (is_write && h2g_valid(address)) {

        switch (page_unprotect(h2g(address), pc)) {

        case 0:

            /* Fault not caused by a page marked unwritable to protect

             * cached translations, must be the guest binary's problem.

             */

            break;

        case 1:

            /* Fault caused by protection of cached translation; TBs

             * invalidated, so resume execution.  Retain helper_retaddr

             * for a possible second fault.

             */

            return 1;

        case 2:

            /* Fault caused by protection of cached translation, and the

             * currently executing TB was modified and must be exited

             * immediately.  Clear helper_retaddr for next execution.

             */

            helper_retaddr = 0;

            cpu_exit_tb_from_sighandler(cpu, old_set);

            /* NORETURN */



        default:

            g_assert_not_reached();

        }

    }



    /* Convert forcefully to guest address space, invalid addresses

       are still valid segv ones */

    address = h2g_nocheck(address);



    cc = CPU_GET_CLASS(cpu);

    /* see if it is an MMU fault */

    g_assert(cc->handle_mmu_fault);

    ret = cc->handle_mmu_fault(cpu, address, is_write, MMU_USER_IDX);



    if (ret == 0) {

        /* The MMU fault was handled without causing real CPU fault.

         *  Retain helper_retaddr for a possible second fault.

         */

        return 1;

    }



    /* All other paths lead to cpu_exit; clear helper_retaddr

     * for next execution.

     */

    helper_retaddr = 0;



    if (ret < 0) {

        return 0; /* not an MMU fault */

    }



    /* Now we have a real cpu fault.  */

    cpu_restore_state(cpu, pc);



    sigprocmask(SIG_SETMASK, old_set, NULL);

    cpu_loop_exit(cpu);



    /* never comes here */

    return 1;

}
