xref: /qemu/linux-user/main.c (revision 2be0071f)
131e31b8aSbellard /*
293ac68bcSbellard  *  qemu user main
331e31b8aSbellard  *
431e31b8aSbellard  *  Copyright (c) 2003 Fabrice Bellard
531e31b8aSbellard  *
631e31b8aSbellard  *  This program is free software; you can redistribute it and/or modify
731e31b8aSbellard  *  it under the terms of the GNU General Public License as published by
831e31b8aSbellard  *  the Free Software Foundation; either version 2 of the License, or
931e31b8aSbellard  *  (at your option) any later version.
1031e31b8aSbellard  *
1131e31b8aSbellard  *  This program is distributed in the hope that it will be useful,
1231e31b8aSbellard  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
1331e31b8aSbellard  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1431e31b8aSbellard  *  GNU General Public License for more details.
1531e31b8aSbellard  *
1631e31b8aSbellard  *  You should have received a copy of the GNU General Public License
1731e31b8aSbellard  *  along with this program; if not, write to the Free Software
1831e31b8aSbellard  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
1931e31b8aSbellard  */
2031e31b8aSbellard #include <stdlib.h>
2131e31b8aSbellard #include <stdio.h>
2231e31b8aSbellard #include <stdarg.h>
2304369ff2Sbellard #include <string.h>
2431e31b8aSbellard #include <errno.h>
250ecfa993Sbellard #include <unistd.h>
2631e31b8aSbellard 
273ef693a0Sbellard #include "qemu.h"
2831e31b8aSbellard 
293ef693a0Sbellard #define DEBUG_LOGFILE "/tmp/qemu.log"
30586314f2Sbellard 
3183fb7adfSbellard #ifdef __APPLE__
3283fb7adfSbellard #include <crt_externs.h>
3383fb7adfSbellard # define environ  (*_NSGetEnviron())
3483fb7adfSbellard #endif
3583fb7adfSbellard 
3674cd30b8Sbellard static const char *interp_prefix = CONFIG_QEMU_PREFIX;
37586314f2Sbellard 
383a4739d6Sbellard #if defined(__i386__) && !defined(CONFIG_STATIC)
39f801f97eSbellard /* Force usage of an ELF interpreter even if it is an ELF shared
40f801f97eSbellard    object ! */
41f801f97eSbellard const char interp[] __attribute__((section(".interp"))) = "/lib/ld-linux.so.2";
424304763bSbellard #endif
4374cd30b8Sbellard 
4493ac68bcSbellard /* for recent libc, we add these dummy symbols which are not declared
4574cd30b8Sbellard    when generating a linked object (bug in ld ?) */
46fbf59244Sbellard #if (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)) && !defined(CONFIG_STATIC)
47c6981055Sbellard long __preinit_array_start[0];
48c6981055Sbellard long __preinit_array_end[0];
4974cd30b8Sbellard long __init_array_start[0];
5074cd30b8Sbellard long __init_array_end[0];
5174cd30b8Sbellard long __fini_array_start[0];
5274cd30b8Sbellard long __fini_array_end[0];
5374cd30b8Sbellard #endif
5474cd30b8Sbellard 
559de5e440Sbellard /* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
569de5e440Sbellard    we allocate a bigger stack. Need a better solution, for example
579de5e440Sbellard    by remapping the process stack directly at the right place */
589de5e440Sbellard unsigned long x86_stack_size = 512 * 1024;
5931e31b8aSbellard 
6031e31b8aSbellard void gemu_log(const char *fmt, ...)
6131e31b8aSbellard {
6231e31b8aSbellard     va_list ap;
6331e31b8aSbellard 
6431e31b8aSbellard     va_start(ap, fmt);
6531e31b8aSbellard     vfprintf(stderr, fmt, ap);
6631e31b8aSbellard     va_end(ap);
6731e31b8aSbellard }
6831e31b8aSbellard 
6961190b14Sbellard void cpu_outb(CPUState *env, int addr, int val)
70367e86e8Sbellard {
71367e86e8Sbellard     fprintf(stderr, "outb: port=0x%04x, data=%02x\n", addr, val);
72367e86e8Sbellard }
73367e86e8Sbellard 
7461190b14Sbellard void cpu_outw(CPUState *env, int addr, int val)
75367e86e8Sbellard {
76367e86e8Sbellard     fprintf(stderr, "outw: port=0x%04x, data=%04x\n", addr, val);
77367e86e8Sbellard }
78367e86e8Sbellard 
7961190b14Sbellard void cpu_outl(CPUState *env, int addr, int val)
80367e86e8Sbellard {
81367e86e8Sbellard     fprintf(stderr, "outl: port=0x%04x, data=%08x\n", addr, val);
82367e86e8Sbellard }
83367e86e8Sbellard 
8461190b14Sbellard int cpu_inb(CPUState *env, int addr)
85367e86e8Sbellard {
86367e86e8Sbellard     fprintf(stderr, "inb: port=0x%04x\n", addr);
87367e86e8Sbellard     return 0;
88367e86e8Sbellard }
89367e86e8Sbellard 
9061190b14Sbellard int cpu_inw(CPUState *env, int addr)
91367e86e8Sbellard {
92367e86e8Sbellard     fprintf(stderr, "inw: port=0x%04x\n", addr);
93367e86e8Sbellard     return 0;
94367e86e8Sbellard }
95367e86e8Sbellard 
9661190b14Sbellard int cpu_inl(CPUState *env, int addr)
97367e86e8Sbellard {
98367e86e8Sbellard     fprintf(stderr, "inl: port=0x%04x\n", addr);
99367e86e8Sbellard     return 0;
100367e86e8Sbellard }
101367e86e8Sbellard 
102a541f297Sbellard int cpu_get_pic_interrupt(CPUState *env)
10392ccca6aSbellard {
10492ccca6aSbellard     return -1;
10592ccca6aSbellard }
10692ccca6aSbellard 
10728ab0e2eSbellard /* timers for rdtsc */
10828ab0e2eSbellard 
10928ab0e2eSbellard #if defined(__i386__)
11028ab0e2eSbellard 
11128ab0e2eSbellard int64_t cpu_get_real_ticks(void)
11228ab0e2eSbellard {
11328ab0e2eSbellard     int64_t val;
11428ab0e2eSbellard     asm volatile ("rdtsc" : "=A" (val));
11528ab0e2eSbellard     return val;
11628ab0e2eSbellard }
11728ab0e2eSbellard 
11828ab0e2eSbellard #elif defined(__x86_64__)
11928ab0e2eSbellard 
12028ab0e2eSbellard int64_t cpu_get_real_ticks(void)
12128ab0e2eSbellard {
12228ab0e2eSbellard     uint32_t low,high;
12328ab0e2eSbellard     int64_t val;
12428ab0e2eSbellard     asm volatile("rdtsc" : "=a" (low), "=d" (high));
12528ab0e2eSbellard     val = high;
12628ab0e2eSbellard     val <<= 32;
12728ab0e2eSbellard     val |= low;
12828ab0e2eSbellard     return val;
12928ab0e2eSbellard }
13028ab0e2eSbellard 
13128ab0e2eSbellard #else
13228ab0e2eSbellard 
13328ab0e2eSbellard static uint64_t emu_time;
13428ab0e2eSbellard 
13528ab0e2eSbellard int64_t cpu_get_real_ticks(void)
13628ab0e2eSbellard {
13728ab0e2eSbellard     return emu_time++;
13828ab0e2eSbellard }
13928ab0e2eSbellard 
14028ab0e2eSbellard #endif
14128ab0e2eSbellard 
142a541f297Sbellard #ifdef TARGET_I386
143a541f297Sbellard /***********************************************************/
144a541f297Sbellard /* CPUX86 core interface */
145a541f297Sbellard 
14628ab0e2eSbellard uint64_t cpu_get_tsc(CPUX86State *env)
14728ab0e2eSbellard {
14828ab0e2eSbellard     return cpu_get_real_ticks();
14928ab0e2eSbellard }
15028ab0e2eSbellard 
151f4beb510Sbellard static void write_dt(void *ptr, unsigned long addr, unsigned long limit,
152f4beb510Sbellard                      int flags)
1536dbad63eSbellard {
154f4beb510Sbellard     unsigned int e1, e2;
1556dbad63eSbellard     e1 = (addr << 16) | (limit & 0xffff);
1566dbad63eSbellard     e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000);
157f4beb510Sbellard     e2 |= flags;
158f4beb510Sbellard     stl((uint8_t *)ptr, e1);
159f4beb510Sbellard     stl((uint8_t *)ptr + 4, e2);
160f4beb510Sbellard }
161f4beb510Sbellard 
162f4beb510Sbellard static void set_gate(void *ptr, unsigned int type, unsigned int dpl,
163f4beb510Sbellard                      unsigned long addr, unsigned int sel)
164f4beb510Sbellard {
165f4beb510Sbellard     unsigned int e1, e2;
166f4beb510Sbellard     e1 = (addr & 0xffff) | (sel << 16);
167f4beb510Sbellard     e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8);
1686dbad63eSbellard     stl((uint8_t *)ptr, e1);
1696dbad63eSbellard     stl((uint8_t *)ptr + 4, e2);
1706dbad63eSbellard }
1716dbad63eSbellard 
1726dbad63eSbellard uint64_t gdt_table[6];
173f4beb510Sbellard uint64_t idt_table[256];
174f4beb510Sbellard 
175f4beb510Sbellard /* only dpl matters as we do only user space emulation */
176f4beb510Sbellard static void set_idt(int n, unsigned int dpl)
177f4beb510Sbellard {
178f4beb510Sbellard     set_gate(idt_table + n, 0, dpl, 0, 0);
179f4beb510Sbellard }
18031e31b8aSbellard 
18189e957e7Sbellard void cpu_loop(CPUX86State *env)
182bc8a22ccSbellard {
183bc8a22ccSbellard     int trapnr;
18480a9d035Sbellard     target_ulong pc;
185bc8a22ccSbellard     target_siginfo_t info;
186bc8a22ccSbellard 
187bc8a22ccSbellard     for(;;) {
188bc8a22ccSbellard         trapnr = cpu_x86_exec(env);
189bc8a22ccSbellard         switch(trapnr) {
190f4beb510Sbellard         case 0x80:
191f4beb510Sbellard             /* linux syscall */
1921b6b029eSbellard             env->regs[R_EAX] = do_syscall(env,
1931b6b029eSbellard                                           env->regs[R_EAX],
1941b6b029eSbellard                                           env->regs[R_EBX],
1951b6b029eSbellard                                           env->regs[R_ECX],
1961b6b029eSbellard                                           env->regs[R_EDX],
1971b6b029eSbellard                                           env->regs[R_ESI],
1981b6b029eSbellard                                           env->regs[R_EDI],
1991b6b029eSbellard                                           env->regs[R_EBP]);
200f4beb510Sbellard             break;
201f4beb510Sbellard         case EXCP0B_NOSEG:
202f4beb510Sbellard         case EXCP0C_STACK:
203f4beb510Sbellard             info.si_signo = SIGBUS;
204f4beb510Sbellard             info.si_errno = 0;
205f4beb510Sbellard             info.si_code = TARGET_SI_KERNEL;
206f4beb510Sbellard             info._sifields._sigfault._addr = 0;
207f4beb510Sbellard             queue_signal(info.si_signo, &info);
208f4beb510Sbellard             break;
209f4beb510Sbellard         case EXCP0D_GPF:
210f4beb510Sbellard             if (env->eflags & VM_MASK) {
211f4beb510Sbellard                 handle_vm86_fault(env);
2121b6b029eSbellard             } else {
2139de5e440Sbellard                 info.si_signo = SIGSEGV;
2149de5e440Sbellard                 info.si_errno = 0;
215b689bc57Sbellard                 info.si_code = TARGET_SI_KERNEL;
2169de5e440Sbellard                 info._sifields._sigfault._addr = 0;
2179de5e440Sbellard                 queue_signal(info.si_signo, &info);
2181b6b029eSbellard             }
2191b6b029eSbellard             break;
220b689bc57Sbellard         case EXCP0E_PAGE:
221b689bc57Sbellard             info.si_signo = SIGSEGV;
222b689bc57Sbellard             info.si_errno = 0;
223b689bc57Sbellard             if (!(env->error_code & 1))
224b689bc57Sbellard                 info.si_code = TARGET_SEGV_MAPERR;
225b689bc57Sbellard             else
226b689bc57Sbellard                 info.si_code = TARGET_SEGV_ACCERR;
227970a87a6Sbellard             info._sifields._sigfault._addr = env->cr[2];
228b689bc57Sbellard             queue_signal(info.si_signo, &info);
229b689bc57Sbellard             break;
2309de5e440Sbellard         case EXCP00_DIVZ:
231bc8a22ccSbellard             if (env->eflags & VM_MASK) {
232447db213Sbellard                 handle_vm86_trap(env, trapnr);
233bc8a22ccSbellard             } else {
2349de5e440Sbellard                 /* division by zero */
2359de5e440Sbellard                 info.si_signo = SIGFPE;
2369de5e440Sbellard                 info.si_errno = 0;
2379de5e440Sbellard                 info.si_code = TARGET_FPE_INTDIV;
2389de5e440Sbellard                 info._sifields._sigfault._addr = env->eip;
2399de5e440Sbellard                 queue_signal(info.si_signo, &info);
240bc8a22ccSbellard             }
2419de5e440Sbellard             break;
242447db213Sbellard         case EXCP01_SSTP:
243447db213Sbellard         case EXCP03_INT3:
244447db213Sbellard             if (env->eflags & VM_MASK) {
245447db213Sbellard                 handle_vm86_trap(env, trapnr);
246447db213Sbellard             } else {
247447db213Sbellard                 info.si_signo = SIGTRAP;
248447db213Sbellard                 info.si_errno = 0;
249447db213Sbellard                 if (trapnr == EXCP01_SSTP) {
250447db213Sbellard                     info.si_code = TARGET_TRAP_BRKPT;
251447db213Sbellard                     info._sifields._sigfault._addr = env->eip;
252447db213Sbellard                 } else {
253447db213Sbellard                     info.si_code = TARGET_SI_KERNEL;
254447db213Sbellard                     info._sifields._sigfault._addr = 0;
255447db213Sbellard                 }
256447db213Sbellard                 queue_signal(info.si_signo, &info);
257447db213Sbellard             }
258447db213Sbellard             break;
2599de5e440Sbellard         case EXCP04_INTO:
2609de5e440Sbellard         case EXCP05_BOUND:
261bc8a22ccSbellard             if (env->eflags & VM_MASK) {
262447db213Sbellard                 handle_vm86_trap(env, trapnr);
263bc8a22ccSbellard             } else {
2649de5e440Sbellard                 info.si_signo = SIGSEGV;
2659de5e440Sbellard                 info.si_errno = 0;
266b689bc57Sbellard                 info.si_code = TARGET_SI_KERNEL;
2679de5e440Sbellard                 info._sifields._sigfault._addr = 0;
2689de5e440Sbellard                 queue_signal(info.si_signo, &info);
269bc8a22ccSbellard             }
2709de5e440Sbellard             break;
2719de5e440Sbellard         case EXCP06_ILLOP:
2729de5e440Sbellard             info.si_signo = SIGILL;
2739de5e440Sbellard             info.si_errno = 0;
2749de5e440Sbellard             info.si_code = TARGET_ILL_ILLOPN;
2759de5e440Sbellard             info._sifields._sigfault._addr = env->eip;
2769de5e440Sbellard             queue_signal(info.si_signo, &info);
2779de5e440Sbellard             break;
2789de5e440Sbellard         case EXCP_INTERRUPT:
2799de5e440Sbellard             /* just indicate that signals should be handled asap */
2809de5e440Sbellard             break;
2811fddef4bSbellard         case EXCP_DEBUG:
2821fddef4bSbellard             {
2831fddef4bSbellard                 int sig;
2841fddef4bSbellard 
2851fddef4bSbellard                 sig = gdb_handlesig (env, TARGET_SIGTRAP);
2861fddef4bSbellard                 if (sig)
2871fddef4bSbellard                   {
2881fddef4bSbellard                     info.si_signo = sig;
2891fddef4bSbellard                     info.si_errno = 0;
2901fddef4bSbellard                     info.si_code = TARGET_TRAP_BRKPT;
2911fddef4bSbellard                     queue_signal(info.si_signo, &info);
2921fddef4bSbellard                   }
2931fddef4bSbellard             }
2941fddef4bSbellard             break;
2951b6b029eSbellard         default:
296970a87a6Sbellard             pc = env->segs[R_CS].base + env->eip;
297bc8a22ccSbellard             fprintf(stderr, "qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n",
298bc8a22ccSbellard                     (long)pc, trapnr);
2991b6b029eSbellard             abort();
3001b6b029eSbellard         }
30166fb9763Sbellard         process_pending_signals(env);
3021b6b029eSbellard     }
3031b6b029eSbellard }
304b346ff46Sbellard #endif
305b346ff46Sbellard 
306b346ff46Sbellard #ifdef TARGET_ARM
307b346ff46Sbellard 
3086f1f31c0Sbellard /* XXX: find a better solution */
3096f1f31c0Sbellard extern void tb_invalidate_page_range(target_ulong start, target_ulong end);
3106f1f31c0Sbellard 
3116f1f31c0Sbellard static void arm_cache_flush(target_ulong start, target_ulong last)
3126f1f31c0Sbellard {
3136f1f31c0Sbellard     target_ulong addr, last1;
3146f1f31c0Sbellard 
3156f1f31c0Sbellard     if (last < start)
3166f1f31c0Sbellard         return;
3176f1f31c0Sbellard     addr = start;
3186f1f31c0Sbellard     for(;;) {
3196f1f31c0Sbellard         last1 = ((addr + TARGET_PAGE_SIZE) & TARGET_PAGE_MASK) - 1;
3206f1f31c0Sbellard         if (last1 > last)
3216f1f31c0Sbellard             last1 = last;
3226f1f31c0Sbellard         tb_invalidate_page_range(addr, last1 + 1);
3236f1f31c0Sbellard         if (last1 == last)
3246f1f31c0Sbellard             break;
3256f1f31c0Sbellard         addr = last1 + 1;
3266f1f31c0Sbellard     }
3276f1f31c0Sbellard }
3286f1f31c0Sbellard 
329b346ff46Sbellard void cpu_loop(CPUARMState *env)
330b346ff46Sbellard {
331b346ff46Sbellard     int trapnr;
332b346ff46Sbellard     unsigned int n, insn;
333b346ff46Sbellard     target_siginfo_t info;
334b346ff46Sbellard 
335b346ff46Sbellard     for(;;) {
336b346ff46Sbellard         trapnr = cpu_arm_exec(env);
337b346ff46Sbellard         switch(trapnr) {
338b346ff46Sbellard         case EXCP_UDEF:
339c6981055Sbellard             {
340c6981055Sbellard                 TaskState *ts = env->opaque;
341c6981055Sbellard                 uint32_t opcode;
342c6981055Sbellard 
343c6981055Sbellard                 /* we handle the FPU emulation here, as Linux */
344c6981055Sbellard                 /* we get the opcode */
345c6981055Sbellard                 opcode = ldl_raw((uint8_t *)env->regs[15]);
346c6981055Sbellard 
347c6981055Sbellard                 if (EmulateAll(opcode, &ts->fpa, env->regs) == 0) {
348b346ff46Sbellard                     info.si_signo = SIGILL;
349b346ff46Sbellard                     info.si_errno = 0;
350b346ff46Sbellard                     info.si_code = TARGET_ILL_ILLOPN;
351b346ff46Sbellard                     info._sifields._sigfault._addr = env->regs[15];
352b346ff46Sbellard                     queue_signal(info.si_signo, &info);
353c6981055Sbellard                 } else {
354c6981055Sbellard                     /* increment PC */
355c6981055Sbellard                     env->regs[15] += 4;
356c6981055Sbellard                 }
357c6981055Sbellard             }
358b346ff46Sbellard             break;
359b346ff46Sbellard         case EXCP_SWI:
360b346ff46Sbellard             {
361b346ff46Sbellard                 /* system call */
362192c7bd9Sbellard                 if (env->thumb) {
363192c7bd9Sbellard                     insn = lduw((void *)(env->regs[15] - 2));
364192c7bd9Sbellard                     n = insn & 0xff;
365192c7bd9Sbellard                 } else {
366b346ff46Sbellard                     insn = ldl((void *)(env->regs[15] - 4));
367b346ff46Sbellard                     n = insn & 0xffffff;
368192c7bd9Sbellard                 }
369192c7bd9Sbellard 
3706f1f31c0Sbellard                 if (n == ARM_NR_cacheflush) {
3716f1f31c0Sbellard                     arm_cache_flush(env->regs[0], env->regs[1]);
372a4f81979Sbellard                 } else if (n == ARM_NR_semihosting
373a4f81979Sbellard                            || n == ARM_NR_thumb_semihosting) {
374a4f81979Sbellard                     env->regs[0] = do_arm_semihosting (env);
375192c7bd9Sbellard                 } else if (n >= ARM_SYSCALL_BASE
376192c7bd9Sbellard                            || (env->thumb && n == ARM_THUMB_SYSCALL)) {
377b346ff46Sbellard                     /* linux syscall */
378192c7bd9Sbellard                     if (env->thumb) {
379192c7bd9Sbellard                         n = env->regs[7];
380192c7bd9Sbellard                     } else {
381b346ff46Sbellard                         n -= ARM_SYSCALL_BASE;
382192c7bd9Sbellard                     }
383b346ff46Sbellard                     env->regs[0] = do_syscall(env,
384b346ff46Sbellard                                               n,
385b346ff46Sbellard                                               env->regs[0],
386b346ff46Sbellard                                               env->regs[1],
387b346ff46Sbellard                                               env->regs[2],
388b346ff46Sbellard                                               env->regs[3],
389b346ff46Sbellard                                               env->regs[4],
390e1a2849cSbellard                                               env->regs[5]);
391b346ff46Sbellard                 } else {
392b346ff46Sbellard                     goto error;
393b346ff46Sbellard                 }
394b346ff46Sbellard             }
395b346ff46Sbellard             break;
39643fff238Sbellard         case EXCP_INTERRUPT:
39743fff238Sbellard             /* just indicate that signals should be handled asap */
39843fff238Sbellard             break;
39968016c62Sbellard         case EXCP_PREFETCH_ABORT:
40068016c62Sbellard         case EXCP_DATA_ABORT:
40168016c62Sbellard             {
40268016c62Sbellard                 info.si_signo = SIGSEGV;
40368016c62Sbellard                 info.si_errno = 0;
40468016c62Sbellard                 /* XXX: check env->error_code */
40568016c62Sbellard                 info.si_code = TARGET_SEGV_MAPERR;
40668016c62Sbellard                 info._sifields._sigfault._addr = env->cp15_6;
40768016c62Sbellard                 queue_signal(info.si_signo, &info);
40868016c62Sbellard             }
40968016c62Sbellard             break;
4101fddef4bSbellard         case EXCP_DEBUG:
4111fddef4bSbellard             {
4121fddef4bSbellard                 int sig;
4131fddef4bSbellard 
4141fddef4bSbellard                 sig = gdb_handlesig (env, TARGET_SIGTRAP);
4151fddef4bSbellard                 if (sig)
4161fddef4bSbellard                   {
4171fddef4bSbellard                     info.si_signo = sig;
4181fddef4bSbellard                     info.si_errno = 0;
4191fddef4bSbellard                     info.si_code = TARGET_TRAP_BRKPT;
4201fddef4bSbellard                     queue_signal(info.si_signo, &info);
4211fddef4bSbellard                   }
4221fddef4bSbellard             }
4231fddef4bSbellard             break;
424b346ff46Sbellard         default:
425b346ff46Sbellard         error:
426b346ff46Sbellard             fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n",
427b346ff46Sbellard                     trapnr);
4287fe48483Sbellard             cpu_dump_state(env, stderr, fprintf, 0);
429b346ff46Sbellard             abort();
430b346ff46Sbellard         }
431b346ff46Sbellard         process_pending_signals(env);
432b346ff46Sbellard     }
433b346ff46Sbellard }
434b346ff46Sbellard 
435b346ff46Sbellard #endif
4361b6b029eSbellard 
43793ac68bcSbellard #ifdef TARGET_SPARC
43893ac68bcSbellard 
439060366c5Sbellard //#define DEBUG_WIN
440060366c5Sbellard 
4412623cbafSbellard /* WARNING: dealing with register windows _is_ complicated. More info
4422623cbafSbellard    can be found at http://www.sics.se/~psm/sparcstack.html */
443060366c5Sbellard static inline int get_reg_index(CPUSPARCState *env, int cwp, int index)
444060366c5Sbellard {
445060366c5Sbellard     index = (index + cwp * 16) & (16 * NWINDOWS - 1);
446060366c5Sbellard     /* wrap handling : if cwp is on the last window, then we use the
447060366c5Sbellard        registers 'after' the end */
448060366c5Sbellard     if (index < 8 && env->cwp == (NWINDOWS - 1))
449060366c5Sbellard         index += (16 * NWINDOWS);
450060366c5Sbellard     return index;
451060366c5Sbellard }
452060366c5Sbellard 
4532623cbafSbellard /* save the register window 'cwp1' */
4542623cbafSbellard static inline void save_window_offset(CPUSPARCState *env, int cwp1)
455060366c5Sbellard {
4562623cbafSbellard     unsigned int i;
457060366c5Sbellard     uint32_t *sp_ptr;
458060366c5Sbellard 
459060366c5Sbellard     sp_ptr = (uint32_t *)(env->regbase[get_reg_index(env, cwp1, 6)]);
460060366c5Sbellard #if defined(DEBUG_WIN)
461060366c5Sbellard     printf("win_overflow: sp_ptr=0x%x save_cwp=%d\n",
462060366c5Sbellard            (int)sp_ptr, cwp1);
463060366c5Sbellard #endif
4642623cbafSbellard     for(i = 0; i < 16; i++) {
4652623cbafSbellard         put_user(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr);
4662623cbafSbellard         sp_ptr++;
4672623cbafSbellard     }
468060366c5Sbellard }
469060366c5Sbellard 
470060366c5Sbellard static void save_window(CPUSPARCState *env)
471060366c5Sbellard {
4722623cbafSbellard     unsigned int new_wim;
4732623cbafSbellard     new_wim = ((env->wim >> 1) | (env->wim << (NWINDOWS - 1))) &
4742623cbafSbellard         ((1LL << NWINDOWS) - 1);
4752623cbafSbellard     save_window_offset(env, (env->cwp - 2) & (NWINDOWS - 1));
4762623cbafSbellard     env->wim = new_wim;
477060366c5Sbellard }
478060366c5Sbellard 
479060366c5Sbellard static void restore_window(CPUSPARCState *env)
480060366c5Sbellard {
481060366c5Sbellard     unsigned int new_wim, i, cwp1;
4822623cbafSbellard     uint32_t *sp_ptr, reg;
483060366c5Sbellard 
484060366c5Sbellard     new_wim = ((env->wim << 1) | (env->wim >> (NWINDOWS - 1))) &
485060366c5Sbellard         ((1LL << NWINDOWS) - 1);
486060366c5Sbellard 
487060366c5Sbellard     /* restore the invalid window */
488060366c5Sbellard     cwp1 = (env->cwp + 1) & (NWINDOWS - 1);
489060366c5Sbellard     sp_ptr = (uint32_t *)(env->regbase[get_reg_index(env, cwp1, 6)]);
490060366c5Sbellard #if defined(DEBUG_WIN)
491060366c5Sbellard     printf("win_underflow: sp_ptr=0x%x load_cwp=%d\n",
492060366c5Sbellard            (int)sp_ptr, cwp1);
493060366c5Sbellard #endif
4942623cbafSbellard     for(i = 0; i < 16; i++) {
4952623cbafSbellard         get_user(reg, sp_ptr);
4962623cbafSbellard         env->regbase[get_reg_index(env, cwp1, 8 + i)] = reg;
4972623cbafSbellard         sp_ptr++;
4982623cbafSbellard     }
499060366c5Sbellard     env->wim = new_wim;
500060366c5Sbellard }
501060366c5Sbellard 
502060366c5Sbellard static void flush_windows(CPUSPARCState *env)
503060366c5Sbellard {
504060366c5Sbellard     int offset, cwp1;
5052623cbafSbellard 
5062623cbafSbellard     offset = 1;
507060366c5Sbellard     for(;;) {
508060366c5Sbellard         /* if restore would invoke restore_window(), then we can stop */
5092623cbafSbellard         cwp1 = (env->cwp + offset) & (NWINDOWS - 1);
510060366c5Sbellard         if (env->wim & (1 << cwp1))
511060366c5Sbellard             break;
5122623cbafSbellard         save_window_offset(env, cwp1);
513060366c5Sbellard         offset++;
514060366c5Sbellard     }
5152623cbafSbellard     /* set wim so that restore will reload the registers */
5162623cbafSbellard     cwp1 = (env->cwp + 1) & (NWINDOWS - 1);
5172623cbafSbellard     env->wim = 1 << cwp1;
5182623cbafSbellard #if defined(DEBUG_WIN)
5192623cbafSbellard     printf("flush_windows: nb=%d\n", offset - 1);
52080a9d035Sbellard #endif
5212623cbafSbellard }
522060366c5Sbellard 
52393ac68bcSbellard void cpu_loop (CPUSPARCState *env)
52493ac68bcSbellard {
525060366c5Sbellard     int trapnr, ret;
52661ff6f58Sbellard     target_siginfo_t info;
52793ac68bcSbellard 
52893ac68bcSbellard     while (1) {
52993ac68bcSbellard         trapnr = cpu_sparc_exec (env);
53093ac68bcSbellard 
53193ac68bcSbellard         switch (trapnr) {
532060366c5Sbellard         case 0x88:
533060366c5Sbellard         case 0x90:
534060366c5Sbellard             ret = do_syscall (env, env->gregs[1],
535060366c5Sbellard                               env->regwptr[0], env->regwptr[1],
536060366c5Sbellard                               env->regwptr[2], env->regwptr[3],
537060366c5Sbellard                               env->regwptr[4], env->regwptr[5]);
538060366c5Sbellard             if ((unsigned int)ret >= (unsigned int)(-515)) {
53993ac68bcSbellard                 env->psr |= PSR_CARRY;
540060366c5Sbellard                 ret = -ret;
541060366c5Sbellard             } else {
542060366c5Sbellard                 env->psr &= ~PSR_CARRY;
543060366c5Sbellard             }
544060366c5Sbellard             env->regwptr[0] = ret;
545060366c5Sbellard             /* next instruction */
546060366c5Sbellard             env->pc = env->npc;
547060366c5Sbellard             env->npc = env->npc + 4;
548060366c5Sbellard             break;
549060366c5Sbellard         case 0x83: /* flush windows */
5502623cbafSbellard             flush_windows(env);
551060366c5Sbellard             /* next instruction */
552060366c5Sbellard             env->pc = env->npc;
553060366c5Sbellard             env->npc = env->npc + 4;
554060366c5Sbellard             break;
5553475187dSbellard #ifndef TARGET_SPARC64
556060366c5Sbellard         case TT_WIN_OVF: /* window overflow */
557060366c5Sbellard             save_window(env);
558060366c5Sbellard             break;
559060366c5Sbellard         case TT_WIN_UNF: /* window underflow */
560060366c5Sbellard             restore_window(env);
56193ac68bcSbellard             break;
56261ff6f58Sbellard         case TT_TFAULT:
56361ff6f58Sbellard         case TT_DFAULT:
56461ff6f58Sbellard             {
56561ff6f58Sbellard                 info.si_signo = SIGSEGV;
56661ff6f58Sbellard                 info.si_errno = 0;
56761ff6f58Sbellard                 /* XXX: check env->error_code */
56861ff6f58Sbellard                 info.si_code = TARGET_SEGV_MAPERR;
56961ff6f58Sbellard                 info._sifields._sigfault._addr = env->mmuregs[4];
57061ff6f58Sbellard                 queue_signal(info.si_signo, &info);
57161ff6f58Sbellard             }
57261ff6f58Sbellard             break;
5733475187dSbellard #else
5743475187dSbellard 	    // XXX
5753475187dSbellard #endif
576e80cfcfcSbellard 	case 0x100: // XXX, why do we get these?
577e80cfcfcSbellard 	    break;
5781fddef4bSbellard         case EXCP_DEBUG:
5791fddef4bSbellard             {
5801fddef4bSbellard                 int sig;
5811fddef4bSbellard 
5821fddef4bSbellard                 sig = gdb_handlesig (env, TARGET_SIGTRAP);
5831fddef4bSbellard                 if (sig)
5841fddef4bSbellard                   {
5851fddef4bSbellard                     info.si_signo = sig;
5861fddef4bSbellard                     info.si_errno = 0;
5871fddef4bSbellard                     info.si_code = TARGET_TRAP_BRKPT;
5881fddef4bSbellard                     queue_signal(info.si_signo, &info);
5891fddef4bSbellard                   }
5901fddef4bSbellard             }
5911fddef4bSbellard             break;
59293ac68bcSbellard         default:
593060366c5Sbellard             printf ("Unhandled trap: 0x%x\n", trapnr);
5947fe48483Sbellard             cpu_dump_state(env, stderr, fprintf, 0);
59593ac68bcSbellard             exit (1);
59693ac68bcSbellard         }
59793ac68bcSbellard         process_pending_signals (env);
59893ac68bcSbellard     }
59993ac68bcSbellard }
60093ac68bcSbellard 
60193ac68bcSbellard #endif
60293ac68bcSbellard 
60367867308Sbellard #ifdef TARGET_PPC
6049fddaa0cSbellard 
6059fddaa0cSbellard static inline uint64_t cpu_ppc_get_tb (CPUState *env)
6069fddaa0cSbellard {
6079fddaa0cSbellard     /* TO FIX */
6089fddaa0cSbellard     return 0;
6099fddaa0cSbellard }
6109fddaa0cSbellard 
6119fddaa0cSbellard uint32_t cpu_ppc_load_tbl (CPUState *env)
6129fddaa0cSbellard {
6139fddaa0cSbellard     return cpu_ppc_get_tb(env) & 0xFFFFFFFF;
6149fddaa0cSbellard }
6159fddaa0cSbellard 
6169fddaa0cSbellard uint32_t cpu_ppc_load_tbu (CPUState *env)
6179fddaa0cSbellard {
6189fddaa0cSbellard     return cpu_ppc_get_tb(env) >> 32;
6199fddaa0cSbellard }
6209fddaa0cSbellard 
6219fddaa0cSbellard static void cpu_ppc_store_tb (CPUState *env, uint64_t value)
6229fddaa0cSbellard {
6239fddaa0cSbellard     /* TO FIX */
6249fddaa0cSbellard }
6259fddaa0cSbellard 
6269fddaa0cSbellard void cpu_ppc_store_tbu (CPUState *env, uint32_t value)
6279fddaa0cSbellard {
6289fddaa0cSbellard     cpu_ppc_store_tb(env, ((uint64_t)value << 32) | cpu_ppc_load_tbl(env));
6299fddaa0cSbellard }
6309fddaa0cSbellard 
6319fddaa0cSbellard void cpu_ppc_store_tbl (CPUState *env, uint32_t value)
6329fddaa0cSbellard {
6339fddaa0cSbellard     cpu_ppc_store_tb(env, ((uint64_t)cpu_ppc_load_tbl(env) << 32) | value);
6349fddaa0cSbellard }
6359fddaa0cSbellard 
6369fddaa0cSbellard uint32_t cpu_ppc_load_decr (CPUState *env)
6379fddaa0cSbellard {
6389fddaa0cSbellard     /* TO FIX */
6399fddaa0cSbellard     return -1;
6409fddaa0cSbellard }
6419fddaa0cSbellard 
6429fddaa0cSbellard void cpu_ppc_store_decr (CPUState *env, uint32_t value)
6439fddaa0cSbellard {
6449fddaa0cSbellard     /* TO FIX */
6459fddaa0cSbellard }
6469fddaa0cSbellard 
64767867308Sbellard void cpu_loop(CPUPPCState *env)
64867867308Sbellard {
64967867308Sbellard     target_siginfo_t info;
65061190b14Sbellard     int trapnr;
65161190b14Sbellard     uint32_t ret;
65267867308Sbellard 
65367867308Sbellard     for(;;) {
65467867308Sbellard         trapnr = cpu_ppc_exec(env);
65561190b14Sbellard         if (trapnr != EXCP_SYSCALL_USER && trapnr != EXCP_BRANCH &&
65661190b14Sbellard             trapnr != EXCP_TRACE) {
65761190b14Sbellard             if (loglevel > 0) {
6587fe48483Sbellard                 cpu_dump_state(env, logfile, fprintf, 0);
65961190b14Sbellard             }
66061190b14Sbellard         }
66167867308Sbellard         switch(trapnr) {
66267867308Sbellard         case EXCP_NONE:
66367867308Sbellard             break;
66461190b14Sbellard         case EXCP_SYSCALL_USER:
66567867308Sbellard             /* system call */
66667867308Sbellard             /* WARNING:
66767867308Sbellard              * PPC ABI uses overflow flag in cr0 to signal an error
66867867308Sbellard              * in syscalls.
66967867308Sbellard              */
67061190b14Sbellard #if 0
67161190b14Sbellard             printf("syscall %d 0x%08x 0x%08x 0x%08x 0x%08x\n", env->gpr[0],
67261190b14Sbellard                    env->gpr[3], env->gpr[4], env->gpr[5], env->gpr[6]);
67361190b14Sbellard #endif
67467867308Sbellard             env->crf[0] &= ~0x1;
67567867308Sbellard             ret = do_syscall(env, env->gpr[0], env->gpr[3], env->gpr[4],
67667867308Sbellard                              env->gpr[5], env->gpr[6], env->gpr[7],
67767867308Sbellard                              env->gpr[8]);
67867867308Sbellard             if (ret > (uint32_t)(-515)) {
67967867308Sbellard                 env->crf[0] |= 0x1;
68067867308Sbellard                 ret = -ret;
68167867308Sbellard             }
68267867308Sbellard             env->gpr[3] = ret;
68361190b14Sbellard #if 0
68461190b14Sbellard             printf("syscall returned 0x%08x (%d)\n", ret, ret);
68561190b14Sbellard #endif
68661190b14Sbellard             break;
68761190b14Sbellard         case EXCP_RESET:
68861190b14Sbellard             /* Should not happen ! */
68961190b14Sbellard             fprintf(stderr, "RESET asked... Stop emulation\n");
69061190b14Sbellard             if (loglevel)
69161190b14Sbellard                 fprintf(logfile, "RESET asked... Stop emulation\n");
69261190b14Sbellard             abort();
69361190b14Sbellard         case EXCP_MACHINE_CHECK:
69461190b14Sbellard             fprintf(stderr, "Machine check exeption...  Stop emulation\n");
69561190b14Sbellard             if (loglevel)
69661190b14Sbellard                 fprintf(logfile, "RESET asked... Stop emulation\n");
69761190b14Sbellard             info.si_signo = TARGET_SIGBUS;
69861190b14Sbellard             info.si_errno = 0;
69961190b14Sbellard             info.si_code = TARGET_BUS_OBJERR;
70061190b14Sbellard             info._sifields._sigfault._addr = env->nip - 4;
70161190b14Sbellard             queue_signal(info.si_signo, &info);
70261190b14Sbellard         case EXCP_DSI:
7033fc6c082Sbellard             fprintf(stderr, "Invalid data memory access: 0x%08x\n",
7043fc6c082Sbellard                     env->spr[SPR_DAR]);
70561190b14Sbellard             if (loglevel) {
70661190b14Sbellard                 fprintf(logfile, "Invalid data memory access: 0x%08x\n",
7073fc6c082Sbellard                         env->spr[SPR_DAR]);
70861190b14Sbellard             }
709*2be0071fSbellard             switch (env->error_code & 0xFF000000) {
710*2be0071fSbellard             case 0x40000000:
71161190b14Sbellard                 info.si_signo = TARGET_SIGSEGV;
71261190b14Sbellard                 info.si_errno = 0;
71361190b14Sbellard                 info.si_code = TARGET_SEGV_MAPERR;
71461190b14Sbellard                 break;
715*2be0071fSbellard             case 0x04000000:
71661190b14Sbellard                 info.si_signo = TARGET_SIGILL;
71761190b14Sbellard                 info.si_errno = 0;
71861190b14Sbellard                 info.si_code = TARGET_ILL_ILLADR;
71961190b14Sbellard                 break;
720*2be0071fSbellard             case 0x08000000:
72161190b14Sbellard                 info.si_signo = TARGET_SIGSEGV;
72261190b14Sbellard                 info.si_errno = 0;
72361190b14Sbellard                 info.si_code = TARGET_SEGV_ACCERR;
72461190b14Sbellard                 break;
72561190b14Sbellard             default:
72661190b14Sbellard                 /* Let's send a regular segfault... */
72761190b14Sbellard                 fprintf(stderr, "Invalid segfault errno (%02x)\n",
72861190b14Sbellard                         env->error_code);
72961190b14Sbellard                 if (loglevel) {
73061190b14Sbellard                     fprintf(logfile, "Invalid segfault errno (%02x)\n",
73161190b14Sbellard                             env->error_code);
73261190b14Sbellard                 }
73361190b14Sbellard                 info.si_signo = TARGET_SIGSEGV;
73461190b14Sbellard                 info.si_errno = 0;
73561190b14Sbellard                 info.si_code = TARGET_SEGV_MAPERR;
73667867308Sbellard                 break;
73767867308Sbellard             }
73861190b14Sbellard             info._sifields._sigfault._addr = env->nip;
73961190b14Sbellard             queue_signal(info.si_signo, &info);
74061190b14Sbellard             break;
74161190b14Sbellard         case EXCP_ISI:
74261190b14Sbellard             fprintf(stderr, "Invalid instruction fetch\n");
74361190b14Sbellard             if (loglevel)
74461190b14Sbellard                 fprintf(logfile, "Invalid instruction fetch\n");
745*2be0071fSbellard             switch (env->error_code & 0xFF000000) {
746*2be0071fSbellard             case 0x40000000:
74761190b14Sbellard                 info.si_signo = TARGET_SIGSEGV;
74861190b14Sbellard             info.si_errno = 0;
74961190b14Sbellard                 info.si_code = TARGET_SEGV_MAPERR;
75061190b14Sbellard                 break;
751*2be0071fSbellard             case 0x10000000:
752*2be0071fSbellard             case 0x08000000:
75361190b14Sbellard                 info.si_signo = TARGET_SIGSEGV;
75461190b14Sbellard                 info.si_errno = 0;
75561190b14Sbellard                 info.si_code = TARGET_SEGV_ACCERR;
75661190b14Sbellard                 break;
75767867308Sbellard             default:
75861190b14Sbellard                 /* Let's send a regular segfault... */
75961190b14Sbellard                 fprintf(stderr, "Invalid segfault errno (%02x)\n",
76061190b14Sbellard                         env->error_code);
76161190b14Sbellard                 if (loglevel) {
76261190b14Sbellard                     fprintf(logfile, "Invalid segfault errno (%02x)\n",
76361190b14Sbellard                             env->error_code);
76461190b14Sbellard                 }
76561190b14Sbellard                 info.si_signo = TARGET_SIGSEGV;
76661190b14Sbellard                 info.si_errno = 0;
76761190b14Sbellard                 info.si_code = TARGET_SEGV_MAPERR;
76861190b14Sbellard                 break;
76961190b14Sbellard             }
77061190b14Sbellard             info._sifields._sigfault._addr = env->nip - 4;
77161190b14Sbellard             queue_signal(info.si_signo, &info);
77261190b14Sbellard             break;
77361190b14Sbellard         case EXCP_EXTERNAL:
77461190b14Sbellard             /* Should not happen ! */
77561190b14Sbellard             fprintf(stderr, "External interruption... Stop emulation\n");
77661190b14Sbellard             if (loglevel)
77761190b14Sbellard                 fprintf(logfile, "External interruption... Stop emulation\n");
77861190b14Sbellard             abort();
77961190b14Sbellard         case EXCP_ALIGN:
78061190b14Sbellard             fprintf(stderr, "Invalid unaligned memory access\n");
78161190b14Sbellard             if (loglevel)
78261190b14Sbellard                 fprintf(logfile, "Invalid unaligned memory access\n");
78361190b14Sbellard             info.si_signo = TARGET_SIGBUS;
78461190b14Sbellard             info.si_errno = 0;
78561190b14Sbellard             info.si_code = TARGET_BUS_ADRALN;
78661190b14Sbellard             info._sifields._sigfault._addr = env->nip - 4;
78761190b14Sbellard             queue_signal(info.si_signo, &info);
78861190b14Sbellard             break;
78961190b14Sbellard         case EXCP_PROGRAM:
79061190b14Sbellard             switch (env->error_code & ~0xF) {
79161190b14Sbellard             case EXCP_FP:
79261190b14Sbellard             fprintf(stderr, "Program exception\n");
79361190b14Sbellard                 if (loglevel)
79461190b14Sbellard                     fprintf(logfile, "Program exception\n");
79561190b14Sbellard                 /* Set FX */
79661190b14Sbellard                 env->fpscr[7] |= 0x8;
79761190b14Sbellard                 /* Finally, update FEX */
79861190b14Sbellard                 if ((((env->fpscr[7] & 0x3) << 3) | (env->fpscr[6] >> 1)) &
79961190b14Sbellard                     ((env->fpscr[1] << 1) | (env->fpscr[0] >> 3)))
80061190b14Sbellard                     env->fpscr[7] |= 0x4;
80161190b14Sbellard                 info.si_signo = TARGET_SIGFPE;
80261190b14Sbellard                 info.si_errno = 0;
80361190b14Sbellard                 switch (env->error_code & 0xF) {
80461190b14Sbellard                 case EXCP_FP_OX:
80561190b14Sbellard                     info.si_code = TARGET_FPE_FLTOVF;
80661190b14Sbellard                     break;
80761190b14Sbellard                 case EXCP_FP_UX:
80861190b14Sbellard                     info.si_code = TARGET_FPE_FLTUND;
80961190b14Sbellard                     break;
81061190b14Sbellard                 case EXCP_FP_ZX:
81161190b14Sbellard                 case EXCP_FP_VXZDZ:
81261190b14Sbellard                     info.si_code = TARGET_FPE_FLTDIV;
81361190b14Sbellard                     break;
81461190b14Sbellard                 case EXCP_FP_XX:
81561190b14Sbellard                     info.si_code = TARGET_FPE_FLTRES;
81661190b14Sbellard                     break;
81761190b14Sbellard                 case EXCP_FP_VXSOFT:
81861190b14Sbellard                     info.si_code = TARGET_FPE_FLTINV;
81961190b14Sbellard                     break;
82061190b14Sbellard                 case EXCP_FP_VXNAN:
82161190b14Sbellard                 case EXCP_FP_VXISI:
82261190b14Sbellard                 case EXCP_FP_VXIDI:
82361190b14Sbellard                 case EXCP_FP_VXIMZ:
82461190b14Sbellard                 case EXCP_FP_VXVC:
82561190b14Sbellard                 case EXCP_FP_VXSQRT:
82661190b14Sbellard                 case EXCP_FP_VXCVI:
82761190b14Sbellard                     info.si_code = TARGET_FPE_FLTSUB;
82861190b14Sbellard                     break;
82961190b14Sbellard                 default:
83061190b14Sbellard                     fprintf(stderr, "Unknown floating point exception "
83161190b14Sbellard                             "(%02x)\n", env->error_code);
83261190b14Sbellard                     if (loglevel) {
83361190b14Sbellard                         fprintf(logfile, "Unknown floating point exception "
83461190b14Sbellard                                 "(%02x)\n", env->error_code & 0xF);
83561190b14Sbellard                     }
83661190b14Sbellard                 }
83761190b14Sbellard             break;
83861190b14Sbellard         case EXCP_INVAL:
83961190b14Sbellard                 fprintf(stderr, "Invalid instruction\n");
84061190b14Sbellard                 if (loglevel)
84161190b14Sbellard                     fprintf(logfile, "Invalid instruction\n");
84261190b14Sbellard                 info.si_signo = TARGET_SIGILL;
84361190b14Sbellard                 info.si_errno = 0;
84461190b14Sbellard                 switch (env->error_code & 0xF) {
84561190b14Sbellard                 case EXCP_INVAL_INVAL:
84661190b14Sbellard                     info.si_code = TARGET_ILL_ILLOPC;
84761190b14Sbellard                     break;
84861190b14Sbellard                 case EXCP_INVAL_LSWX:
84961190b14Sbellard             info.si_code = TARGET_ILL_ILLOPN;
85061190b14Sbellard                     break;
85161190b14Sbellard                 case EXCP_INVAL_SPR:
85261190b14Sbellard                     info.si_code = TARGET_ILL_PRVREG;
85361190b14Sbellard                     break;
85461190b14Sbellard                 case EXCP_INVAL_FP:
85561190b14Sbellard                     info.si_code = TARGET_ILL_COPROC;
85661190b14Sbellard                     break;
85761190b14Sbellard                 default:
85861190b14Sbellard                     fprintf(stderr, "Unknown invalid operation (%02x)\n",
85961190b14Sbellard                             env->error_code & 0xF);
86061190b14Sbellard                     if (loglevel) {
86161190b14Sbellard                         fprintf(logfile, "Unknown invalid operation (%02x)\n",
86261190b14Sbellard                                 env->error_code & 0xF);
86361190b14Sbellard                     }
86461190b14Sbellard                     info.si_code = TARGET_ILL_ILLADR;
86561190b14Sbellard                     break;
86661190b14Sbellard                 }
86761190b14Sbellard                 break;
86861190b14Sbellard             case EXCP_PRIV:
86961190b14Sbellard                 fprintf(stderr, "Privilege violation\n");
87061190b14Sbellard                 if (loglevel)
87161190b14Sbellard                     fprintf(logfile, "Privilege violation\n");
87261190b14Sbellard                 info.si_signo = TARGET_SIGILL;
87361190b14Sbellard                 info.si_errno = 0;
87461190b14Sbellard                 switch (env->error_code & 0xF) {
87561190b14Sbellard                 case EXCP_PRIV_OPC:
87661190b14Sbellard                     info.si_code = TARGET_ILL_PRVOPC;
87761190b14Sbellard                     break;
87861190b14Sbellard                 case EXCP_PRIV_REG:
87961190b14Sbellard                     info.si_code = TARGET_ILL_PRVREG;
88061190b14Sbellard                 break;
88161190b14Sbellard                 default:
88261190b14Sbellard                     fprintf(stderr, "Unknown privilege violation (%02x)\n",
88361190b14Sbellard                             env->error_code & 0xF);
88461190b14Sbellard                     info.si_code = TARGET_ILL_PRVOPC;
88561190b14Sbellard                     break;
88661190b14Sbellard                 }
88761190b14Sbellard                 break;
88861190b14Sbellard             case EXCP_TRAP:
88961190b14Sbellard                 fprintf(stderr, "Tried to call a TRAP\n");
89061190b14Sbellard                 if (loglevel)
89161190b14Sbellard                     fprintf(logfile, "Tried to call a TRAP\n");
89261190b14Sbellard                 abort();
89361190b14Sbellard             default:
89461190b14Sbellard                 /* Should not happen ! */
89561190b14Sbellard                 fprintf(stderr, "Unknown program exception (%02x)\n",
89661190b14Sbellard                         env->error_code);
89761190b14Sbellard                 if (loglevel) {
89861190b14Sbellard                     fprintf(logfile, "Unknwon program exception (%02x)\n",
89961190b14Sbellard                             env->error_code);
90061190b14Sbellard                 }
90167867308Sbellard                 abort();
90267867308Sbellard             }
90361190b14Sbellard             info._sifields._sigfault._addr = env->nip - 4;
90461190b14Sbellard             queue_signal(info.si_signo, &info);
90561190b14Sbellard             break;
90661190b14Sbellard         case EXCP_NO_FP:
90761190b14Sbellard             fprintf(stderr, "No floating point allowed\n");
90861190b14Sbellard             if (loglevel)
90961190b14Sbellard                 fprintf(logfile, "No floating point allowed\n");
91061190b14Sbellard             info.si_signo = TARGET_SIGILL;
91161190b14Sbellard             info.si_errno = 0;
91261190b14Sbellard             info.si_code = TARGET_ILL_COPROC;
91361190b14Sbellard             info._sifields._sigfault._addr = env->nip - 4;
91461190b14Sbellard             queue_signal(info.si_signo, &info);
91561190b14Sbellard             break;
91661190b14Sbellard         case EXCP_DECR:
91761190b14Sbellard             /* Should not happen ! */
91861190b14Sbellard             fprintf(stderr, "Decrementer exception\n");
91961190b14Sbellard             if (loglevel)
92061190b14Sbellard                 fprintf(logfile, "Decrementer exception\n");
92161190b14Sbellard             abort();
92261190b14Sbellard         case EXCP_TRACE:
92361190b14Sbellard             /* Do nothing: we use this to trace execution */
92461190b14Sbellard             break;
92561190b14Sbellard         case EXCP_FP_ASSIST:
92661190b14Sbellard             /* Should not happen ! */
92761190b14Sbellard             fprintf(stderr, "Floating point assist exception\n");
92861190b14Sbellard             if (loglevel)
92961190b14Sbellard                 fprintf(logfile, "Floating point assist exception\n");
93061190b14Sbellard             abort();
93161190b14Sbellard         case EXCP_MTMSR:
93261190b14Sbellard             /* We reloaded the msr, just go on */
9339fddaa0cSbellard             if (msr_pr == 0) {
93461190b14Sbellard                 fprintf(stderr, "Tried to go into supervisor mode !\n");
93561190b14Sbellard                 if (loglevel)
93661190b14Sbellard                     fprintf(logfile, "Tried to go into supervisor mode !\n");
93761190b14Sbellard                 abort();
93861190b14Sbellard         }
93961190b14Sbellard             break;
94061190b14Sbellard         case EXCP_BRANCH:
94161190b14Sbellard             /* We stopped because of a jump... */
94261190b14Sbellard             break;
94361190b14Sbellard         case EXCP_INTERRUPT:
94461190b14Sbellard             /* Don't know why this should ever happen... */
94561190b14Sbellard             break;
946a541f297Sbellard         case EXCP_DEBUG:
9471fddef4bSbellard             {
9481fddef4bSbellard                 int sig;
9491fddef4bSbellard 
9501fddef4bSbellard                 sig = gdb_handlesig (env, TARGET_SIGTRAP);
9511fddef4bSbellard                 if (sig)
9521fddef4bSbellard                   {
9531fddef4bSbellard                     info.si_signo = sig;
9541fddef4bSbellard                     info.si_errno = 0;
9551fddef4bSbellard                     info.si_code = TARGET_TRAP_BRKPT;
9561fddef4bSbellard                     queue_signal(info.si_signo, &info);
9571fddef4bSbellard                   }
9581fddef4bSbellard             }
959a541f297Sbellard             break;
96061190b14Sbellard         default:
96161190b14Sbellard             fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n",
96261190b14Sbellard                     trapnr);
96361190b14Sbellard             if (loglevel) {
96461190b14Sbellard                 fprintf(logfile, "qemu: unhandled CPU exception 0x%02x - "
96561190b14Sbellard                         "0x%02x - aborting\n", trapnr, env->error_code);
96661190b14Sbellard             }
96761190b14Sbellard             abort();
96861190b14Sbellard         }
96967867308Sbellard         process_pending_signals(env);
97067867308Sbellard     }
97167867308Sbellard }
97267867308Sbellard #endif
97367867308Sbellard 
97431e31b8aSbellard void usage(void)
97531e31b8aSbellard {
9764606bb3fSbellard     printf("qemu-" TARGET_ARCH " version " QEMU_VERSION ", Copyright (c) 2003-2004 Fabrice Bellard\n"
9771fddef4bSbellard            "usage: qemu-" TARGET_ARCH " [-h] [-g] [-d opts] [-L path] [-s size] program [arguments...]\n"
978b346ff46Sbellard            "Linux CPU emulator (compiled for %s emulation)\n"
979d691f669Sbellard            "\n"
980d691f669Sbellard            "-h           print this help\n"
9811fddef4bSbellard            "-g           wait gdb connection to port %d\n"
982b346ff46Sbellard            "-L path      set the elf interpreter prefix (default=%s)\n"
983b346ff46Sbellard            "-s size      set the stack size in bytes (default=%ld)\n"
98454936004Sbellard            "\n"
98554936004Sbellard            "debug options:\n"
986c6981055Sbellard #ifdef USE_CODE_COPY
987c6981055Sbellard            "-no-code-copy   disable code copy acceleration\n"
988c6981055Sbellard #endif
9896f1f31c0Sbellard            "-d options   activate log (logfile=%s)\n"
99054936004Sbellard            "-p pagesize  set the host page size to 'pagesize'\n",
991b346ff46Sbellard            TARGET_ARCH,
9921fddef4bSbellard            DEFAULT_GDBSTUB_PORT,
993d691f669Sbellard            interp_prefix,
99454936004Sbellard            x86_stack_size,
99554936004Sbellard            DEBUG_LOGFILE);
99674cd30b8Sbellard     _exit(1);
99731e31b8aSbellard }
99831e31b8aSbellard 
9999de5e440Sbellard /* XXX: currently only used for async signals (see signal.c) */
1000b346ff46Sbellard CPUState *global_env;
100159faf6d6Sbellard /* used only if single thread */
100259faf6d6Sbellard CPUState *cpu_single_env = NULL;
100359faf6d6Sbellard 
1004851e67a1Sbellard /* used to free thread contexts */
1005851e67a1Sbellard TaskState *first_task_state;
10069de5e440Sbellard 
100731e31b8aSbellard int main(int argc, char **argv)
100831e31b8aSbellard {
100931e31b8aSbellard     const char *filename;
101001ffc75bSbellard     struct target_pt_regs regs1, *regs = &regs1;
101131e31b8aSbellard     struct image_info info1, *info = &info1;
1012851e67a1Sbellard     TaskState ts1, *ts = &ts1;
1013b346ff46Sbellard     CPUState *env;
1014586314f2Sbellard     int optind;
1015d691f669Sbellard     const char *r;
10161fddef4bSbellard     int use_gdbstub = 0;
101731e31b8aSbellard 
101831e31b8aSbellard     if (argc <= 1)
101931e31b8aSbellard         usage();
1020f801f97eSbellard 
1021cc38b844Sbellard     /* init debug */
1022cc38b844Sbellard     cpu_set_log_filename(DEBUG_LOGFILE);
1023cc38b844Sbellard 
1024586314f2Sbellard     optind = 1;
1025d691f669Sbellard     for(;;) {
1026d691f669Sbellard         if (optind >= argc)
1027d691f669Sbellard             break;
1028d691f669Sbellard         r = argv[optind];
1029d691f669Sbellard         if (r[0] != '-')
1030d691f669Sbellard             break;
1031586314f2Sbellard         optind++;
1032d691f669Sbellard         r++;
1033d691f669Sbellard         if (!strcmp(r, "-")) {
1034d691f669Sbellard             break;
1035d691f669Sbellard         } else if (!strcmp(r, "d")) {
1036e19e89a5Sbellard             int mask;
1037e19e89a5Sbellard             CPULogItem *item;
1038e19e89a5Sbellard 
10396f1f31c0Sbellard 	    if (optind >= argc)
10406f1f31c0Sbellard 		break;
10416f1f31c0Sbellard 
10426f1f31c0Sbellard 	    r = argv[optind++];
10436f1f31c0Sbellard             mask = cpu_str_to_log_mask(r);
1044e19e89a5Sbellard             if (!mask) {
1045e19e89a5Sbellard                 printf("Log items (comma separated):\n");
1046e19e89a5Sbellard                 for(item = cpu_log_items; item->mask != 0; item++) {
1047e19e89a5Sbellard                     printf("%-10s %s\n", item->name, item->help);
1048e19e89a5Sbellard                 }
1049e19e89a5Sbellard                 exit(1);
1050e19e89a5Sbellard             }
1051e19e89a5Sbellard             cpu_set_log(mask);
1052d691f669Sbellard         } else if (!strcmp(r, "s")) {
1053d691f669Sbellard             r = argv[optind++];
1054d691f669Sbellard             x86_stack_size = strtol(r, (char **)&r, 0);
1055d691f669Sbellard             if (x86_stack_size <= 0)
1056d691f669Sbellard                 usage();
1057d691f669Sbellard             if (*r == 'M')
1058d691f669Sbellard                 x86_stack_size *= 1024 * 1024;
1059d691f669Sbellard             else if (*r == 'k' || *r == 'K')
1060d691f669Sbellard                 x86_stack_size *= 1024;
1061d691f669Sbellard         } else if (!strcmp(r, "L")) {
1062d691f669Sbellard             interp_prefix = argv[optind++];
106354936004Sbellard         } else if (!strcmp(r, "p")) {
106483fb7adfSbellard             qemu_host_page_size = atoi(argv[optind++]);
106583fb7adfSbellard             if (qemu_host_page_size == 0 ||
106683fb7adfSbellard                 (qemu_host_page_size & (qemu_host_page_size - 1)) != 0) {
106754936004Sbellard                 fprintf(stderr, "page size must be a power of two\n");
106854936004Sbellard                 exit(1);
106954936004Sbellard             }
10701fddef4bSbellard         } else if (!strcmp(r, "g")) {
10711fddef4bSbellard             use_gdbstub = 1;
1072c6981055Sbellard         } else
1073c6981055Sbellard #ifdef USE_CODE_COPY
1074c6981055Sbellard         if (!strcmp(r, "no-code-copy")) {
1075c6981055Sbellard             code_copy_enabled = 0;
1076c6981055Sbellard         } else
1077c6981055Sbellard #endif
1078c6981055Sbellard         {
1079d691f669Sbellard             usage();
1080586314f2Sbellard         }
1081d691f669Sbellard     }
1082d691f669Sbellard     if (optind >= argc)
1083d691f669Sbellard         usage();
1084586314f2Sbellard     filename = argv[optind];
108531e31b8aSbellard 
108631e31b8aSbellard     /* Zero out regs */
108701ffc75bSbellard     memset(regs, 0, sizeof(struct target_pt_regs));
108831e31b8aSbellard 
108931e31b8aSbellard     /* Zero out image_info */
109031e31b8aSbellard     memset(info, 0, sizeof(struct image_info));
109131e31b8aSbellard 
109274cd30b8Sbellard     /* Scan interp_prefix dir for replacement files. */
109374cd30b8Sbellard     init_paths(interp_prefix);
109474cd30b8Sbellard 
109583fb7adfSbellard     /* NOTE: we need to init the CPU at this stage to get
109683fb7adfSbellard        qemu_host_page_size */
1097b346ff46Sbellard     env = cpu_init();
109854936004Sbellard 
109974cd30b8Sbellard     if (elf_exec(filename, argv+optind, environ, regs, info) != 0) {
110031e31b8aSbellard 	printf("Error loading %s\n", filename);
110174cd30b8Sbellard 	_exit(1);
110231e31b8aSbellard     }
110331e31b8aSbellard 
11044b74fe1fSbellard     if (loglevel) {
110554936004Sbellard         page_dump(logfile);
110654936004Sbellard 
11074b74fe1fSbellard         fprintf(logfile, "start_brk   0x%08lx\n" , info->start_brk);
11084b74fe1fSbellard         fprintf(logfile, "end_code    0x%08lx\n" , info->end_code);
11094b74fe1fSbellard         fprintf(logfile, "start_code  0x%08lx\n" , info->start_code);
11104b74fe1fSbellard         fprintf(logfile, "end_data    0x%08lx\n" , info->end_data);
11114b74fe1fSbellard         fprintf(logfile, "start_stack 0x%08lx\n" , info->start_stack);
11124b74fe1fSbellard         fprintf(logfile, "brk         0x%08lx\n" , info->brk);
1113b346ff46Sbellard         fprintf(logfile, "entry       0x%08lx\n" , info->entry);
11144b74fe1fSbellard     }
111531e31b8aSbellard 
111631e31b8aSbellard     target_set_brk((char *)info->brk);
111731e31b8aSbellard     syscall_init();
111866fb9763Sbellard     signal_init();
111931e31b8aSbellard 
11209de5e440Sbellard     global_env = env;
112131e31b8aSbellard 
1122851e67a1Sbellard     /* build Task State */
1123851e67a1Sbellard     memset(ts, 0, sizeof(TaskState));
1124851e67a1Sbellard     env->opaque = ts;
1125851e67a1Sbellard     ts->used = 1;
112659faf6d6Sbellard     env->user_mode_only = 1;
1127851e67a1Sbellard 
1128b346ff46Sbellard #if defined(TARGET_I386)
11292e255c6bSbellard     cpu_x86_set_cpl(env, 3);
11302e255c6bSbellard 
11313802ce26Sbellard     env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK;
11321bde465eSbellard     env->hflags |= HF_PE_MASK;
11331bde465eSbellard     if (env->cpuid_features & CPUID_SSE) {
11341bde465eSbellard         env->cr[4] |= CR4_OSFXSR_MASK;
11351bde465eSbellard         env->hflags |= HF_OSFXSR_MASK;
11361bde465eSbellard     }
11373802ce26Sbellard 
1138415e561fSbellard     /* flags setup : we activate the IRQs by default as in user mode */
1139415e561fSbellard     env->eflags |= IF_MASK;
1140415e561fSbellard 
11416dbad63eSbellard     /* linux register setup */
11420ecfa993Sbellard     env->regs[R_EAX] = regs->eax;
11430ecfa993Sbellard     env->regs[R_EBX] = regs->ebx;
11440ecfa993Sbellard     env->regs[R_ECX] = regs->ecx;
11450ecfa993Sbellard     env->regs[R_EDX] = regs->edx;
11460ecfa993Sbellard     env->regs[R_ESI] = regs->esi;
11470ecfa993Sbellard     env->regs[R_EDI] = regs->edi;
11480ecfa993Sbellard     env->regs[R_EBP] = regs->ebp;
11490ecfa993Sbellard     env->regs[R_ESP] = regs->esp;
1150dab2ed99Sbellard     env->eip = regs->eip;
115131e31b8aSbellard 
1152f4beb510Sbellard     /* linux interrupt setup */
115380a9d035Sbellard     env->idt.base = (long)idt_table;
1154f4beb510Sbellard     env->idt.limit = sizeof(idt_table) - 1;
1155f4beb510Sbellard     set_idt(0, 0);
1156f4beb510Sbellard     set_idt(1, 0);
1157f4beb510Sbellard     set_idt(2, 0);
1158f4beb510Sbellard     set_idt(3, 3);
1159f4beb510Sbellard     set_idt(4, 3);
1160f4beb510Sbellard     set_idt(5, 3);
1161f4beb510Sbellard     set_idt(6, 0);
1162f4beb510Sbellard     set_idt(7, 0);
1163f4beb510Sbellard     set_idt(8, 0);
1164f4beb510Sbellard     set_idt(9, 0);
1165f4beb510Sbellard     set_idt(10, 0);
1166f4beb510Sbellard     set_idt(11, 0);
1167f4beb510Sbellard     set_idt(12, 0);
1168f4beb510Sbellard     set_idt(13, 0);
1169f4beb510Sbellard     set_idt(14, 0);
1170f4beb510Sbellard     set_idt(15, 0);
1171f4beb510Sbellard     set_idt(16, 0);
1172f4beb510Sbellard     set_idt(17, 0);
1173f4beb510Sbellard     set_idt(18, 0);
1174f4beb510Sbellard     set_idt(19, 0);
1175f4beb510Sbellard     set_idt(0x80, 3);
1176f4beb510Sbellard 
11776dbad63eSbellard     /* linux segment setup */
117880a9d035Sbellard     env->gdt.base = (long)gdt_table;
11796dbad63eSbellard     env->gdt.limit = sizeof(gdt_table) - 1;
1180f4beb510Sbellard     write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff,
1181f4beb510Sbellard              DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
1182f4beb510Sbellard              (3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
1183f4beb510Sbellard     write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff,
1184f4beb510Sbellard              DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
1185f4beb510Sbellard              (3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT));
11866dbad63eSbellard     cpu_x86_load_seg(env, R_CS, __USER_CS);
11876dbad63eSbellard     cpu_x86_load_seg(env, R_DS, __USER_DS);
11886dbad63eSbellard     cpu_x86_load_seg(env, R_ES, __USER_DS);
11896dbad63eSbellard     cpu_x86_load_seg(env, R_SS, __USER_DS);
11906dbad63eSbellard     cpu_x86_load_seg(env, R_FS, __USER_DS);
11916dbad63eSbellard     cpu_x86_load_seg(env, R_GS, __USER_DS);
119292ccca6aSbellard 
1193b346ff46Sbellard #elif defined(TARGET_ARM)
1194b346ff46Sbellard     {
1195b346ff46Sbellard         int i;
1196b346ff46Sbellard         for(i = 0; i < 16; i++) {
1197b346ff46Sbellard             env->regs[i] = regs->uregs[i];
1198b346ff46Sbellard         }
1199b346ff46Sbellard         env->cpsr = regs->uregs[16];
1200a4f81979Sbellard         ts->stack_base = info->start_stack;
1201a4f81979Sbellard         ts->heap_base = info->brk;
1202a4f81979Sbellard         /* This will be filled in on the first SYS_HEAPINFO call.  */
1203a4f81979Sbellard         ts->heap_limit = 0;
1204b346ff46Sbellard     }
120593ac68bcSbellard #elif defined(TARGET_SPARC)
1206060366c5Sbellard     {
1207060366c5Sbellard         int i;
1208060366c5Sbellard 	env->pc = regs->pc;
1209060366c5Sbellard 	env->npc = regs->npc;
1210060366c5Sbellard         env->y = regs->y;
1211060366c5Sbellard         for(i = 0; i < 8; i++)
1212060366c5Sbellard             env->gregs[i] = regs->u_regs[i];
1213060366c5Sbellard         for(i = 0; i < 8; i++)
1214060366c5Sbellard             env->regwptr[i] = regs->u_regs[i + 8];
1215060366c5Sbellard     }
121667867308Sbellard #elif defined(TARGET_PPC)
121767867308Sbellard     {
12183fc6c082Sbellard         ppc_def_t *def;
121967867308Sbellard         int i;
12203fc6c082Sbellard 
12213fc6c082Sbellard         /* Choose and initialise CPU */
12223fc6c082Sbellard         /* XXX: CPU model (or PVR) should be provided on command line */
12233fc6c082Sbellard         //        ppc_find_by_name("750gx", &def);
12243fc6c082Sbellard         //        ppc_find_by_name("750fx", &def);
12253fc6c082Sbellard         //        ppc_find_by_name("750p", &def);
12263fc6c082Sbellard         ppc_find_by_name("750", &def);
12273fc6c082Sbellard         //        ppc_find_by_name("G3", &def);
12283fc6c082Sbellard         //        ppc_find_by_name("604r", &def);
12293fc6c082Sbellard         //        ppc_find_by_name("604e", &def);
12303fc6c082Sbellard         //        ppc_find_by_name("604", &def);
12313fc6c082Sbellard         if (def == NULL) {
12323fc6c082Sbellard             cpu_abort(cpu_single_env,
12333fc6c082Sbellard                       "Unable to find PowerPC CPU definition\n");
12343fc6c082Sbellard         }
12353fc6c082Sbellard         cpu_ppc_register(cpu_single_env, def);
12363fc6c082Sbellard 
123761190b14Sbellard         for (i = 0; i < 32; i++) {
12384c2e770fSbellard             if (i != 12 && i != 6 && i != 13)
123967867308Sbellard                 env->msr[i] = (regs->msr >> i) & 1;
124061190b14Sbellard         }
124167867308Sbellard         env->nip = regs->nip;
124267867308Sbellard         for(i = 0; i < 32; i++) {
124367867308Sbellard             env->gpr[i] = regs->gpr[i];
124467867308Sbellard         }
124567867308Sbellard     }
1246b346ff46Sbellard #else
1247b346ff46Sbellard #error unsupported target CPU
1248b346ff46Sbellard #endif
124931e31b8aSbellard 
12501fddef4bSbellard     if (use_gdbstub) {
12511fddef4bSbellard         gdbserver_start (DEFAULT_GDBSTUB_PORT);
12521fddef4bSbellard         gdb_handlesig(env, 0);
12531fddef4bSbellard     }
12541b6b029eSbellard     cpu_loop(env);
12551b6b029eSbellard     /* never exits */
125631e31b8aSbellard     return 0;
125731e31b8aSbellard }
1258