No header includes qemu-common.h after this commit, as prescribed by qemu-common.h's file comment. Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-Id: <20190523143508.25387-5-armbru@redhat.com> [Rebased with conflicts resolved automatically, except for include/hw/arm/xlnx-zynqmp.h hw/arm/nrf51_soc.c hw/arm/msf2-soc.c block/qcow2-refcount.c block/qcow2-cluster.c block/qcow2-cache.c target/arm/cpu.h target/lm32/cpu.h target/m68k/cpu.h target/mips/cpu.h target/moxie/cpu.h target/nios2/cpu.h target/openrisc/cpu.h target/riscv/cpu.h target/tilegx/cpu.h target/tricore/cpu.h target/unicore32/cpu.h target/xtensa/cpu.h; bsd-user/main.c and net/tap-bsd.c fixed up]
		
			
				
	
	
		
			288 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			288 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 *  qemu user cpu loop
 | 
						|
 *
 | 
						|
 *  Copyright (c) 2003-2008 Fabrice Bellard
 | 
						|
 *
 | 
						|
 *  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, see <http://www.gnu.org/licenses/>.
 | 
						|
 */
 | 
						|
 | 
						|
#include "qemu/osdep.h"
 | 
						|
#include "qemu-common.h"
 | 
						|
#include "qemu.h"
 | 
						|
#include "cpu_loop-common.h"
 | 
						|
 | 
						|
static void gen_sigill_reg(CPUTLGState *env)
 | 
						|
{
 | 
						|
    target_siginfo_t info;
 | 
						|
 | 
						|
    info.si_signo = TARGET_SIGILL;
 | 
						|
    info.si_errno = 0;
 | 
						|
    info.si_code = TARGET_ILL_PRVREG;
 | 
						|
    info._sifields._sigfault._addr = env->pc;
 | 
						|
    queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
 | 
						|
}
 | 
						|
 | 
						|
