* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+#include <sys/resource.h>
#include "qemu.h"
+#include "qemu-common.h"
+#include "cache-utils.h"
+#include "cpu.h"
+#include "tcg.h"
+#include "qemu-timer.h"
+#include "envlist.h"
#define DEBUG_LOGFILE "/tmp/qemu.log"
-static const char *interp_prefix = CONFIG_QEMU_PREFIX;
-const char *qemu_uname_release = CONFIG_UNAME_RELEASE;
+char *exec_path;
-#if defined(__i386__) && !defined(CONFIG_STATIC)
-/* Force usage of an ELF interpreter even if it is an ELF shared
- object ! */
-const char interp[] __attribute__((section(".interp"))) = "/lib/ld-linux.so.2";
+int singlestep;
+unsigned long mmap_min_addr;
+#if defined(CONFIG_USE_GUEST_BASE)
+unsigned long guest_base;
+int have_guest_base;
+unsigned long reserved_va;
#endif
-/* for recent libc, we add these dummy symbols which are not declared
- when generating a linked object (bug in ld ?) */
-#if (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)) && !defined(CONFIG_STATIC)
-asm(".globl __preinit_array_start\n"
- ".globl __preinit_array_end\n"
- ".globl __init_array_start\n"
- ".globl __init_array_end\n"
- ".globl __fini_array_start\n"
- ".globl __fini_array_end\n"
- ".section \".rodata\"\n"
- "__preinit_array_start:\n"
- "__preinit_array_end:\n"
- "__init_array_start:\n"
- "__init_array_end:\n"
- "__fini_array_start:\n"
- "__fini_array_end:\n"
- ".long 0\n"
- ".previous\n");
-#endif
+static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
+const char *qemu_uname_release = CONFIG_UNAME_RELEASE;
/* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
we allocate a bigger stack. Need a better solution, for example
by remapping the process stack directly at the right place */
-unsigned long x86_stack_size = 512 * 1024;
+unsigned long guest_stack_size = 8 * 1024 * 1024UL;
void gemu_log(const char *fmt, ...)
{
va_end(ap);
}
-void cpu_outb(CPUState *env, int addr, int val)
+#if defined(TARGET_I386)
+int cpu_get_pic_interrupt(CPUState *env)
{
- fprintf(stderr, "outb: port=0x%04x, data=%02x\n", addr, val);
+ return -1;
}
+#endif
+
+/* timers for rdtsc */
+
+#if 0
+
+static uint64_t emu_time;
-void cpu_outw(CPUState *env, int addr, int val)
+int64_t cpu_get_real_ticks(void)
{
- fprintf(stderr, "outw: port=0x%04x, data=%04x\n", addr, val);
+ return emu_time++;
}
-void cpu_outl(CPUState *env, int addr, int val)
+#endif
+
+#if defined(CONFIG_USE_NPTL)
+/***********************************************************/
+/* Helper routines for implementing atomic operations. */
+
+/* To implement exclusive operations we force all cpus to syncronise.
+ We don't require a full sync, only that no cpus are executing guest code.
+ The alternative is to map target atomic ops onto host equivalents,
+ which requires quite a lot of per host/target work. */
+static pthread_mutex_t cpu_list_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t exclusive_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t exclusive_cond = PTHREAD_COND_INITIALIZER;
+static pthread_cond_t exclusive_resume = PTHREAD_COND_INITIALIZER;
+static int pending_cpus;
+
+/* Make sure everything is in a consistent state for calling fork(). */
+void fork_start(void)
{
- fprintf(stderr, "outl: port=0x%04x, data=%08x\n", addr, val);
+ pthread_mutex_lock(&tb_lock);
+ pthread_mutex_lock(&exclusive_lock);
+ mmap_fork_start();
}
-int cpu_inb(CPUState *env, int addr)
+void fork_end(int child)
{
- fprintf(stderr, "inb: port=0x%04x\n", addr);
- return 0;
+ mmap_fork_end(child);
+ if (child) {
+ /* Child processes created by fork() only have a single thread.
+ Discard information about the parent threads. */
+ first_cpu = thread_env;
+ thread_env->next_cpu = NULL;
+ pending_cpus = 0;
+ pthread_mutex_init(&exclusive_lock, NULL);
+ pthread_mutex_init(&cpu_list_mutex, NULL);
+ pthread_cond_init(&exclusive_cond, NULL);
+ pthread_cond_init(&exclusive_resume, NULL);
+ pthread_mutex_init(&tb_lock, NULL);
+ gdbserver_fork(thread_env);
+ } else {
+ pthread_mutex_unlock(&exclusive_lock);
+ pthread_mutex_unlock(&tb_lock);
+ }
}
-int cpu_inw(CPUState *env, int addr)
+/* Wait for pending exclusive operations to complete. The exclusive lock
+ must be held. */
+static inline void exclusive_idle(void)
{
- fprintf(stderr, "inw: port=0x%04x\n", addr);
- return 0;
+ while (pending_cpus) {
+ pthread_cond_wait(&exclusive_resume, &exclusive_lock);
+ }
}
-int cpu_inl(CPUState *env, int addr)
+/* Start an exclusive operation.
+ Must only be called from outside cpu_arm_exec. */
+static inline void start_exclusive(void)
{
- fprintf(stderr, "inl: port=0x%04x\n", addr);
- return 0;
+ CPUState *other;
+ pthread_mutex_lock(&exclusive_lock);
+ exclusive_idle();
+
+ pending_cpus = 1;
+ /* Make all other cpus stop executing. */
+ for (other = first_cpu; other; other = other->next_cpu) {
+ if (other->running) {
+ pending_cpus++;
+ cpu_exit(other);
+ }
+ }
+ if (pending_cpus > 1) {
+ pthread_cond_wait(&exclusive_cond, &exclusive_lock);
+ }
}
-int cpu_get_pic_interrupt(CPUState *env)
+/* Finish an exclusive operation. */
+static inline void end_exclusive(void)
{
- return -1;
+ pending_cpus = 0;
+ pthread_cond_broadcast(&exclusive_resume);
+ pthread_mutex_unlock(&exclusive_lock);
}
-/* timers for rdtsc */
+/* Wait for exclusive ops to finish, and begin cpu execution. */
+static inline void cpu_exec_start(CPUState *env)
+{
+ pthread_mutex_lock(&exclusive_lock);
+ exclusive_idle();
+ env->running = 1;
+ pthread_mutex_unlock(&exclusive_lock);
+}
-#if 0
+/* Mark cpu as not executing, and release pending exclusive ops. */
+static inline void cpu_exec_end(CPUState *env)
+{
+ pthread_mutex_lock(&exclusive_lock);
+ env->running = 0;
+ if (pending_cpus > 1) {
+ pending_cpus--;
+ if (pending_cpus == 1) {
+ pthread_cond_signal(&exclusive_cond);
+ }
+ }
+ exclusive_idle();
+ pthread_mutex_unlock(&exclusive_lock);
+}
-static uint64_t emu_time;
+void cpu_list_lock(void)
+{
+ pthread_mutex_lock(&cpu_list_mutex);
+}
-int64_t cpu_get_real_ticks(void)
+void cpu_list_unlock(void)
+{
+ pthread_mutex_unlock(&cpu_list_mutex);
+}
+#else /* if !CONFIG_USE_NPTL */
+/* These are no-ops because we are not threadsafe. */
+static inline void cpu_exec_start(CPUState *env)
+{
+}
+
+static inline void cpu_exec_end(CPUState *env)
+{
+}
+
+static inline void start_exclusive(void)
+{
+}
+
+static inline void end_exclusive(void)
+{
+}
+
+void fork_start(void)
+{
+}
+
+void fork_end(int child)
+{
+ if (child) {
+ gdbserver_fork(thread_env);
+ }
+}
+
+void cpu_list_lock(void)
{
- return emu_time++;
}
+void cpu_list_unlock(void)
+{
+}
#endif
+
#ifdef TARGET_I386
/***********************************************************/
/* CPUX86 core interface */
e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000);
e2 |= flags;
p = ptr;
- p[0] = tswapl(e1);
- p[1] = tswapl(e2);
+ p[0] = tswap32(e1);
+ p[1] = tswap32(e2);
}
-#if TARGET_X86_64
-uint64_t idt_table[512];
-
+static uint64_t *idt_table;
+#ifdef TARGET_X86_64
static void set_gate64(void *ptr, unsigned int type, unsigned int dpl,
uint64_t addr, unsigned int sel)
{
set_gate64(idt_table + n * 2, 0, dpl, 0, 0);
}
#else
-uint64_t idt_table[256];
-
static void set_gate(void *ptr, unsigned int type, unsigned int dpl,
uint32_t addr, unsigned int sel)
{
env->regs[R_EDX],
env->regs[R_ESI],
env->regs[R_EDI],
- env->regs[R_EBP]);
+ env->regs[R_EBP],
+ 0, 0);
break;
#ifndef TARGET_ABI32
case EXCP_SYSCALL:
- /* linux syscall from syscall intruction */
+ /* linux syscall from syscall instruction */
env->regs[R_EAX] = do_syscall(env,
env->regs[R_EAX],
env->regs[R_EDI],
env->regs[R_EDX],
env->regs[10],
env->regs[8],
- env->regs[9]);
+ env->regs[9],
+ 0, 0);
env->eip = env->exception_next_eip;
break;
#endif
info.si_errno = 0;
info.si_code = TARGET_SI_KERNEL;
info._sifields._sigfault._addr = 0;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
break;
case EXCP0D_GPF:
/* XXX: potential problem if ABI32 */
info.si_errno = 0;
info.si_code = TARGET_SI_KERNEL;
info._sifields._sigfault._addr = 0;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
}
break;
case EXCP0E_PAGE:
else
info.si_code = TARGET_SEGV_ACCERR;
info._sifields._sigfault._addr = env->cr[2];
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
break;
case EXCP00_DIVZ:
#ifndef TARGET_X86_64
info.si_errno = 0;
info.si_code = TARGET_FPE_INTDIV;
info._sifields._sigfault._addr = env->eip;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
}
break;
- case EXCP01_SSTP:
+ case EXCP01_DB:
case EXCP03_INT3:
#ifndef TARGET_X86_64
if (env->eflags & VM_MASK) {
{
info.si_signo = SIGTRAP;
info.si_errno = 0;
- if (trapnr == EXCP01_SSTP) {
+ if (trapnr == EXCP01_DB) {
info.si_code = TARGET_TRAP_BRKPT;
info._sifields._sigfault._addr = env->eip;
} else {
info.si_code = TARGET_SI_KERNEL;
info._sifields._sigfault._addr = 0;
}
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
}
break;
case EXCP04_INTO:
info.si_errno = 0;
info.si_code = TARGET_SI_KERNEL;
info._sifields._sigfault._addr = 0;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
}
break;
case EXCP06_ILLOP:
info.si_errno = 0;
info.si_code = TARGET_ILL_ILLOPN;
info._sifields._sigfault._addr = env->eip;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
break;
case EXCP_INTERRUPT:
/* just indicate that signals should be handled asap */
info.si_signo = sig;
info.si_errno = 0;
info.si_code = TARGET_TRAP_BRKPT;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
}
}
break;
#ifdef TARGET_ARM
-/* XXX: find a better solution */
-extern void tb_invalidate_page_range(abi_ulong start, abi_ulong end);
+/*
+ * See the Linux kernel's Documentation/arm/kernel_user_helpers.txt
+ * Input:
+ * r0 = pointer to oldval
+ * r1 = pointer to newval
+ * r2 = pointer to target value
+ *
+ * Output:
+ * r0 = 0 if *ptr was changed, non-0 if no exchange happened
+ * C set if *ptr was changed, clear if no exchange happened
+ *
+ * Note segv's in kernel helpers are a bit tricky, we can set the
+ * data address sensibly but the PC address is just the entry point.
+ */
+static void arm_kernel_cmpxchg64_helper(CPUARMState *env)
+{
+ uint64_t oldval, newval, val;
+ uint32_t addr, cpsr;
+ target_siginfo_t info;
+
+ /* Based on the 32 bit code in do_kernel_trap */
+
+ /* XXX: This only works between threads, not between processes.
+ It's probably possible to implement this with native host
+ operations. However things like ldrex/strex are much harder so
+ there's not much point trying. */
+ start_exclusive();
+ cpsr = cpsr_read(env);
+ addr = env->regs[2];
+
+ if (get_user_u64(oldval, env->regs[0])) {
+ env->cp15.c6_data = env->regs[0];
+ goto segv;
+ };
+
+ if (get_user_u64(newval, env->regs[1])) {
+ env->cp15.c6_data = env->regs[1];
+ goto segv;
+ };
+
+ if (get_user_u64(val, addr)) {
+ env->cp15.c6_data = addr;
+ goto segv;
+ }
+
+ if (val == oldval) {
+ val = newval;
+
+ if (put_user_u64(val, addr)) {
+ env->cp15.c6_data = addr;
+ goto segv;
+ };
-static void arm_cache_flush(abi_ulong start, abi_ulong last)
+ env->regs[0] = 0;
+ cpsr |= CPSR_C;
+ } else {
+ env->regs[0] = -1;
+ cpsr &= ~CPSR_C;
+ }
+ cpsr_write(env, cpsr, CPSR_C);
+ end_exclusive();
+ return;
+
+segv:
+ end_exclusive();
+ /* We get the PC of the entry address - which is as good as anything,
+ on a real kernel what you get depends on which mode it uses. */
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ /* XXX: check env->error_code */
+ info.si_code = TARGET_SEGV_MAPERR;
+ info._sifields._sigfault._addr = env->cp15.c6_data;
+ queue_signal(env, info.si_signo, &info);
+
+ end_exclusive();
+}
+
+/* Handle a jump to the kernel code page. */
+static int
+do_kernel_trap(CPUARMState *env)
{
- abi_ulong addr, last1;
+ uint32_t addr;
+ uint32_t cpsr;
+ uint32_t val;
+
+ switch (env->regs[15]) {
+ case 0xffff0fa0: /* __kernel_memory_barrier */
+ /* ??? No-op. Will need to do better for SMP. */
+ break;
+ case 0xffff0fc0: /* __kernel_cmpxchg */
+ /* XXX: This only works between threads, not between processes.
+ It's probably possible to implement this with native host
+ operations. However things like ldrex/strex are much harder so
+ there's not much point trying. */
+ start_exclusive();
+ cpsr = cpsr_read(env);
+ addr = env->regs[2];
+ /* FIXME: This should SEGV if the access fails. */
+ if (get_user_u32(val, addr))
+ val = ~env->regs[0];
+ if (val == env->regs[0]) {
+ val = env->regs[1];
+ /* FIXME: Check for segfaults. */
+ put_user_u32(val, addr);
+ env->regs[0] = 0;
+ cpsr |= CPSR_C;
+ } else {
+ env->regs[0] = -1;
+ cpsr &= ~CPSR_C;
+ }
+ cpsr_write(env, cpsr, CPSR_C);
+ end_exclusive();
+ break;
+ case 0xffff0fe0: /* __kernel_get_tls */
+ env->regs[0] = env->cp15.c13_tls2;
+ break;
+ case 0xffff0f60: /* __kernel_cmpxchg64 */
+ arm_kernel_cmpxchg64_helper(env);
+ break;
+
+ default:
+ return 1;
+ }
+ /* Jump back to the caller. */
+ addr = env->regs[14];
+ if (addr & 1) {
+ env->thumb = 1;
+ addr &= ~1;
+ }
+ env->regs[15] = addr;
- if (last < start)
- return;
- addr = start;
- for(;;) {
- last1 = ((addr + TARGET_PAGE_SIZE) & TARGET_PAGE_MASK) - 1;
- if (last1 > last)
- last1 = last;
- tb_invalidate_page_range(addr, last1 + 1);
- if (last1 == last)
- break;
- addr = last1 + 1;
+ return 0;
+}
+
+static int do_strex(CPUARMState *env)
+{
+ uint32_t val;
+ int size;
+ int rc = 1;
+ int segv = 0;
+ uint32_t addr;
+ start_exclusive();
+ addr = env->exclusive_addr;
+ if (addr != env->exclusive_test) {
+ goto fail;
+ }
+ size = env->exclusive_info & 0xf;
+ switch (size) {
+ case 0:
+ segv = get_user_u8(val, addr);
+ break;
+ case 1:
+ segv = get_user_u16(val, addr);
+ break;
+ case 2:
+ case 3:
+ segv = get_user_u32(val, addr);
+ break;
+ default:
+ abort();
}
+ if (segv) {
+ env->cp15.c6_data = addr;
+ goto done;
+ }
+ if (val != env->exclusive_val) {
+ goto fail;
+ }
+ if (size == 3) {
+ segv = get_user_u32(val, addr + 4);
+ if (segv) {
+ env->cp15.c6_data = addr + 4;
+ goto done;
+ }
+ if (val != env->exclusive_high) {
+ goto fail;
+ }
+ }
+ val = env->regs[(env->exclusive_info >> 8) & 0xf];
+ switch (size) {
+ case 0:
+ segv = put_user_u8(val, addr);
+ break;
+ case 1:
+ segv = put_user_u16(val, addr);
+ break;
+ case 2:
+ case 3:
+ segv = put_user_u32(val, addr);
+ break;
+ }
+ if (segv) {
+ env->cp15.c6_data = addr;
+ goto done;
+ }
+ if (size == 3) {
+ val = env->regs[(env->exclusive_info >> 12) & 0xf];
+ segv = put_user_u32(val, addr + 4);
+ if (segv) {
+ env->cp15.c6_data = addr + 4;
+ goto done;
+ }
+ }
+ rc = 0;
+fail:
+ env->regs[15] += 4;
+ env->regs[(env->exclusive_info >> 4) & 0xf] = rc;
+done:
+ end_exclusive();
+ return segv;
}
void cpu_loop(CPUARMState *env)
uint32_t addr;
for(;;) {
+ cpu_exec_start(env);
trapnr = cpu_arm_exec(env);
+ cpu_exec_end(env);
switch(trapnr) {
case EXCP_UDEF:
{
info.si_errno = 0;
info.si_code = TARGET_ILL_ILLOPN;
info._sifields._sigfault._addr = env->regs[15];
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
} else if (rc < 0) { /* FP exception */
int arm_fpe=0;
if (arm_fpe & BIT_IOC) info.si_code = TARGET_FPE_FLTINV;
info._sifields._sigfault._addr = env->regs[15];
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
} else {
env->regs[15] += 4;
}
}
if (n == ARM_NR_cacheflush) {
- arm_cache_flush(env->regs[0], env->regs[1]);
+ /* nop */
} else if (n == ARM_NR_semihosting
|| n == ARM_NR_thumb_semihosting) {
env->regs[0] = do_arm_semihosting (env);
n -= ARM_SYSCALL_BASE;
env->eabi = 0;
}
- env->regs[0] = do_syscall(env,
- n,
- env->regs[0],
- env->regs[1],
- env->regs[2],
- env->regs[3],
- env->regs[4],
- env->regs[5]);
+ if ( n > ARM_NR_BASE) {
+ switch (n) {
+ case ARM_NR_cacheflush:
+ /* nop */
+ break;
+ case ARM_NR_set_tls:
+ cpu_set_tls(env, env->regs[0]);
+ env->regs[0] = 0;
+ break;
+ default:
+ gemu_log("qemu: Unsupported ARM syscall: 0x%x\n",
+ n);
+ env->regs[0] = -TARGET_ENOSYS;
+ break;
+ }
+ } else {
+ env->regs[0] = do_syscall(env,
+ n,
+ env->regs[0],
+ env->regs[1],
+ env->regs[2],
+ env->regs[3],
+ env->regs[4],
+ env->regs[5],
+ 0, 0);
+ }
} else {
goto error;
}
/* just indicate that signals should be handled asap */
break;
case EXCP_PREFETCH_ABORT:
- addr = env->cp15.c6_data;
- goto do_segv;
- case EXCP_DATA_ABORT:
addr = env->cp15.c6_insn;
goto do_segv;
+ case EXCP_DATA_ABORT:
+ addr = env->cp15.c6_data;
do_segv:
{
info.si_signo = SIGSEGV;
/* XXX: check env->error_code */
info.si_code = TARGET_SEGV_MAPERR;
info._sifields._sigfault._addr = addr;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
}
break;
case EXCP_DEBUG:
info.si_signo = sig;
info.si_errno = 0;
info.si_code = TARGET_TRAP_BRKPT;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
}
}
break;
+ case EXCP_KERNEL_TRAP:
+ if (do_kernel_trap(env))
+ goto error;
+ break;
+ case EXCP_STREX:
+ if (do_strex(env)) {
+ addr = env->cp15.c6_data;
+ goto do_segv;
+ }
+ break;
default:
error:
fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n",
#endif
+#ifdef TARGET_UNICORE32
+
+void cpu_loop(CPUState *env)
+{
+ int trapnr;
+ unsigned int n, insn;
+ target_siginfo_t info;
+
+ for (;;) {
+ cpu_exec_start(env);
+ trapnr = uc32_cpu_exec(env);
+ cpu_exec_end(env);
+ switch (trapnr) {
+ case UC32_EXCP_PRIV:
+ {
+ /* system call */
+ get_user_u32(insn, env->regs[31] - 4);
+ n = insn & 0xffffff;
+
+ if (n >= UC32_SYSCALL_BASE) {
+ /* linux syscall */
+ n -= UC32_SYSCALL_BASE;
+ if (n == UC32_SYSCALL_NR_set_tls) {
+ cpu_set_tls(env, env->regs[0]);
+ env->regs[0] = 0;
+ } else {
+ env->regs[0] = do_syscall(env,
+ n,
+ env->regs[0],
+ env->regs[1],
+ env->regs[2],
+ env->regs[3],
+ env->regs[4],
+ env->regs[5],
+ 0, 0);
+ }
+ } else {
+ goto error;
+ }
+ }
+ break;
+ case UC32_EXCP_TRAP:
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ /* XXX: check env->error_code */
+ info.si_code = TARGET_SEGV_MAPERR;
+ info._sifields._sigfault._addr = env->cp0.c4_faultaddr;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case EXCP_INTERRUPT:
+ /* just indicate that signals should be handled asap */
+ break;
+ case EXCP_DEBUG:
+ {
+ int sig;
+
+ sig = gdb_handlesig(env, TARGET_SIGTRAP);
+ if (sig) {
+ info.si_signo = sig;
+ info.si_errno = 0;
+ info.si_code = TARGET_TRAP_BRKPT;
+ queue_signal(env, info.si_signo, &info);
+ }
+ }
+ break;
+ default:
+ goto error;
+ }
+ process_pending_signals(env);
+ }
+
+error:
+ fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", trapnr);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ abort();
+}
+#endif
+
#ifdef TARGET_SPARC
+#define SPARC64_STACK_BIAS 2047
//#define DEBUG_WIN
can be found at http://www.sics.se/~psm/sparcstack.html */
static inline int get_reg_index(CPUSPARCState *env, int cwp, int index)
{
- index = (index + cwp * 16) & (16 * NWINDOWS - 1);
+ index = (index + cwp * 16) % (16 * env->nwindows);
/* wrap handling : if cwp is on the last window, then we use the
registers 'after' the end */
- if (index < 8 && env->cwp == (NWINDOWS - 1))
- index += (16 * NWINDOWS);
+ if (index < 8 && env->cwp == env->nwindows - 1)
+ index += 16 * env->nwindows;
return index;
}
abi_ulong sp_ptr;
sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)];
+#ifdef TARGET_SPARC64
+ if (sp_ptr & 3)
+ sp_ptr += SPARC64_STACK_BIAS;
+#endif
#if defined(DEBUG_WIN)
- printf("win_overflow: sp_ptr=0x%x save_cwp=%d\n",
- (int)sp_ptr, cwp1);
+ printf("win_overflow: sp_ptr=0x" TARGET_ABI_FMT_lx " save_cwp=%d\n",
+ sp_ptr, cwp1);
#endif
for(i = 0; i < 16; i++) {
/* FIXME - what to do if put_user() fails? */
{
#ifndef TARGET_SPARC64
unsigned int new_wim;
- new_wim = ((env->wim >> 1) | (env->wim << (NWINDOWS - 1))) &
- ((1LL << NWINDOWS) - 1);
- save_window_offset(env, (env->cwp - 2) & (NWINDOWS - 1));
+ new_wim = ((env->wim >> 1) | (env->wim << (env->nwindows - 1))) &
+ ((1LL << env->nwindows) - 1);
+ save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2));
env->wim = new_wim;
#else
- save_window_offset(env, (env->cwp - 2) & (NWINDOWS - 1));
+ save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2));
env->cansave++;
env->canrestore--;
#endif
static void restore_window(CPUSPARCState *env)
{
- unsigned int new_wim, i, cwp1;
+#ifndef TARGET_SPARC64
+ unsigned int new_wim;
+#endif
+ unsigned int i, cwp1;
abi_ulong sp_ptr;
- new_wim = ((env->wim << 1) | (env->wim >> (NWINDOWS - 1))) &
- ((1LL << NWINDOWS) - 1);
+#ifndef TARGET_SPARC64
+ new_wim = ((env->wim << 1) | (env->wim >> (env->nwindows - 1))) &
+ ((1LL << env->nwindows) - 1);
+#endif
/* restore the invalid window */
- cwp1 = (env->cwp + 1) & (NWINDOWS - 1);
+ cwp1 = cpu_cwp_inc(env, env->cwp + 1);
sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)];
+#ifdef TARGET_SPARC64
+ if (sp_ptr & 3)
+ sp_ptr += SPARC64_STACK_BIAS;
+#endif
#if defined(DEBUG_WIN)
- printf("win_underflow: sp_ptr=0x%x load_cwp=%d\n",
- (int)sp_ptr, cwp1);
+ printf("win_underflow: sp_ptr=0x" TARGET_ABI_FMT_lx " load_cwp=%d\n",
+ sp_ptr, cwp1);
#endif
for(i = 0; i < 16; i++) {
/* FIXME - what to do if get_user() fails? */
get_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr);
sp_ptr += sizeof(abi_ulong);
}
- env->wim = new_wim;
#ifdef TARGET_SPARC64
env->canrestore++;
- if (env->cleanwin < NWINDOWS - 1)
- env->cleanwin++;
+ if (env->cleanwin < env->nwindows - 1)
+ env->cleanwin++;
env->cansave--;
+#else
+ env->wim = new_wim;
#endif
}
offset = 1;
for(;;) {
/* if restore would invoke restore_window(), then we can stop */
- cwp1 = (env->cwp + offset) & (NWINDOWS - 1);
+ cwp1 = cpu_cwp_inc(env, env->cwp + offset);
+#ifndef TARGET_SPARC64
if (env->wim & (1 << cwp1))
break;
+#else
+ if (env->canrestore == 0)
+ break;
+ env->cansave++;
+ env->canrestore--;
+#endif
save_window_offset(env, cwp1);
offset++;
}
+ cwp1 = cpu_cwp_inc(env, env->cwp + 1);
+#ifndef TARGET_SPARC64
/* set wim so that restore will reload the registers */
- cwp1 = (env->cwp + 1) & (NWINDOWS - 1);
env->wim = 1 << cwp1;
+#endif
#if defined(DEBUG_WIN)
printf("flush_windows: nb=%d\n", offset - 1);
#endif
void cpu_loop (CPUSPARCState *env)
{
- int trapnr, ret;
+ int trapnr;
+ abi_long ret;
target_siginfo_t info;
while (1) {
ret = do_syscall (env, env->gregs[1],
env->regwptr[0], env->regwptr[1],
env->regwptr[2], env->regwptr[3],
- env->regwptr[4], env->regwptr[5]);
- if ((unsigned int)ret >= (unsigned int)(-515)) {
+ env->regwptr[4], env->regwptr[5],
+ 0, 0);
+ if ((abi_ulong)ret >= (abi_ulong)(-515)) {
#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
env->xcc |= PSR_CARRY;
#else
/* XXX: check env->error_code */
info.si_code = TARGET_SEGV_MAPERR;
info._sifields._sigfault._addr = env->mmuregs[4];
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
}
break;
#else
if (trapnr == TT_DFAULT)
info._sifields._sigfault._addr = env->dmmuregs[4];
else
- info._sifields._sigfault._addr = env->tsptr->tpc;
- queue_signal(info.si_signo, &info);
+ info._sifields._sigfault._addr = cpu_tsptr(env)->tpc;
+ queue_signal(env, info.si_signo, &info);
}
break;
#ifndef TARGET_ABI32
info.si_signo = sig;
info.si_errno = 0;
info.si_code = TARGET_TRAP_BRKPT;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
}
}
break;
return 0;
}
-uint32_t cpu_ppc_load_tbl (CPUState *env)
+uint64_t cpu_ppc_load_tbl (CPUState *env)
{
- return cpu_ppc_get_tb(env) & 0xFFFFFFFF;
+ return cpu_ppc_get_tb(env);
}
uint32_t cpu_ppc_load_tbu (CPUState *env)
return cpu_ppc_get_tb(env) >> 32;
}
-uint32_t cpu_ppc_load_atbl (CPUState *env)
+uint64_t cpu_ppc_load_atbl (CPUState *env)
{
- return cpu_ppc_get_tb(env) & 0xFFFFFFFF;
+ return cpu_ppc_get_tb(env);
}
uint32_t cpu_ppc_load_atbu (CPUState *env)
}
/* XXX: to be fixed */
-int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, target_ulong *valp)
+int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp)
{
return -1;
}
-int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, target_ulong val)
+int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val)
{
return -1;
}
-#define EXCP_DUMP(env, fmt, args...) \
-do { \
- fprintf(stderr, fmt , ##args); \
- cpu_dump_state(env, stderr, fprintf, 0); \
- if (loglevel != 0) { \
- fprintf(logfile, fmt , ##args); \
- cpu_dump_state(env, logfile, fprintf, 0); \
- } \
+#define EXCP_DUMP(env, fmt, ...) \
+do { \
+ fprintf(stderr, fmt , ## __VA_ARGS__); \
+ cpu_dump_state(env, stderr, fprintf, 0); \
+ qemu_log(fmt, ## __VA_ARGS__); \
+ if (logfile) \
+ log_cpu_state(env, 0); \
} while (0)
+static int do_store_exclusive(CPUPPCState *env)
+{
+ target_ulong addr;
+ target_ulong page_addr;
+ target_ulong val;
+ int flags;
+ int segv = 0;
+
+ addr = env->reserve_ea;
+ page_addr = addr & TARGET_PAGE_MASK;
+ start_exclusive();
+ mmap_lock();
+ flags = page_get_flags(page_addr);
+ if ((flags & PAGE_READ) == 0) {
+ segv = 1;
+ } else {
+ int reg = env->reserve_info & 0x1f;
+ int size = (env->reserve_info >> 5) & 0xf;
+ int stored = 0;
+
+ if (addr == env->reserve_addr) {
+ switch (size) {
+ case 1: segv = get_user_u8(val, addr); break;
+ case 2: segv = get_user_u16(val, addr); break;
+ case 4: segv = get_user_u32(val, addr); break;
+#if defined(TARGET_PPC64)
+ case 8: segv = get_user_u64(val, addr); break;
+#endif
+ default: abort();
+ }
+ if (!segv && val == env->reserve_val) {
+ val = env->gpr[reg];
+ switch (size) {
+ case 1: segv = put_user_u8(val, addr); break;
+ case 2: segv = put_user_u16(val, addr); break;
+ case 4: segv = put_user_u32(val, addr); break;
+#if defined(TARGET_PPC64)
+ case 8: segv = put_user_u64(val, addr); break;
+#endif
+ default: abort();
+ }
+ if (!segv) {
+ stored = 1;
+ }
+ }
+ }
+ env->crf[0] = (stored << 1) | xer_so;
+ env->reserve_addr = (target_ulong)-1;
+ }
+ if (!segv) {
+ env->nip += 4;
+ }
+ mmap_unlock();
+ end_exclusive();
+ return segv;
+}
+
void cpu_loop(CPUPPCState *env)
{
target_siginfo_t info;
uint32_t ret;
for(;;) {
+ cpu_exec_start(env);
trapnr = cpu_ppc_exec(env);
+ cpu_exec_end(env);
switch(trapnr) {
case POWERPC_EXCP_NONE:
/* Just go on */
"Aborting\n");
break;
case POWERPC_EXCP_DSI: /* Data storage exception */
- EXCP_DUMP(env, "Invalid data memory access: 0x" ADDRX "\n",
+ EXCP_DUMP(env, "Invalid data memory access: 0x" TARGET_FMT_lx "\n",
env->spr[SPR_DAR]);
/* XXX: check this. Seems bugged */
switch (env->error_code & 0xFF000000) {
break;
}
info._sifields._sigfault._addr = env->nip;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
break;
case POWERPC_EXCP_ISI: /* Instruction storage exception */
- EXCP_DUMP(env, "Invalid instruction fetch: 0x\n" ADDRX "\n",
- env->spr[SPR_SRR0]);
+ EXCP_DUMP(env, "Invalid instruction fetch: 0x\n" TARGET_FMT_lx
+ "\n", env->spr[SPR_SRR0]);
/* XXX: check this */
switch (env->error_code & 0xFF000000) {
case 0x40000000:
break;
}
info._sifields._sigfault._addr = env->nip - 4;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
break;
case POWERPC_EXCP_EXTERNAL: /* External input */
cpu_abort(env, "External interrupt while in user mode. "
info.si_errno = 0;
info.si_code = TARGET_BUS_ADRALN;
info._sifields._sigfault._addr = env->nip - 4;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
break;
case POWERPC_EXCP_PROGRAM: /* Program exception */
/* XXX: check this */
break;
}
info._sifields._sigfault._addr = env->nip - 4;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
break;
case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */
EXCP_DUMP(env, "No floating point allowed\n");
info.si_errno = 0;
info.si_code = TARGET_ILL_COPROC;
info._sifields._sigfault._addr = env->nip - 4;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
break;
case POWERPC_EXCP_SYSCALL: /* System call exception */
cpu_abort(env, "Syscall exception while in user mode. "
info.si_errno = 0;
info.si_code = TARGET_ILL_COPROC;
info._sifields._sigfault._addr = env->nip - 4;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
break;
case POWERPC_EXCP_DECR: /* Decrementer exception */
cpu_abort(env, "Decrementer interrupt while in user mode. "
cpu_abort(env, "Instruction TLB exception while in user mode. "
"Aborting\n");
break;
- case POWERPC_EXCP_DEBUG: /* Debug interrupt */
- /* XXX: check this */
- {
- int sig;
-
- sig = gdb_handlesig(env, TARGET_SIGTRAP);
- if (sig) {
- info.si_signo = sig;
- info.si_errno = 0;
- info.si_code = TARGET_TRAP_BRKPT;
- queue_signal(info.si_signo, &info);
- }
- }
- break;
case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavail. */
EXCP_DUMP(env, "No SPE/floating-point instruction allowed\n");
info.si_signo = TARGET_SIGILL;
info.si_errno = 0;
info.si_code = TARGET_ILL_COPROC;
info._sifields._sigfault._addr = env->nip - 4;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
break;
case POWERPC_EXCP_EFPDI: /* Embedded floating-point data IRQ */
cpu_abort(env, "Embedded floating-point data IRQ not handled\n");
info.si_errno = 0;
info.si_code = TARGET_ILL_COPROC;
info._sifields._sigfault._addr = env->nip - 4;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
break;
case POWERPC_EXCP_PIT: /* Programmable interval timer IRQ */
cpu_abort(env, "Programable interval timer interrupt "
env->crf[0] &= ~0x1;
ret = do_syscall(env, env->gpr[0], env->gpr[3], env->gpr[4],
env->gpr[5], env->gpr[6], env->gpr[7],
- env->gpr[8]);
+ env->gpr[8], 0, 0);
+ if (ret == (uint32_t)(-TARGET_QEMU_ESIGRETURN)) {
+ /* Returning from a successful sigreturn syscall.
+ Avoid corrupting register state. */
+ break;
+ }
if (ret > (uint32_t)(-515)) {
env->crf[0] |= 0x1;
ret = -ret;
printf("syscall returned 0x%08x (%d)\n", ret, ret);
#endif
break;
+ case POWERPC_EXCP_STCX:
+ if (do_store_exclusive(env)) {
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SEGV_MAPERR;
+ info._sifields._sigfault._addr = env->nip;
+ queue_signal(env, info.si_signo, &info);
+ }
+ break;
+ case EXCP_DEBUG:
+ {
+ int sig;
+
+ sig = gdb_handlesig(env, TARGET_SIGTRAP);
+ if (sig) {
+ info.si_signo = sig;
+ info.si_errno = 0;
+ info.si_code = TARGET_TRAP_BRKPT;
+ queue_signal(env, info.si_signo, &info);
+ }
+ }
+ break;
case EXCP_INTERRUPT:
/* just indicate that signals should be handled asap */
break;
MIPS_SYS(sys_ipc , 6)
MIPS_SYS(sys_fsync , 1)
MIPS_SYS(sys_sigreturn , 0)
- MIPS_SYS(sys_clone , 0) /* 4120 */
+ MIPS_SYS(sys_clone , 6) /* 4120 */
MIPS_SYS(sys_setdomainname, 2)
MIPS_SYS(sys_newuname , 1)
MIPS_SYS(sys_ni_syscall , 0) /* sys_modify_ldt */
MIPS_SYS(sys_getcwd , 2)
MIPS_SYS(sys_capget , 2)
MIPS_SYS(sys_capset , 2) /* 4205 */
- MIPS_SYS(sys_sigaltstack , 0)
+ MIPS_SYS(sys_sigaltstack , 2)
MIPS_SYS(sys_sendfile , 4)
MIPS_SYS(sys_ni_syscall , 0)
MIPS_SYS(sys_ni_syscall , 0)
MIPS_SYS(sys_epoll_pwait, 6)
MIPS_SYS(sys_ioprio_set, 3)
MIPS_SYS(sys_ioprio_get, 2)
+ MIPS_SYS(sys_utimensat, 4)
+ MIPS_SYS(sys_signalfd, 3)
+ MIPS_SYS(sys_ni_syscall, 0) /* was timerfd */
+ MIPS_SYS(sys_eventfd, 1)
+ MIPS_SYS(sys_fallocate, 6) /* 4320 */
+ MIPS_SYS(sys_timerfd_create, 2)
+ MIPS_SYS(sys_timerfd_gettime, 2)
+ MIPS_SYS(sys_timerfd_settime, 4)
+ MIPS_SYS(sys_signalfd4, 4)
+ MIPS_SYS(sys_eventfd2, 2) /* 4325 */
+ MIPS_SYS(sys_epoll_create1, 1)
+ MIPS_SYS(sys_dup3, 3)
+ MIPS_SYS(sys_pipe2, 2)
+ MIPS_SYS(sys_inotify_init1, 1)
+ MIPS_SYS(sys_preadv, 6) /* 4330 */
+ MIPS_SYS(sys_pwritev, 6)
+ MIPS_SYS(sys_rt_tgsigqueueinfo, 4)
+ MIPS_SYS(sys_perf_event_open, 5)
+ MIPS_SYS(sys_accept4, 4)
+ MIPS_SYS(sys_recvmmsg, 5) /* 4335 */
+ MIPS_SYS(sys_fanotify_init, 2)
+ MIPS_SYS(sys_fanotify_mark, 6)
+ MIPS_SYS(sys_prlimit64, 4)
+ MIPS_SYS(sys_name_to_handle_at, 5)
+ MIPS_SYS(sys_open_by_handle_at, 3) /* 4340 */
+ MIPS_SYS(sys_clock_adjtime, 2)
+ MIPS_SYS(sys_syncfs, 1)
};
#undef MIPS_SYS
+static int do_store_exclusive(CPUMIPSState *env)
+{
+ target_ulong addr;
+ target_ulong page_addr;
+ target_ulong val;
+ int flags;
+ int segv = 0;
+ int reg;
+ int d;
+
+ addr = env->lladdr;
+ page_addr = addr & TARGET_PAGE_MASK;
+ start_exclusive();
+ mmap_lock();
+ flags = page_get_flags(page_addr);
+ if ((flags & PAGE_READ) == 0) {
+ segv = 1;
+ } else {
+ reg = env->llreg & 0x1f;
+ d = (env->llreg & 0x20) != 0;
+ if (d) {
+ segv = get_user_s64(val, addr);
+ } else {
+ segv = get_user_s32(val, addr);
+ }
+ if (!segv) {
+ if (val != env->llval) {
+ env->active_tc.gpr[reg] = 0;
+ } else {
+ if (d) {
+ segv = put_user_u64(env->llnewval, addr);
+ } else {
+ segv = put_user_u32(env->llnewval, addr);
+ }
+ if (!segv) {
+ env->active_tc.gpr[reg] = 1;
+ }
+ }
+ }
+ }
+ env->lladdr = -1;
+ if (!segv) {
+ env->active_tc.PC += 4;
+ }
+ mmap_unlock();
+ end_exclusive();
+ return segv;
+}
+
void cpu_loop(CPUMIPSState *env)
{
target_siginfo_t info;
unsigned int syscall_num;
for(;;) {
+ cpu_exec_start(env);
trapnr = cpu_mips_exec(env);
+ cpu_exec_end(env);
switch(trapnr) {
case EXCP_SYSCALL:
- syscall_num = env->gpr[env->current_tc][2] - 4000;
- env->PC[env->current_tc] += 4;
+ syscall_num = env->active_tc.gpr[2] - 4000;
+ env->active_tc.PC += 4;
if (syscall_num >= sizeof(mips_syscall_args)) {
- ret = -ENOSYS;
+ ret = -TARGET_ENOSYS;
} else {
int nb_args;
abi_ulong sp_reg;
abi_ulong arg5 = 0, arg6 = 0, arg7 = 0, arg8 = 0;
nb_args = mips_syscall_args[syscall_num];
- sp_reg = env->gpr[env->current_tc][29];
+ sp_reg = env->active_tc.gpr[29];
switch (nb_args) {
/* these arguments are taken from the stack */
/* FIXME - what to do if get_user() fails? */
default:
break;
}
- ret = do_syscall(env, env->gpr[env->current_tc][2],
- env->gpr[env->current_tc][4],
- env->gpr[env->current_tc][5],
- env->gpr[env->current_tc][6],
- env->gpr[env->current_tc][7],
- arg5, arg6/*, arg7, arg8*/);
+ ret = do_syscall(env, env->active_tc.gpr[2],
+ env->active_tc.gpr[4],
+ env->active_tc.gpr[5],
+ env->active_tc.gpr[6],
+ env->active_tc.gpr[7],
+ arg5, arg6, arg7, arg8);
+ }
+ if (ret == -TARGET_QEMU_ESIGRETURN) {
+ /* Returning from a successful sigreturn syscall.
+ Avoid clobbering register state. */
+ break;
}
if ((unsigned int)ret >= (unsigned int)(-1133)) {
- env->gpr[env->current_tc][7] = 1; /* error flag */
+ env->active_tc.gpr[7] = 1; /* error flag */
ret = -ret;
} else {
- env->gpr[env->current_tc][7] = 0; /* error flag */
+ env->active_tc.gpr[7] = 0; /* error flag */
}
- env->gpr[env->current_tc][2] = ret;
+ env->active_tc.gpr[2] = ret;
break;
case EXCP_TLBL:
case EXCP_TLBS:
+ case EXCP_AdEL:
+ case EXCP_AdES:
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ /* XXX: check env->error_code */
+ info.si_code = TARGET_SEGV_MAPERR;
+ info._sifields._sigfault._addr = env->CP0_BadVAddr;
+ queue_signal(env, info.si_signo, &info);
+ break;
case EXCP_CpU:
case EXCP_RI:
info.si_signo = TARGET_SIGILL;
info.si_errno = 0;
info.si_code = 0;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
break;
case EXCP_INTERRUPT:
/* just indicate that signals should be handled asap */
info.si_signo = sig;
info.si_errno = 0;
info.si_code = TARGET_TRAP_BRKPT;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
}
}
break;
+ case EXCP_SC:
+ if (do_store_exclusive(env)) {
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SEGV_MAPERR;
+ info._sifields._sigfault._addr = env->active_tc.PC;
+ queue_signal(env, info.si_signo, &info);
+ }
+ break;
default:
// error:
fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n",
switch (trapnr) {
case 0x160:
+ env->pc += 2;
ret = do_syscall(env,
env->gregs[3],
env->gregs[4],
env->gregs[6],
env->gregs[7],
env->gregs[0],
- env->gregs[1]);
+ env->gregs[1],
+ 0, 0);
env->gregs[0] = ret;
- env->pc += 2;
break;
case EXCP_INTERRUPT:
/* just indicate that signals should be handled asap */
info.si_signo = sig;
info.si_errno = 0;
info.si_code = TARGET_TRAP_BRKPT;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
}
}
break;
info.si_errno = 0;
info.si_code = TARGET_SEGV_MAPERR;
info._sifields._sigfault._addr = env->tea;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
break;
default:
info.si_errno = 0;
/* XXX: check env->error_code */
info.si_code = TARGET_SEGV_MAPERR;
- info._sifields._sigfault._addr = env->debug1;
- queue_signal(info.si_signo, &info);
+ info._sifields._sigfault._addr = env->pregs[PR_EDA];
+ queue_signal(env, info.si_signo, &info);
}
break;
case EXCP_INTERRUPT:
env->regs[12],
env->regs[13],
env->pregs[7],
- env->pregs[11]);
+ env->pregs[11],
+ 0, 0);
env->regs[10] = ret;
- env->pc += 2;
break;
case EXCP_DEBUG:
{
info.si_signo = sig;
info.si_errno = 0;
info.si_code = TARGET_TRAP_BRKPT;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
}
}
break;
}
#endif
-#ifdef TARGET_M68K
-
-void cpu_loop(CPUM68KState *env)
+#ifdef TARGET_MICROBLAZE
+void cpu_loop (CPUState *env)
{
- int trapnr;
- unsigned int n;
+ int trapnr, ret;
+ target_siginfo_t info;
+
+ while (1) {
+ trapnr = cpu_mb_exec (env);
+ switch (trapnr) {
+ case 0xaa:
+ {
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ /* XXX: check env->error_code */
+ info.si_code = TARGET_SEGV_MAPERR;
+ info._sifields._sigfault._addr = 0;
+ queue_signal(env, info.si_signo, &info);
+ }
+ break;
+ case EXCP_INTERRUPT:
+ /* just indicate that signals should be handled asap */
+ break;
+ case EXCP_BREAK:
+ /* Return address is 4 bytes after the call. */
+ env->regs[14] += 4;
+ ret = do_syscall(env,
+ env->regs[12],
+ env->regs[5],
+ env->regs[6],
+ env->regs[7],
+ env->regs[8],
+ env->regs[9],
+ env->regs[10],
+ 0, 0);
+ env->regs[3] = ret;
+ env->sregs[SR_PC] = env->regs[14];
+ break;
+ case EXCP_HW_EXCP:
+ env->regs[17] = env->sregs[SR_PC] + 4;
+ if (env->iflags & D_FLAG) {
+ env->sregs[SR_ESR] |= 1 << 12;
+ env->sregs[SR_PC] -= 4;
+ /* FIXME: if branch was immed, replay the imm aswell. */
+ }
+
+ env->iflags &= ~(IMM_FLAG | D_FLAG);
+
+ switch (env->sregs[SR_ESR] & 31) {
+ case ESR_EC_DIVZERO:
+ info.si_signo = SIGFPE;
+ info.si_errno = 0;
+ info.si_code = TARGET_FPE_FLTDIV;
+ info._sifields._sigfault._addr = 0;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case ESR_EC_FPU:
+ info.si_signo = SIGFPE;
+ info.si_errno = 0;
+ if (env->sregs[SR_FSR] & FSR_IO) {
+ info.si_code = TARGET_FPE_FLTINV;
+ }
+ if (env->sregs[SR_FSR] & FSR_DZ) {
+ info.si_code = TARGET_FPE_FLTDIV;
+ }
+ info._sifields._sigfault._addr = 0;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ default:
+ printf ("Unhandled hw-exception: 0x%x\n",
+ env->sregs[SR_ESR] & ESR_EC_MASK);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ exit (1);
+ break;
+ }
+ break;
+ case EXCP_DEBUG:
+ {
+ int sig;
+
+ sig = gdb_handlesig (env, TARGET_SIGTRAP);
+ if (sig)
+ {
+ info.si_signo = sig;
+ info.si_errno = 0;
+ info.si_code = TARGET_TRAP_BRKPT;
+ queue_signal(env, info.si_signo, &info);
+ }
+ }
+ break;
+ default:
+ printf ("Unhandled trap: 0x%x\n", trapnr);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ exit (1);
+ }
+ process_pending_signals (env);
+ }
+}
+#endif
+
+#ifdef TARGET_M68K
+
+void cpu_loop(CPUM68KState *env)
+{
+ int trapnr;
+ unsigned int n;
target_siginfo_t info;
TaskState *ts = env->opaque;
info.si_errno = 0;
info.si_code = TARGET_ILL_ILLOPN;
info._sifields._sigfault._addr = env->pc;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
break;
case EXCP_TRAP0:
{
env->dregs[3],
env->dregs[4],
env->dregs[5],
- env->aregs[0]);
+ env->aregs[0],
+ 0, 0);
}
break;
case EXCP_INTERRUPT:
/* XXX: check env->error_code */
info.si_code = TARGET_SEGV_MAPERR;
info._sifields._sigfault._addr = env->mmu.ar;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
}
break;
case EXCP_DEBUG:
info.si_signo = sig;
info.si_errno = 0;
info.si_code = TARGET_TRAP_BRKPT;
- queue_signal(info.si_signo, &info);
+ queue_signal(env, info.si_signo, &info);
}
}
break;
#endif /* TARGET_M68K */
#ifdef TARGET_ALPHA
+static void do_store_exclusive(CPUAlphaState *env, int reg, int quad)
+{
+ target_ulong addr, val, tmp;
+ target_siginfo_t info;
+ int ret = 0;
+
+ addr = env->lock_addr;
+ tmp = env->lock_st_addr;
+ env->lock_addr = -1;
+ env->lock_st_addr = 0;
+
+ start_exclusive();
+ mmap_lock();
+
+ if (addr == tmp) {
+ if (quad ? get_user_s64(val, addr) : get_user_s32(val, addr)) {
+ goto do_sigsegv;
+ }
+
+ if (val == env->lock_value) {
+ tmp = env->ir[reg];
+ if (quad ? put_user_u64(tmp, addr) : put_user_u32(tmp, addr)) {
+ goto do_sigsegv;
+ }
+ ret = 1;
+ }
+ }
+ env->ir[reg] = ret;
+ env->pc += 4;
+
+ mmap_unlock();
+ end_exclusive();
+ return;
+
+ do_sigsegv:
+ mmap_unlock();
+ end_exclusive();
+
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = TARGET_SEGV_MAPERR;
+ info._sifields._sigfault._addr = addr;
+ queue_signal(env, TARGET_SIGSEGV, &info);
+}
+
void cpu_loop (CPUState *env)
{
int trapnr;
target_siginfo_t info;
+ abi_long sysret;
while (1) {
trapnr = cpu_alpha_exec (env);
+ /* All of the traps imply a transition through PALcode, which
+ implies an REI instruction has been executed. Which means
+ that the intr_flag should be cleared. */
+ env->intr_flag = 0;
+
switch (trapnr) {
case EXCP_RESET:
fprintf(stderr, "Reset requested. Exit\n");
fprintf(stderr, "Machine check exception. Exit\n");
exit(1);
break;
- case EXCP_ARITH:
- fprintf(stderr, "Arithmetic trap.\n");
- exit(1);
- break;
- case EXCP_HW_INTERRUPT:
+ case EXCP_SMP_INTERRUPT:
+ case EXCP_CLK_INTERRUPT:
+ case EXCP_DEV_INTERRUPT:
fprintf(stderr, "External interrupt. Exit\n");
exit(1);
break;
- case EXCP_DFAULT:
- fprintf(stderr, "MMU data fault\n");
- exit(1);
- break;
- case EXCP_DTB_MISS_PAL:
- fprintf(stderr, "MMU data TLB miss in PALcode\n");
- exit(1);
- break;
- case EXCP_ITB_MISS:
- fprintf(stderr, "MMU instruction TLB miss\n");
- exit(1);
- break;
- case EXCP_ITB_ACV:
- fprintf(stderr, "MMU instruction access violation\n");
- exit(1);
- break;
- case EXCP_DTB_MISS_NATIVE:
- fprintf(stderr, "MMU data TLB miss\n");
- exit(1);
+ case EXCP_MMFAULT:
+ env->lock_addr = -1;
+ info.si_signo = TARGET_SIGSEGV;
+ info.si_errno = 0;
+ info.si_code = (page_get_flags(env->trap_arg0) & PAGE_VALID
+ ? TARGET_SEGV_ACCERR : TARGET_SEGV_MAPERR);
+ info._sifields._sigfault._addr = env->trap_arg0;
+ queue_signal(env, info.si_signo, &info);
break;
case EXCP_UNALIGN:
- fprintf(stderr, "Unaligned access\n");
- exit(1);
+ env->lock_addr = -1;
+ info.si_signo = TARGET_SIGBUS;
+ info.si_errno = 0;
+ info.si_code = TARGET_BUS_ADRALN;
+ info._sifields._sigfault._addr = env->trap_arg0;
+ queue_signal(env, info.si_signo, &info);
break;
case EXCP_OPCDEC:
- fprintf(stderr, "Invalid instruction\n");
- exit(1);
+ do_sigill:
+ env->lock_addr = -1;
+ info.si_signo = TARGET_SIGILL;
+ info.si_errno = 0;
+ info.si_code = TARGET_ILL_ILLOPC;
+ info._sifields._sigfault._addr = env->pc;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case EXCP_ARITH:
+ env->lock_addr = -1;
+ info.si_signo = TARGET_SIGFPE;
+ info.si_errno = 0;
+ info.si_code = TARGET_FPE_FLTINV;
+ info._sifields._sigfault._addr = env->pc;
+ queue_signal(env, info.si_signo, &info);
break;
case EXCP_FEN:
- fprintf(stderr, "Floating-point not allowed\n");
- exit(1);
+ /* No-op. Linux simply re-enables the FPU. */
+ break;
+ case EXCP_CALL_PAL:
+ env->lock_addr = -1;
+ switch (env->error_code) {
+ case 0x80:
+ /* BPT */
+ info.si_signo = TARGET_SIGTRAP;
+ info.si_errno = 0;
+ info.si_code = TARGET_TRAP_BRKPT;
+ info._sifields._sigfault._addr = env->pc;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case 0x81:
+ /* BUGCHK */
+ info.si_signo = TARGET_SIGTRAP;
+ info.si_errno = 0;
+ info.si_code = 0;
+ info._sifields._sigfault._addr = env->pc;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ case 0x83:
+ /* CALLSYS */
+ trapnr = env->ir[IR_V0];
+ sysret = do_syscall(env, trapnr,
+ env->ir[IR_A0], env->ir[IR_A1],
+ env->ir[IR_A2], env->ir[IR_A3],
+ env->ir[IR_A4], env->ir[IR_A5],
+ 0, 0);
+ if (trapnr == TARGET_NR_sigreturn
+ || trapnr == TARGET_NR_rt_sigreturn) {
+ break;
+ }
+ /* Syscall writes 0 to V0 to bypass error check, similar
+ to how this is handled internal to Linux kernel. */
+ if (env->ir[IR_V0] == 0) {
+ env->ir[IR_V0] = sysret;
+ } else {
+ env->ir[IR_V0] = (sysret < 0 ? -sysret : sysret);
+ env->ir[IR_A3] = (sysret < 0);
+ }
+ break;
+ case 0x86:
+ /* IMB */
+ /* ??? We can probably elide the code using page_unprotect
+ that is checking for self-modifying code. Instead we
+ could simply call tb_flush here. Until we work out the
+ changes required to turn off the extra write protection,
+ this can be a no-op. */
+ break;
+ case 0x9E:
+ /* RDUNIQUE */
+ /* Handled in the translator for usermode. */
+ abort();
+ case 0x9F:
+ /* WRUNIQUE */
+ /* Handled in the translator for usermode. */
+ abort();
+ case 0xAA:
+ /* GENTRAP */
+ info.si_signo = TARGET_SIGFPE;
+ switch (env->ir[IR_A0]) {
+ case TARGET_GEN_INTOVF:
+ info.si_code = TARGET_FPE_INTOVF;
+ break;
+ case TARGET_GEN_INTDIV:
+ info.si_code = TARGET_FPE_INTDIV;
+ break;
+ case TARGET_GEN_FLTOVF:
+ info.si_code = TARGET_FPE_FLTOVF;
+ break;
+ case TARGET_GEN_FLTUND:
+ info.si_code = TARGET_FPE_FLTUND;
+ break;
+ case TARGET_GEN_FLTINV:
+ info.si_code = TARGET_FPE_FLTINV;
+ break;
+ case TARGET_GEN_FLTINE:
+ info.si_code = TARGET_FPE_FLTRES;
+ break;
+ case TARGET_GEN_ROPRAND:
+ info.si_code = 0;
+ break;
+ default:
+ info.si_signo = TARGET_SIGTRAP;
+ info.si_code = 0;
+ break;
+ }
+ info.si_errno = 0;
+ info._sifields._sigfault._addr = env->pc;
+ queue_signal(env, info.si_signo, &info);
+ break;
+ default:
+ goto do_sigill;
+ }
break;
- case EXCP_CALL_PAL ... (EXCP_CALL_PALP - 1):
- fprintf(stderr, "Call to PALcode\n");
- call_pal(env, (trapnr >> 6) | 0x80);
+ case EXCP_DEBUG:
+ info.si_signo = gdb_handlesig (env, TARGET_SIGTRAP);
+ if (info.si_signo) {
+ env->lock_addr = -1;
+ info.si_errno = 0;
+ info.si_code = TARGET_TRAP_BRKPT;
+ queue_signal(env, info.si_signo, &info);
+ }
break;
- case EXCP_CALL_PALP ... (EXCP_CALL_PALE - 1):
- fprintf(stderr, "Privileged call to PALcode\n");
- exit(1);
+ case EXCP_STL_C:
+ case EXCP_STQ_C:
+ do_store_exclusive(env, env->error_code, trapnr - EXCP_STL_C);
+ break;
+ default:
+ printf ("Unhandled trap: 0x%x\n", trapnr);
+ cpu_dump_state(env, stderr, fprintf, 0);
+ exit (1);
+ }
+ process_pending_signals (env);
+ }
+}
+#endif /* TARGET_ALPHA */
+
+#ifdef TARGET_S390X
+void cpu_loop(CPUS390XState *env)
+{
+ int trapnr;
+ target_siginfo_t info;
+
+ while (1) {
+ trapnr = cpu_s390x_exec (env);
+
+ switch (trapnr) {
+ case EXCP_INTERRUPT:
+ /* just indicate that signals should be handled asap */
break;
case EXCP_DEBUG:
{
int sig;
sig = gdb_handlesig (env, TARGET_SIGTRAP);
- if (sig)
- {
+ if (sig) {
info.si_signo = sig;
info.si_errno = 0;
info.si_code = TARGET_TRAP_BRKPT;
- queue_signal(info.si_signo, &info);
- }
+ queue_signal(env, info.si_signo, &info);
+ }
+ }
+ break;
+ case EXCP_SVC:
+ {
+ int n = env->int_svc_code;
+ if (!n) {
+ /* syscalls > 255 */
+ n = env->regs[1];
+ }
+ env->psw.addr += env->int_svc_ilc;
+ env->regs[2] = do_syscall(env, n,
+ env->regs[2],
+ env->regs[3],
+ env->regs[4],
+ env->regs[5],
+ env->regs[6],
+ env->regs[7],
+ 0, 0);
+ }
+ break;
+ case EXCP_ADDR:
+ {
+ info.si_signo = SIGSEGV;
+ info.si_errno = 0;
+ /* XXX: check env->error_code */
+ info.si_code = TARGET_SEGV_MAPERR;
+ info._sifields._sigfault._addr = env->__excp_addr;
+ queue_signal(env, info.si_signo, &info);
+ }
+ break;
+ case EXCP_SPEC:
+ {
+ fprintf(stderr,"specification exception insn 0x%08x%04x\n", ldl(env->psw.addr), lduw(env->psw.addr + 4));
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = TARGET_ILL_ILLOPC;
+ info._sifields._sigfault._addr = env->__excp_addr;
+ queue_signal(env, info.si_signo, &info);
}
break;
default:
process_pending_signals (env);
}
}
-#endif /* TARGET_ALPHA */
-void usage(void)
+#endif /* TARGET_S390X */
+
+static void version(void)
+{
+ printf("qemu-" TARGET_ARCH " version " QEMU_VERSION QEMU_PKGVERSION
+ ", Copyright (c) 2003-2008 Fabrice Bellard\n");
+}
+
+static void usage(void)
{
- printf("qemu-" TARGET_ARCH " version " QEMU_VERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n"
- "usage: qemu-" TARGET_ARCH " [options] program [arguments...]\n"
+ version();
+ printf("usage: qemu-" TARGET_ARCH " [options] program [arguments...]\n"
"Linux CPU emulator (compiled for %s emulation)\n"
"\n"
"Standard options:\n"
"-h print this help\n"
+ "-version display version information and exit\n"
"-g port wait gdb connection to port\n"
"-L path set the elf interpreter prefix (default=%s)\n"
"-s size set the stack size in bytes (default=%ld)\n"
"-cpu model select CPU (-cpu ? for list)\n"
"-drop-ld-preload drop LD_PRELOAD for target process\n"
+ "-E var=value sets/modifies targets environment variable(s)\n"
+ "-U var unsets targets environment variable(s)\n"
+ "-0 argv0 forces target process argv[0] to be argv0\n"
+#if defined(CONFIG_USE_GUEST_BASE)
+ "-B address set guest_base address to address\n"
+ "-R size reserve size bytes for guest virtual address space\n"
+#endif
"\n"
"Debug options:\n"
"-d options activate log (logfile=%s)\n"
"-p pagesize set the host page size to 'pagesize'\n"
+ "-singlestep always run in singlestep mode\n"
"-strace log system calls\n"
"\n"
"Environment variables:\n"
"QEMU_STRACE Print system calls and arguments similar to the\n"
" 'strace' program. Enable by setting to any value.\n"
+ "You can use -E and -U options to set/unset environment variables\n"
+ "for target process. It is possible to provide several variables\n"
+ "by repeating the option. For example:\n"
+ " -E var1=val2 -E var2=val2 -U LD_PRELOAD -U LD_DEBUG\n"
+ "Note that if you provide several changes to single variable\n"
+ "last change will stay in effect.\n"
,
TARGET_ARCH,
interp_prefix,
- x86_stack_size,
+ guest_stack_size,
DEBUG_LOGFILE);
- _exit(1);
+ exit(1);
}
-/* XXX: currently only used for async signals (see signal.c) */
-CPUState *global_env;
+THREAD CPUState *thread_env;
-/* used to free thread contexts */
-TaskState *first_task_state;
+void task_settid(TaskState *ts)
+{
+ if (ts->ts_tid == 0) {
+#ifdef CONFIG_USE_NPTL
+ ts->ts_tid = (pid_t)syscall(SYS_gettid);
+#else
+ /* when no threads are used, tid becomes pid */
+ ts->ts_tid = getpid();
+#endif
+ }
+}
+
+void stop_all_tasks(void)
+{
+ /*
+ * We trust that when using NPTL, start_exclusive()
+ * handles thread stopping correctly.
+ */
+ start_exclusive();
+}
-int main(int argc, char **argv)
+/* Assumes contents are already zeroed. */
+void init_task_state(TaskState *ts)
+{
+ int i;
+
+ ts->used = 1;
+ ts->first_free = ts->sigqueue_table;
+ for (i = 0; i < MAX_SIGQUEUE_SIZE - 1; i++) {
+ ts->sigqueue_table[i].next = &ts->sigqueue_table[i + 1];
+ }
+ ts->sigqueue_table[i].next = NULL;
+}
+
+int main(int argc, char **argv, char **envp)
{
const char *filename;
const char *cpu_model;
+ const char *log_file = DEBUG_LOGFILE;
+ const char *log_mask = NULL;
struct target_pt_regs regs1, *regs = ®s1;
struct image_info info1, *info = &info1;
- TaskState ts1, *ts = &ts1;
+ struct linux_binprm bprm;
+ TaskState *ts;
CPUState *env;
int optind;
const char *r;
int gdbstub_port = 0;
- int drop_ld_preload = 0, environ_count = 0;
- char **target_environ, **wrk, **dst;
+ char **target_environ, **wrk;
+ char **target_argv;
+ int target_argc;
+ envlist_t *envlist = NULL;
+ const char *argv0 = NULL;
+ int i;
+ int ret;
if (argc <= 1)
usage();
- /* init debug */
- cpu_set_log_filename(DEBUG_LOGFILE);
+ qemu_cache_utils_init(envp);
+
+ if ((envlist = envlist_create()) == NULL) {
+ (void) fprintf(stderr, "Unable to allocate envlist\n");
+ exit(1);
+ }
+
+ /* add current environment into the list */
+ for (wrk = environ; *wrk != NULL; wrk++) {
+ (void) envlist_setenv(envlist, *wrk);
+ }
+
+ /* Read the stack limit from the kernel. If it's "unlimited",
+ then we can do little else besides use the default. */
+ {
+ struct rlimit lim;
+ if (getrlimit(RLIMIT_STACK, &lim) == 0
+ && lim.rlim_cur != RLIM_INFINITY
+ && lim.rlim_cur == (target_long)lim.rlim_cur) {
+ guest_stack_size = lim.rlim_cur;
+ }
+ }
cpu_model = NULL;
+#if defined(cpudef_setup)
+ cpudef_setup(); /* parse cpu definitions in target config file (TBD) */
+#endif
+
optind = 1;
for(;;) {
if (optind >= argc)
if (!strcmp(r, "-")) {
break;
} else if (!strcmp(r, "d")) {
- int mask;
- CPULogItem *item;
-
- if (optind >= argc)
+ if (optind >= argc) {
break;
-
- r = argv[optind++];
- mask = cpu_str_to_log_mask(r);
- if (!mask) {
- printf("Log items (comma separated):\n");
- for(item = cpu_log_items; item->mask != 0; item++) {
- printf("%-10s %s\n", item->name, item->help);
- }
+ }
+ log_mask = argv[optind++];
+ } else if (!strcmp(r, "D")) {
+ if (optind >= argc) {
+ break;
+ }
+ log_file = argv[optind++];
+ } else if (!strcmp(r, "E")) {
+ r = argv[optind++];
+ if (envlist_setenv(envlist, r) != 0)
+ usage();
+ } else if (!strcmp(r, "ignore-environment")) {
+ envlist_free(envlist);
+ if ((envlist = envlist_create()) == NULL) {
+ (void) fprintf(stderr, "Unable to allocate envlist\n");
exit(1);
}
- cpu_set_log(mask);
+ } else if (!strcmp(r, "U")) {
+ r = argv[optind++];
+ if (envlist_unsetenv(envlist, r) != 0)
+ usage();
+ } else if (!strcmp(r, "0")) {
+ r = argv[optind++];
+ argv0 = r;
} else if (!strcmp(r, "s")) {
+ if (optind >= argc)
+ break;
r = argv[optind++];
- x86_stack_size = strtol(r, (char **)&r, 0);
- if (x86_stack_size <= 0)
+ guest_stack_size = strtoul(r, (char **)&r, 0);
+ if (guest_stack_size == 0)
usage();
if (*r == 'M')
- x86_stack_size *= 1024 * 1024;
+ guest_stack_size *= 1024 * 1024;
else if (*r == 'k' || *r == 'K')
- x86_stack_size *= 1024;
+ guest_stack_size *= 1024;
} else if (!strcmp(r, "L")) {
interp_prefix = argv[optind++];
} else if (!strcmp(r, "p")) {
+ if (optind >= argc)
+ break;
qemu_host_page_size = atoi(argv[optind++]);
if (qemu_host_page_size == 0 ||
(qemu_host_page_size & (qemu_host_page_size - 1)) != 0) {
exit(1);
}
} else if (!strcmp(r, "g")) {
+ if (optind >= argc)
+ break;
gdbstub_port = atoi(argv[optind++]);
} else if (!strcmp(r, "r")) {
qemu_uname_release = argv[optind++];
} else if (!strcmp(r, "cpu")) {
cpu_model = argv[optind++];
- if (strcmp(cpu_model, "?") == 0) {
+ if (cpu_model == NULL || strcmp(cpu_model, "?") == 0) {
/* XXX: implement xxx_cpu_list for targets that still miss it */
-#if defined(cpu_list)
- cpu_list(stdout, &fprintf);
+#if defined(cpu_list_id)
+ cpu_list_id(stdout, &fprintf, "");
+#elif defined(cpu_list)
+ cpu_list(stdout, &fprintf); /* deprecated */
#endif
- _exit(1);
+ exit(1);
}
+#if defined(CONFIG_USE_GUEST_BASE)
+ } else if (!strcmp(r, "B")) {
+ guest_base = strtol(argv[optind++], NULL, 0);
+ have_guest_base = 1;
+ } else if (!strcmp(r, "R")) {
+ char *p;
+ int shift = 0;
+ reserved_va = strtoul(argv[optind++], &p, 0);
+ switch (*p) {
+ case 'k':
+ case 'K':
+ shift = 10;
+ break;
+ case 'M':
+ shift = 20;
+ break;
+ case 'G':
+ shift = 30;
+ break;
+ }
+ if (shift) {
+ unsigned long unshifted = reserved_va;
+ p++;
+ reserved_va <<= shift;
+ if (((reserved_va >> shift) != unshifted)
+#if HOST_LONG_BITS > TARGET_VIRT_ADDR_SPACE_BITS
+ || (reserved_va > (1ul << TARGET_VIRT_ADDR_SPACE_BITS))
+#endif
+ ) {
+ fprintf(stderr, "Reserved virtual address too big\n");
+ exit(1);
+ }
+ }
+ if (*p) {
+ fprintf(stderr, "Unrecognised -R size suffix '%s'\n", p);
+ exit(1);
+ }
+#endif
} else if (!strcmp(r, "drop-ld-preload")) {
- drop_ld_preload = 1;
+ (void) envlist_unsetenv(envlist, "LD_PRELOAD");
+ } else if (!strcmp(r, "singlestep")) {
+ singlestep = 1;
} else if (!strcmp(r, "strace")) {
do_strace = 1;
- } else
- {
+ } else if (!strcmp(r, "version")) {
+ version();
+ exit(0);
+ } else {
usage();
}
}
- if (optind >= argc)
+ /* init debug */
+ cpu_set_log_filename(log_file);
+ if (log_mask) {
+ int mask;
+ const CPULogItem *item;
+
+ mask = cpu_str_to_log_mask(log_mask);
+ if (!mask) {
+ printf("Log items (comma separated):\n");
+ for (item = cpu_log_items; item->mask != 0; item++) {
+ printf("%-10s %s\n", item->name, item->help);
+ }
+ exit(1);
+ }
+ cpu_set_log(mask);
+ }
+
+ if (optind >= argc) {
usage();
+ }
filename = argv[optind];
+ exec_path = argv[optind];
/* Zero out regs */
memset(regs, 0, sizeof(struct target_pt_regs));
/* Zero out image_info */
memset(info, 0, sizeof(struct image_info));
+ memset(&bprm, 0, sizeof (bprm));
+
/* Scan interp_prefix dir for replacement files. */
init_paths(interp_prefix);
cpu_model = "qemu32";
#endif
#elif defined(TARGET_ARM)
- cpu_model = "arm926";
+ cpu_model = "any";
+#elif defined(TARGET_UNICORE32)
+ cpu_model = "any";
#elif defined(TARGET_M68K)
cpu_model = "any";
#elif defined(TARGET_SPARC)
#endif
#elif defined(TARGET_PPC)
#ifdef TARGET_PPC64
- cpu_model = "970";
+ cpu_model = "970fx";
#else
cpu_model = "750";
#endif
cpu_model = "any";
#endif
}
+ tcg_exec_init(0);
+ cpu_exec_init_all();
/* NOTE: we need to init the CPU at this stage to get
qemu_host_page_size */
env = cpu_init(cpu_model);
fprintf(stderr, "Unable to find CPU definition\n");
exit(1);
}
- global_env = env;
+#if defined(TARGET_I386) || defined(TARGET_SPARC) || defined(TARGET_PPC)
+ cpu_reset(env);
+#endif
+
+ thread_env = env;
if (getenv("QEMU_STRACE")) {
do_strace = 1;
}
- wrk = environ;
- while (*(wrk++))
- environ_count++;
+ target_environ = envlist_to_environ(envlist, NULL);
+ envlist_free(envlist);
- target_environ = malloc((environ_count + 1) * sizeof(char *));
- if (!target_environ)
- abort();
- for (wrk = environ, dst = target_environ; *wrk; wrk++) {
- if (drop_ld_preload && !strncmp(*wrk, "LD_PRELOAD=", 11))
- continue;
- *(dst++) = strdup(*wrk);
+#if defined(CONFIG_USE_GUEST_BASE)
+ /*
+ * Now that page sizes are configured in cpu_init() we can do
+ * proper page alignment for guest_base.
+ */
+ guest_base = HOST_PAGE_ALIGN(guest_base);
+
+ if (reserved_va) {
+ void *p;
+ int flags;
+
+ flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE;
+ if (have_guest_base) {
+ flags |= MAP_FIXED;
+ }
+ p = mmap((void *)guest_base, reserved_va, PROT_NONE, flags, -1, 0);
+ if (p == MAP_FAILED) {
+ fprintf(stderr, "Unable to reserve guest address space\n");
+ exit(1);
+ }
+ guest_base = (unsigned long)p;
+ /* Make sure the address is properly aligned. */
+ if (guest_base & ~qemu_host_page_mask) {
+ munmap(p, reserved_va);
+ p = mmap((void *)guest_base, reserved_va + qemu_host_page_size,
+ PROT_NONE, flags, -1, 0);
+ if (p == MAP_FAILED) {
+ fprintf(stderr, "Unable to reserve guest address space\n");
+ exit(1);
+ }
+ guest_base = HOST_PAGE_ALIGN((unsigned long)p);
+ }
+ qemu_log("Reserved 0x%lx bytes of guest address space\n", reserved_va);
+ }
+
+ if (reserved_va || have_guest_base) {
+ if (!guest_validate_base(guest_base)) {
+ fprintf(stderr, "Guest base/Reserved VA rejected by guest code\n");
+ exit(1);
+ }
+ }
+#endif /* CONFIG_USE_GUEST_BASE */
+
+ /*
+ * Read in mmap_min_addr kernel parameter. This value is used
+ * When loading the ELF image to determine whether guest_base
+ * is needed. It is also used in mmap_find_vma.
+ */
+ {
+ FILE *fp;
+
+ if ((fp = fopen("/proc/sys/vm/mmap_min_addr", "r")) != NULL) {
+ unsigned long tmp;
+ if (fscanf(fp, "%lu", &tmp) == 1) {
+ mmap_min_addr = tmp;
+ qemu_log("host mmap_min_addr=0x%lx\n", mmap_min_addr);
+ }
+ fclose(fp);
+ }
+ }
+
+ /*
+ * Prepare copy of argv vector for target.
+ */
+ target_argc = argc - optind;
+ target_argv = calloc(target_argc + 1, sizeof (char *));
+ if (target_argv == NULL) {
+ (void) fprintf(stderr, "Unable to allocate memory for target_argv\n");
+ exit(1);
}
- *dst = NULL; /* NULL terminate target_environ */
- if (loader_exec(filename, argv+optind, target_environ, regs, info) != 0) {
- printf("Error loading %s\n", filename);
+ /*
+ * If argv0 is specified (using '-0' switch) we replace
+ * argv[0] pointer with the given one.
+ */
+ i = 0;
+ if (argv0 != NULL) {
+ target_argv[i++] = strdup(argv0);
+ }
+ for (; i < target_argc; i++) {
+ target_argv[i] = strdup(argv[optind + i]);
+ }
+ target_argv[target_argc] = NULL;
+
+ ts = g_malloc0 (sizeof(TaskState));
+ init_task_state(ts);
+ /* build Task State */
+ ts->info = info;
+ ts->bprm = &bprm;
+ env->opaque = ts;
+ task_settid(ts);
+
+ ret = loader_exec(filename, target_argv, target_environ, regs,
+ info, &bprm);
+ if (ret != 0) {
+ printf("Error %d while loading %s\n", ret, filename);
_exit(1);
}
+ for (i = 0; i < target_argc; i++) {
+ free(target_argv[i]);
+ }
+ free(target_argv);
+
for (wrk = target_environ; *wrk; wrk++) {
free(*wrk);
}
free(target_environ);
- if (loglevel) {
- page_dump(logfile);
-
- fprintf(logfile, "start_brk 0x" TARGET_ABI_FMT_lx "\n", info->start_brk);
- fprintf(logfile, "end_code 0x" TARGET_ABI_FMT_lx "\n", info->end_code);
- fprintf(logfile, "start_code 0x" TARGET_ABI_FMT_lx "\n",
- info->start_code);
- fprintf(logfile, "start_data 0x" TARGET_ABI_FMT_lx "\n",
- info->start_data);
- fprintf(logfile, "end_data 0x" TARGET_ABI_FMT_lx "\n", info->end_data);
- fprintf(logfile, "start_stack 0x" TARGET_ABI_FMT_lx "\n",
- info->start_stack);
- fprintf(logfile, "brk 0x" TARGET_ABI_FMT_lx "\n", info->brk);
- fprintf(logfile, "entry 0x" TARGET_ABI_FMT_lx "\n", info->entry);
+ if (qemu_log_enabled()) {
+#if defined(CONFIG_USE_GUEST_BASE)
+ qemu_log("guest_base 0x%lx\n", guest_base);
+#endif
+ log_page_dump();
+
+ qemu_log("start_brk 0x" TARGET_ABI_FMT_lx "\n", info->start_brk);
+ qemu_log("end_code 0x" TARGET_ABI_FMT_lx "\n", info->end_code);
+ qemu_log("start_code 0x" TARGET_ABI_FMT_lx "\n",
+ info->start_code);
+ qemu_log("start_data 0x" TARGET_ABI_FMT_lx "\n",
+ info->start_data);
+ qemu_log("end_data 0x" TARGET_ABI_FMT_lx "\n", info->end_data);
+ qemu_log("start_stack 0x" TARGET_ABI_FMT_lx "\n",
+ info->start_stack);
+ qemu_log("brk 0x" TARGET_ABI_FMT_lx "\n", info->brk);
+ qemu_log("entry 0x" TARGET_ABI_FMT_lx "\n", info->entry);
}
target_set_brk(info->brk);
syscall_init();
signal_init();
- /* build Task State */
- memset(ts, 0, sizeof(TaskState));
- env->opaque = ts;
- ts->used = 1;
- ts->info = info;
- env->user_mode_only = 1;
+#if defined(CONFIG_USE_GUEST_BASE)
+ /* Now that we've loaded the binary, GUEST_BASE is fixed. Delay
+ generating the prologue until now so that the prologue can take
+ the real value of GUEST_BASE into account. */
+ tcg_prologue_init(&tcg_ctx);
+#endif
#if defined(TARGET_I386)
cpu_x86_set_cpl(env, 3);
#endif
/* linux interrupt setup */
- env->idt.base = h2g(idt_table);
- env->idt.limit = sizeof(idt_table) - 1;
+#ifndef TARGET_ABI32
+ env->idt.limit = 511;
+#else
+ env->idt.limit = 255;
+#endif
+ env->idt.base = target_mmap(0, sizeof(uint64_t) * (env->idt.limit + 1),
+ PROT_READ|PROT_WRITE,
+ MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
+ idt_table = g2h(env->idt.base);
set_idt(0, 0);
set_idt(1, 0);
set_idt(2, 0);
set_idt(3, 3);
set_idt(4, 3);
- set_idt(5, 3);
+ set_idt(5, 0);
set_idt(6, 0);
set_idt(7, 0);
set_idt(8, 0);
/* linux segment setup */
{
uint64_t *gdt_table;
- gdt_table = qemu_mallocz(sizeof(uint64_t) * TARGET_GDT_ENTRIES);
- env->gdt.base = h2g((unsigned long)gdt_table);
+ env->gdt.base = target_mmap(0, sizeof(uint64_t) * TARGET_GDT_ENTRIES,
+ PROT_READ|PROT_WRITE,
+ MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
env->gdt.limit = sizeof(uint64_t) * TARGET_GDT_ENTRIES - 1;
+ gdt_table = g2h(env->gdt.base);
#ifdef TARGET_ABI32
write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff,
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
env->regs[i] = regs->uregs[i];
}
}
+#elif defined(TARGET_UNICORE32)
+ {
+ int i;
+ cpu_asr_write(env, regs->uregs[32], 0xffffffff);
+ for (i = 0; i < 32; i++) {
+ env->regs[i] = regs->uregs[i];
+ }
+ }
#elif defined(TARGET_SPARC)
{
int i;
env->sr = regs->sr;
ts->sim_syscalls = 1;
}
+#elif defined(TARGET_MICROBLAZE)
+ {
+ env->regs[0] = regs->r0;
+ env->regs[1] = regs->r1;
+ env->regs[2] = regs->r2;
+ env->regs[3] = regs->r3;
+ env->regs[4] = regs->r4;
+ env->regs[5] = regs->r5;
+ env->regs[6] = regs->r6;
+ env->regs[7] = regs->r7;
+ env->regs[8] = regs->r8;
+ env->regs[9] = regs->r9;
+ env->regs[10] = regs->r10;
+ env->regs[11] = regs->r11;
+ env->regs[12] = regs->r12;
+ env->regs[13] = regs->r13;
+ env->regs[14] = regs->r14;
+ env->regs[15] = regs->r15;
+ env->regs[16] = regs->r16;
+ env->regs[17] = regs->r17;
+ env->regs[18] = regs->r18;
+ env->regs[19] = regs->r19;
+ env->regs[20] = regs->r20;
+ env->regs[21] = regs->r21;
+ env->regs[22] = regs->r22;
+ env->regs[23] = regs->r23;
+ env->regs[24] = regs->r24;
+ env->regs[25] = regs->r25;
+ env->regs[26] = regs->r26;
+ env->regs[27] = regs->r27;
+ env->regs[28] = regs->r28;
+ env->regs[29] = regs->r29;
+ env->regs[30] = regs->r30;
+ env->regs[31] = regs->r31;
+ env->sregs[SR_PC] = regs->pc;
+ }
#elif defined(TARGET_MIPS)
{
int i;
for(i = 0; i < 32; i++) {
- env->gpr[env->current_tc][i] = regs->regs[i];
+ env->active_tc.gpr[i] = regs->regs[i];
+ }
+ env->active_tc.PC = regs->cp0_epc & ~(target_ulong)1;
+ if (regs->cp0_epc & 1) {
+ env->hflags |= MIPS_HFLAG_M16;
}
- env->PC[env->current_tc] = regs->cp0_epc;
}
#elif defined(TARGET_SH4)
{
for(i = 0; i < 28; i++) {
env->ir[i] = ((abi_ulong *)regs)[i];
}
- env->ipr[IPR_USP] = regs->usp;
- env->ir[30] = regs->usp;
+ env->ir[IR_SP] = regs->usp;
env->pc = regs->pc;
- env->unique = regs->unique;
}
#elif defined(TARGET_CRIS)
{
env->regs[15] = regs->acr;
env->pc = regs->erp;
}
+#elif defined(TARGET_S390X)
+ {
+ int i;
+ for (i = 0; i < 16; i++) {
+ env->regs[i] = regs->gprs[i];
+ }
+ env->psw.mask = regs->psw.mask;
+ env->psw.addr = regs->psw.addr;
+ }
#else
#error unsupported target CPU
#endif
-#if defined(TARGET_ARM) || defined(TARGET_M68K)
+#if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_UNICORE32)
ts->stack_base = info->start_stack;
ts->heap_base = info->brk;
/* This will be filled in on the first SYS_HEAPINFO call. */
#endif
if (gdbstub_port) {
- gdbserver_start (gdbstub_port);
+ if (gdbserver_start(gdbstub_port) < 0) {
+ fprintf(stderr, "qemu: could not open gdbserver on port %d\n",
+ gdbstub_port);
+ exit(1);
+ }
gdb_handlesig(env, 0);
}
cpu_loop(env);