static void do_signal(CPUTLGState *env, int signo, int sigcode)
 | 
						|
{
 | 
						|
    target_siginfo_t info;
 | 
						|
 | 
						|
    info.si_signo = signo;
 | 
						|
    info.si_errno = 0;
 | 
						|
    info._sifields._sigfault._addr = env->pc;
 | 
						|
 | 
						|
    if (signo == TARGET_SIGSEGV) {
 | 
						|
        /* The passed in sigcode is a dummy; check for a page mapping
 | 
						|
           and pass either MAPERR or ACCERR.  */
 | 
						|
        target_ulong addr = env->excaddr;
 | 
						|
        info._sifields._sigfault._addr = addr;
 | 
						|
        if (page_check_range(addr, 1, PAGE_VALID) < 0) {
 | 
						|
            sigcode = TARGET_SEGV_MAPERR;
 | 
						|
        } else {
 | 
						|
            sigcode = TARGET_SEGV_ACCERR;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    info.si_code = sigcode;
 | 
						|
 | 
						|
    queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
 | 
						|
}
 | 
						|
 | 
						|
static void gen_sigsegv_maperr(CPUTLGState *env, target_ulong addr)
 | 
						|
{
 | 
						|
    env->excaddr = addr;
 | 
						|
    do_signal(env, TARGET_SIGSEGV, 0);
 | 
						|
}
 | 
						|
 | 
						|
static void set_regval(CPUTLGState *env, uint8_t reg, uint64_t val)
 | 
						|
{
 | 
						|
    if (unlikely(reg >= TILEGX_R_COUNT)) {
 | 
						|
        switch (reg) {
 | 
						|
        case TILEGX_R_SN:
 | 
						|
        case TILEGX_R_ZERO:
 | 
						|
            return;
 | 
						|
        case TILEGX_R_IDN0:
 | 
						|
        case TILEGX_R_IDN1:
 | 
						|
        case TILEGX_R_UDN0:
 | 
						|
        case TILEGX_R_UDN1:
 | 
						|
        case TILEGX_R_UDN2:
 | 
						|
        case TILEGX_R_UDN3:
 | 
						|
            gen_sigill_reg(env);
 | 
						|
            return;
 | 
						|
        default:
 | 
						|
            g_assert_not_reached();
 | 
						|
        }
 | 
						|
    }
 | 
						|
    env->regs[reg] = val;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Compare the 8-byte contents of the CmpValue SPR with the 8-byte value in
 | 
						|
 * memory at the address held in the first source register. If the values are
 | 
						|
 * not equal, then no memory operation is performed. If the values are equal,
 | 
						|
 * the 8-byte quantity from the second source register is written into memory
 | 
						|
 * at the address held in the first source register. In either case, the result
 | 
						|
 * of the instruction is the value read from memory. The compare and write to
 | 
						|
 * memory are atomic and thus can be used for synchronization purposes. This
 | 
						|
 * instruction only operates for addresses aligned to a 8-byte boundary.
 | 
						|
 * Unaligned memory access causes an Unaligned Data Reference interrupt.
 | 
						|
 *
 | 
						|
 * Functional Description (64-bit)
 | 
						|
 *       uint64_t memVal = memoryReadDoubleWord (rf[SrcA]);
 | 
						|
 *       rf[Dest] = memVal;
 | 
						|
 *       if (memVal == SPR[CmpValueSPR])
 | 
						|
 *           memoryWriteDoubleWord (rf[SrcA], rf[SrcB]);
 | 
						|
 *
 | 
						|
 * Functional Description (32-bit)
 | 
						|
 *       uint64_t memVal = signExtend32 (memoryReadWord (rf[SrcA]));
 | 
						|
 *       rf[Dest] = memVal;
 | 
						|
 *       if (memVal == signExtend32 (SPR[CmpValueSPR]))
 | 
						|
 *           memoryWriteWord (rf[SrcA], rf[SrcB]);
 | 
						|
 *
 | 
						|
 *
 | 
						|
 * This function also processes exch and exch4 which need not process SPR.
 | 
						|
 */
 | 
						|
static void do_exch(CPUTLGState *env, bool quad, bool cmp)
 | 
						|
{
 | 
						|
    target_ulong addr;
 | 
						|
    target_long val, sprval;
 | 
						|
 | 
						|
    start_exclusive();
 | 
						|
 | 
						|
    addr = env->atomic_srca;
 | 
						|
    if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) {
 | 
						|
        goto sigsegv_maperr;
 | 
						|
    }
 | 
						|
 | 
						|
    if (cmp) {
 | 
						|
        if (quad) {
 | 
						|
            sprval = env->spregs[TILEGX_SPR_CMPEXCH];
 | 
						|
        } else {
 | 
						|
            sprval = sextract64(env->spregs[TILEGX_SPR_CMPEXCH], 0, 32);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (!cmp || val == sprval) {
 | 
						|
        target_long valb = env->atomic_srcb;
 | 
						|
        if (quad ? put_user_u64(valb, addr) : put_user_u32(valb, addr)) {
 | 
						|
            goto sigsegv_maperr;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    set_regval(env, env->atomic_dstr, val);
 | 
						|
    end_exclusive();
 | 
						|
    return;
 | 
						|
 | 
						|
 sigsegv_maperr:
 | 
						|
    end_exclusive();
 | 
						|
    gen_sigsegv_maperr(env, addr);
 | 
						|
}
 | 
						|
 | 
						|
static void do_fetch(CPUTLGState *env, int trapnr, bool quad)
 | 
						|
{
 | 
						|
    int8_t write = 1;
 | 
						|
    target_ulong addr;
 | 
						|
    target_long val, valb;
 | 
						|
 | 
						|
    start_exclusive();
 | 
						|
 | 
						|
    addr = env->atomic_srca;
 | 
						|
    valb = env->atomic_srcb;
 | 
						|
    if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) {
 | 
						|
        goto sigsegv_maperr;
 | 
						|
    }
 | 
						|
 | 
						|
    switch (trapnr) {
 | 
						|
    case TILEGX_EXCP_OPCODE_FETCHADD:
 | 
						|
    case TILEGX_EXCP_OPCODE_FETCHADD4:
 | 
						|
        valb += val;
 | 
						|
        break;
 | 
						|
    case TILEGX_EXCP_OPCODE_FETCHADDGEZ:
 | 
						|
        valb += val;
 | 
						|
        if (valb < 0) {
 | 
						|
            write = 0;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
    case TILEGX_EXCP_OPCODE_FETCHADDGEZ4:
 | 
						|
        valb += val;
 | 
						|
        if ((int32_t)valb < 0) {
 | 
						|
            write = 0;
 | 
						|
        }
 | 
						|
        break;
 | 
						|
    case TILEGX_EXCP_OPCODE_FETCHAND:
 | 
						|
    case TILEGX_EXCP_OPCODE_FETCHAND4:
 | 
						|
        valb &= val;
 | 
						|
        break;
 | 
						|
    case TILEGX_EXCP_OPCODE_FETCHOR:
 | 
						|
    case TILEGX_EXCP_OPCODE_FETCHOR4:
 | 
						|
        valb |= val;
 | 
						|
        break;
 | 
						|
    default:
 | 
						|
        g_assert_not_reached();
 | 
						|
    }
 | 
						|
 | 
						|
    if (write) {
 | 
						|
        if (quad ? put_user_u64(valb, addr) : put_user_u32(valb, addr)) {
 | 
						|
            goto sigsegv_maperr;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    set_regval(env, env->atomic_dstr, val);
 | 
						|
    end_exclusive();
 | 
						|
    return;
 | 
						|
 | 
						|
 sigsegv_maperr:
 | 
						|
    end_exclusive();
 | 
						|
    gen_sigsegv_maperr(env, addr);
 | 
						|
}
 | 
						|
 | 
						|
void cpu_loop(CPUTLGState *env)
 | 
						|
{
 | 
						|
    CPUState *cs = env_cpu(env);
 | 
						|
    int trapnr;
 | 
						|
 | 
						|
    while (1) {
 | 
						|
        cpu_exec_start(cs);
 | 
						|
        trapnr = cpu_exec(cs);
 | 
						|
        cpu_exec_end(cs);
 | 
						|
        process_queued_cpu_work(cs);
 | 
						|
 | 
						|
        switch (trapnr) {
 | 
						|
        case TILEGX_EXCP_SYSCALL:
 | 
						|
        {
 | 
						|
            abi_ulong ret = do_syscall(env, env->regs[TILEGX_R_NR],
 | 
						|
                                       env->regs[0], env->regs[1],
 | 
						|
                                       env->regs[2], env->regs[3],
 | 
						|
                                       env->regs[4], env->regs[5],
 | 
						|
                                       env->regs[6], env->regs[7]);
 | 
						|
            if (ret == -TARGET_ERESTARTSYS) {
 | 
						|
                env->pc -= 8;
 | 
						|
            } else if (ret != -TARGET_QEMU_ESIGRETURN) {
 | 
						|
                env->regs[TILEGX_R_RE] = ret;
 | 
						|
                env->regs[TILEGX_R_ERR] = TILEGX_IS_ERRNO(ret) ? -ret : 0;
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        case TILEGX_EXCP_OPCODE_EXCH:
 | 
						|
            do_exch(env, true, false);
 | 
						|
            break;
 | 
						|
        case TILEGX_EXCP_OPCODE_EXCH4:
 | 
						|
            do_exch(env, false, false);
 | 
						|
            break;
 | 
						|
        case TILEGX_EXCP_OPCODE_CMPEXCH:
 | 
						|
            do_exch(env, true, true);
 | 
						|
            break;
 | 
						|
        case TILEGX_EXCP_OPCODE_CMPEXCH4:
 | 
						|
            do_exch(env, false, true);
 | 
						|
            break;
 | 
						|
        case TILEGX_EXCP_OPCODE_FETCHADD:
 | 
						|
        case TILEGX_EXCP_OPCODE_FETCHADDGEZ:
 | 
						|
        case TILEGX_EXCP_OPCODE_FETCHAND:
 | 
						|
        case TILEGX_EXCP_OPCODE_FETCHOR:
 | 
						|
            do_fetch(env, trapnr, true);
 | 
						|
            break;
 | 
						|
        case TILEGX_EXCP_OPCODE_FETCHADD4:
 | 
						|
        case TILEGX_EXCP_OPCODE_FETCHADDGEZ4:
 | 
						|
        case TILEGX_EXCP_OPCODE_FETCHAND4:
 | 
						|
        case TILEGX_EXCP_OPCODE_FETCHOR4:
 | 
						|
            do_fetch(env, trapnr, false);
 | 
						|
            break;
 | 
						|
        case TILEGX_EXCP_SIGNAL:
 | 
						|
            do_signal(env, env->signo, env->sigcode);
 | 
						|
            break;
 | 
						|
        case TILEGX_EXCP_REG_IDN_ACCESS:
 | 
						|
        case TILEGX_EXCP_REG_UDN_ACCESS:
 | 
						|
            gen_sigill_reg(env);
 | 
						|
            break;
 | 
						|
        case EXCP_ATOMIC:
 | 
						|
            cpu_exec_step_atomic(cs);
 | 
						|
            break;
 | 
						|
        default:
 | 
						|
            fprintf(stderr, "trapnr is %d[0x%x].\n", trapnr, trapnr);
 | 
						|
            g_assert_not_reached();
 | 
						|
        }
 | 
						|
        process_pending_signals(env);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
 | 
						|
{
 | 
						|
    int i;
 | 
						|
    for (i = 0; i < TILEGX_R_COUNT; i++) {
 | 
						|
        env->regs[i] = regs->regs[i];
 | 
						|
    }
 | 
						|
    for (i = 0; i < TILEGX_SPR_COUNT; i++) {
 | 
						|
        env->spregs[i] = 0;
 | 
						|
    }
 | 
						|
    env->pc = regs->pc;
 | 
						|
}
 |