optimize omap3 dss packed 24bpp mode
[qemu] / bsd-user / main.c
1 /*
2  *  qemu user main
3  *
4  *  Copyright (c) 2003-2008 Fabrice Bellard
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
19  *  MA 02110-1301, USA.
20  */
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <stdarg.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <unistd.h>
27 #include <machine/trap.h>
28
29 #include "qemu.h"
30 #include "qemu-common.h"
31 /* For tb_lock */
32 #include "exec-all.h"
33
34 #define DEBUG_LOGFILE "/tmp/qemu.log"
35
36 static const char *interp_prefix = CONFIG_QEMU_PREFIX;
37 const char *qemu_uname_release = CONFIG_UNAME_RELEASE;
38 extern char **environ;
39
40 /* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
41    we allocate a bigger stack. Need a better solution, for example
42    by remapping the process stack directly at the right place */
43 unsigned long x86_stack_size = 512 * 1024;
44
45 void gemu_log(const char *fmt, ...)
46 {
47     va_list ap;
48
49     va_start(ap, fmt);
50     vfprintf(stderr, fmt, ap);
51     va_end(ap);
52 }
53
54 /* These are no-ops because we are not threadsafe.  */
55 static inline void cpu_exec_start(CPUState *env)
56 {
57 }
58
59 static inline void cpu_exec_end(CPUState *env)
60 {
61 }
62
63 static inline void start_exclusive(void)
64 {
65 }
66
67 static inline void end_exclusive(void)
68 {
69 }
70
71 void fork_start(void)
72 {
73 }
74
75 void fork_end(int child)
76 {
77     if (child) {
78         gdbserver_fork(thread_env);
79     }
80 }
81
82 void cpu_list_lock(void)
83 {
84 }
85
86 void cpu_list_unlock(void)
87 {
88 }
89
90 #ifdef TARGET_SPARC
91 #define SPARC64_STACK_BIAS 2047
92
93 //#define DEBUG_WIN
94 /* WARNING: dealing with register windows _is_ complicated. More info
95    can be found at http://www.sics.se/~psm/sparcstack.html */
96 static inline int get_reg_index(CPUSPARCState *env, int cwp, int index)
97 {
98     index = (index + cwp * 16) % (16 * env->nwindows);
99     /* wrap handling : if cwp is on the last window, then we use the
100        registers 'after' the end */
101     if (index < 8 && env->cwp == env->nwindows - 1)
102         index += 16 * env->nwindows;
103     return index;
104 }
105
106 /* save the register window 'cwp1' */
107 static inline void save_window_offset(CPUSPARCState *env, int cwp1)
108 {
109     unsigned int i;
110     abi_ulong sp_ptr;
111
112     sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)];
113 #ifdef TARGET_SPARC64
114     if (sp_ptr & 3)
115         sp_ptr += SPARC64_STACK_BIAS;
116 #endif
117 #if defined(DEBUG_WIN)
118     printf("win_overflow: sp_ptr=0x" TARGET_ABI_FMT_lx " save_cwp=%d\n",
119            sp_ptr, cwp1);
120 #endif
121     for(i = 0; i < 16; i++) {
122         /* FIXME - what to do if put_user() fails? */
123         put_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr);
124         sp_ptr += sizeof(abi_ulong);
125     }
126 }
127
128 static void save_window(CPUSPARCState *env)
129 {
130 #ifndef TARGET_SPARC64
131     unsigned int new_wim;
132     new_wim = ((env->wim >> 1) | (env->wim << (env->nwindows - 1))) &
133         ((1LL << env->nwindows) - 1);
134     save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2));
135     env->wim = new_wim;
136 #else
137     save_window_offset(env, cpu_cwp_dec(env, env->cwp - 2));
138     env->cansave++;
139     env->canrestore--;
140 #endif
141 }
142
143 static void restore_window(CPUSPARCState *env)
144 {
145 #ifndef TARGET_SPARC64
146     unsigned int new_wim;
147 #endif
148     unsigned int i, cwp1;
149     abi_ulong sp_ptr;
150
151 #ifndef TARGET_SPARC64
152     new_wim = ((env->wim << 1) | (env->wim >> (env->nwindows - 1))) &
153         ((1LL << env->nwindows) - 1);
154 #endif
155
156     /* restore the invalid window */
157     cwp1 = cpu_cwp_inc(env, env->cwp + 1);
158     sp_ptr = env->regbase[get_reg_index(env, cwp1, 6)];
159 #ifdef TARGET_SPARC64
160     if (sp_ptr & 3)
161         sp_ptr += SPARC64_STACK_BIAS;
162 #endif
163 #if defined(DEBUG_WIN)
164     printf("win_underflow: sp_ptr=0x" TARGET_ABI_FMT_lx " load_cwp=%d\n",
165            sp_ptr, cwp1);
166 #endif
167     for(i = 0; i < 16; i++) {
168         /* FIXME - what to do if get_user() fails? */
169         get_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr);
170         sp_ptr += sizeof(abi_ulong);
171     }
172 #ifdef TARGET_SPARC64
173     env->canrestore++;
174     if (env->cleanwin < env->nwindows - 1)
175         env->cleanwin++;
176     env->cansave--;
177 #else
178     env->wim = new_wim;
179 #endif
180 }
181
182 static void flush_windows(CPUSPARCState *env)
183 {
184     int offset, cwp1;
185
186     offset = 1;
187     for(;;) {
188         /* if restore would invoke restore_window(), then we can stop */
189         cwp1 = cpu_cwp_inc(env, env->cwp + offset);
190 #ifndef TARGET_SPARC64
191         if (env->wim & (1 << cwp1))
192             break;
193 #else
194         if (env->canrestore == 0)
195             break;
196         env->cansave++;
197         env->canrestore--;
198 #endif
199         save_window_offset(env, cwp1);
200         offset++;
201     }
202     cwp1 = cpu_cwp_inc(env, env->cwp + 1);
203 #ifndef TARGET_SPARC64
204     /* set wim so that restore will reload the registers */
205     env->wim = 1 << cwp1;
206 #endif
207 #if defined(DEBUG_WIN)
208     printf("flush_windows: nb=%d\n", offset - 1);
209 #endif
210 }
211
212 void cpu_loop(CPUSPARCState *env, enum BSDType bsd_type)
213 {
214     int trapnr, ret, syscall_nr;
215     //target_siginfo_t info;
216
217     while (1) {
218         trapnr = cpu_sparc_exec (env);
219
220         switch (trapnr) {
221 #ifndef TARGET_SPARC64
222         case 0x80:
223 #else
224         case 0x100:
225 #endif
226             syscall_nr = env->gregs[1];
227             if (bsd_type == target_freebsd)
228                 ret = do_freebsd_syscall(env, syscall_nr,
229                                          env->regwptr[0], env->regwptr[1],
230                                          env->regwptr[2], env->regwptr[3],
231                                          env->regwptr[4], env->regwptr[5]);
232             else if (bsd_type == target_netbsd)
233                 ret = do_netbsd_syscall(env, syscall_nr,
234                                         env->regwptr[0], env->regwptr[1],
235                                         env->regwptr[2], env->regwptr[3],
236                                         env->regwptr[4], env->regwptr[5]);
237             else { //if (bsd_type == target_openbsd)
238 #if defined(TARGET_SPARC64)
239                 syscall_nr &= ~(TARGET_OPENBSD_SYSCALL_G7RFLAG |
240                                 TARGET_OPENBSD_SYSCALL_G2RFLAG);
241 #endif
242                 ret = do_openbsd_syscall(env, syscall_nr,
243                                          env->regwptr[0], env->regwptr[1],
244                                          env->regwptr[2], env->regwptr[3],
245                                          env->regwptr[4], env->regwptr[5]);
246             }
247             if ((unsigned int)ret >= (unsigned int)(-515)) {
248 #if defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
249                 env->xcc |= PSR_CARRY;
250 #else
251                 env->psr |= PSR_CARRY;
252 #endif
253             } else {
254 #if defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
255                 env->xcc &= ~PSR_CARRY;
256 #else
257                 env->psr &= ~PSR_CARRY;
258 #endif
259             }
260             env->regwptr[0] = ret;
261             /* next instruction */
262 #if defined(TARGET_SPARC64)
263             if (bsd_type == target_openbsd &&
264                 env->gregs[1] & TARGET_OPENBSD_SYSCALL_G2RFLAG) {
265                 env->pc = env->gregs[2];
266                 env->npc = env->pc + 4;
267             } else if (bsd_type == target_openbsd &&
268                        env->gregs[1] & TARGET_OPENBSD_SYSCALL_G7RFLAG) {
269                 env->pc = env->gregs[7];
270                 env->npc = env->pc + 4;
271             } else {
272                 env->pc = env->npc;
273                 env->npc = env->npc + 4;
274             }
275 #else
276             env->pc = env->npc;
277             env->npc = env->npc + 4;
278 #endif
279             break;
280         case 0x83: /* flush windows */
281 #ifdef TARGET_ABI32
282         case 0x103:
283 #endif
284             flush_windows(env);
285             /* next instruction */
286             env->pc = env->npc;
287             env->npc = env->npc + 4;
288             break;
289 #ifndef TARGET_SPARC64
290         case TT_WIN_OVF: /* window overflow */
291             save_window(env);
292             break;
293         case TT_WIN_UNF: /* window underflow */
294             restore_window(env);
295             break;
296         case TT_TFAULT:
297         case TT_DFAULT:
298 #if 0
299             {
300                 info.si_signo = SIGSEGV;
301                 info.si_errno = 0;
302                 /* XXX: check env->error_code */
303                 info.si_code = TARGET_SEGV_MAPERR;
304                 info._sifields._sigfault._addr = env->mmuregs[4];
305                 queue_signal(env, info.si_signo, &info);
306             }
307 #endif
308             break;
309 #else
310         case TT_SPILL: /* window overflow */
311             save_window(env);
312             break;
313         case TT_FILL: /* window underflow */
314             restore_window(env);
315             break;
316         case TT_TFAULT:
317         case TT_DFAULT:
318 #if 0
319             {
320                 info.si_signo = SIGSEGV;
321                 info.si_errno = 0;
322                 /* XXX: check env->error_code */
323                 info.si_code = TARGET_SEGV_MAPERR;
324                 if (trapnr == TT_DFAULT)
325                     info._sifields._sigfault._addr = env->dmmuregs[4];
326                 else
327                     info._sifields._sigfault._addr = env->tsptr->tpc;
328                 //queue_signal(env, info.si_signo, &info);
329             }
330 #endif
331             break;
332 #endif
333         case EXCP_INTERRUPT:
334             /* just indicate that signals should be handled asap */
335             break;
336         case EXCP_DEBUG:
337             {
338                 int sig;
339
340                 sig = gdb_handlesig (env, TARGET_SIGTRAP);
341 #if 0
342                 if (sig)
343                   {
344                     info.si_signo = sig;
345                     info.si_errno = 0;
346                     info.si_code = TARGET_TRAP_BRKPT;
347                     //queue_signal(env, info.si_signo, &info);
348                   }
349 #endif
350             }
351             break;
352         default:
353             printf ("Unhandled trap: 0x%x\n", trapnr);
354             cpu_dump_state(env, stderr, fprintf, 0);
355             exit (1);
356         }
357         process_pending_signals (env);
358     }
359 }
360
361 #endif
362
363 static void usage(void)
364 {
365     printf("qemu-" TARGET_ARCH " version " QEMU_VERSION ", Copyright (c) 2003-2008 Fabrice Bellard\n"
366            "usage: qemu-" TARGET_ARCH " [options] program [arguments...]\n"
367            "BSD CPU emulator (compiled for %s emulation)\n"
368            "\n"
369            "Standard options:\n"
370            "-h                print this help\n"
371            "-g port           wait gdb connection to port\n"
372            "-L path           set the elf interpreter prefix (default=%s)\n"
373            "-s size           set the stack size in bytes (default=%ld)\n"
374            "-cpu model        select CPU (-cpu ? for list)\n"
375            "-drop-ld-preload  drop LD_PRELOAD for target process\n"
376            "-bsd type         select emulated BSD type FreeBSD/NetBSD/OpenBSD (default)\n"
377            "\n"
378            "Debug options:\n"
379            "-d options   activate log (logfile=%s)\n"
380            "-p pagesize  set the host page size to 'pagesize'\n"
381            "-strace      log system calls\n"
382            "\n"
383            "Environment variables:\n"
384            "QEMU_STRACE       Print system calls and arguments similar to the\n"
385            "                  'strace' program.  Enable by setting to any value.\n"
386            ,
387            TARGET_ARCH,
388            interp_prefix,
389            x86_stack_size,
390            DEBUG_LOGFILE);
391     exit(1);
392 }
393
394 THREAD CPUState *thread_env;
395
396 /* Assumes contents are already zeroed.  */
397 void init_task_state(TaskState *ts)
398 {
399     int i;
400
401     ts->used = 1;
402     ts->first_free = ts->sigqueue_table;
403     for (i = 0; i < MAX_SIGQUEUE_SIZE - 1; i++) {
404         ts->sigqueue_table[i].next = &ts->sigqueue_table[i + 1];
405     }
406     ts->sigqueue_table[i].next = NULL;
407 }
408
409 int main(int argc, char **argv)
410 {
411     const char *filename;
412     const char *cpu_model;
413     struct target_pt_regs regs1, *regs = &regs1;
414     struct image_info info1, *info = &info1;
415     TaskState ts1, *ts = &ts1;
416     CPUState *env;
417     int optind;
418     const char *r;
419     int gdbstub_port = 0;
420     int drop_ld_preload = 0, environ_count = 0;
421     char **target_environ, **wrk, **dst;
422     enum BSDType bsd_type = target_openbsd;
423
424     if (argc <= 1)
425         usage();
426
427     /* init debug */
428     cpu_set_log_filename(DEBUG_LOGFILE);
429
430     cpu_model = NULL;
431     optind = 1;
432     for(;;) {
433         if (optind >= argc)
434             break;
435         r = argv[optind];
436         if (r[0] != '-')
437             break;
438         optind++;
439         r++;
440         if (!strcmp(r, "-")) {
441             break;
442         } else if (!strcmp(r, "d")) {
443             int mask;
444             const CPULogItem *item;
445
446             if (optind >= argc)
447                 break;
448
449             r = argv[optind++];
450             mask = cpu_str_to_log_mask(r);
451             if (!mask) {
452                 printf("Log items (comma separated):\n");
453                 for(item = cpu_log_items; item->mask != 0; item++) {
454                     printf("%-10s %s\n", item->name, item->help);
455                 }
456                 exit(1);
457             }
458             cpu_set_log(mask);
459         } else if (!strcmp(r, "s")) {
460             r = argv[optind++];
461             x86_stack_size = strtol(r, (char **)&r, 0);
462             if (x86_stack_size <= 0)
463                 usage();
464             if (*r == 'M')
465                 x86_stack_size *= 1024 * 1024;
466             else if (*r == 'k' || *r == 'K')
467                 x86_stack_size *= 1024;
468         } else if (!strcmp(r, "L")) {
469             interp_prefix = argv[optind++];
470         } else if (!strcmp(r, "p")) {
471             qemu_host_page_size = atoi(argv[optind++]);
472             if (qemu_host_page_size == 0 ||
473                 (qemu_host_page_size & (qemu_host_page_size - 1)) != 0) {
474                 fprintf(stderr, "page size must be a power of two\n");
475                 exit(1);
476             }
477         } else if (!strcmp(r, "g")) {
478             gdbstub_port = atoi(argv[optind++]);
479         } else if (!strcmp(r, "r")) {
480             qemu_uname_release = argv[optind++];
481         } else if (!strcmp(r, "cpu")) {
482             cpu_model = argv[optind++];
483             if (strcmp(cpu_model, "?") == 0) {
484 /* XXX: implement xxx_cpu_list for targets that still miss it */
485 #if defined(cpu_list)
486                     cpu_list(stdout, &fprintf);
487 #endif
488                 exit(1);
489             }
490         } else if (!strcmp(r, "drop-ld-preload")) {
491             drop_ld_preload = 1;
492         } else if (!strcmp(r, "bsd")) {
493             if (!strcasecmp(argv[optind], "freebsd")) {
494                 bsd_type = target_freebsd;
495             } else if (!strcasecmp(argv[optind], "netbsd")) {
496                 bsd_type = target_netbsd;
497             } else if (!strcasecmp(argv[optind], "openbsd")) {
498                 bsd_type = target_openbsd;
499             } else {
500                 usage();
501             }
502             optind++;
503         } else if (!strcmp(r, "strace")) {
504             do_strace = 1;
505         } else
506         {
507             usage();
508         }
509     }
510     if (optind >= argc)
511         usage();
512     filename = argv[optind];
513
514     /* Zero out regs */
515     memset(regs, 0, sizeof(struct target_pt_regs));
516
517     /* Zero out image_info */
518     memset(info, 0, sizeof(struct image_info));
519
520     /* Scan interp_prefix dir for replacement files. */
521     init_paths(interp_prefix);
522
523     if (cpu_model == NULL) {
524 #if defined(TARGET_SPARC)
525 #ifdef TARGET_SPARC64
526         cpu_model = "TI UltraSparc II";
527 #else
528         cpu_model = "Fujitsu MB86904";
529 #endif
530 #else
531         cpu_model = "any";
532 #endif
533     }
534     cpu_exec_init_all(0);
535     /* NOTE: we need to init the CPU at this stage to get
536        qemu_host_page_size */
537     env = cpu_init(cpu_model);
538     if (!env) {
539         fprintf(stderr, "Unable to find CPU definition\n");
540         exit(1);
541     }
542     thread_env = env;
543
544     if (getenv("QEMU_STRACE")) {
545         do_strace = 1;
546     }
547
548     wrk = environ;
549     while (*(wrk++))
550         environ_count++;
551
552     target_environ = malloc((environ_count + 1) * sizeof(char *));
553     if (!target_environ)
554         abort();
555     for (wrk = environ, dst = target_environ; *wrk; wrk++) {
556         if (drop_ld_preload && !strncmp(*wrk, "LD_PRELOAD=", 11))
557             continue;
558         *(dst++) = strdup(*wrk);
559     }
560     *dst = NULL; /* NULL terminate target_environ */
561
562     if (loader_exec(filename, argv+optind, target_environ, regs, info) != 0) {
563         printf("Error loading %s\n", filename);
564         _exit(1);
565     }
566
567     for (wrk = target_environ; *wrk; wrk++) {
568         free(*wrk);
569     }
570
571     free(target_environ);
572
573     if (qemu_log_enabled()) {
574         log_page_dump();
575
576         qemu_log("start_brk   0x" TARGET_ABI_FMT_lx "\n", info->start_brk);
577         qemu_log("end_code    0x" TARGET_ABI_FMT_lx "\n", info->end_code);
578         qemu_log("start_code  0x" TARGET_ABI_FMT_lx "\n",
579                  info->start_code);
580         qemu_log("start_data  0x" TARGET_ABI_FMT_lx "\n",
581                  info->start_data);
582         qemu_log("end_data    0x" TARGET_ABI_FMT_lx "\n", info->end_data);
583         qemu_log("start_stack 0x" TARGET_ABI_FMT_lx "\n",
584                  info->start_stack);
585         qemu_log("brk         0x" TARGET_ABI_FMT_lx "\n", info->brk);
586         qemu_log("entry       0x" TARGET_ABI_FMT_lx "\n", info->entry);
587     }
588
589     target_set_brk(info->brk);
590     syscall_init();
591     signal_init();
592
593     /* build Task State */
594     memset(ts, 0, sizeof(TaskState));
595     init_task_state(ts);
596     ts->info = info;
597     env->opaque = ts;
598
599 #if defined(TARGET_SPARC)
600     {
601         int i;
602         env->pc = regs->pc;
603         env->npc = regs->npc;
604         env->y = regs->y;
605         for(i = 0; i < 8; i++)
606             env->gregs[i] = regs->u_regs[i];
607         for(i = 0; i < 8; i++)
608             env->regwptr[i] = regs->u_regs[i + 8];
609     }
610 #else
611 #error unsupported target CPU
612 #endif
613
614     if (gdbstub_port) {
615         gdbserver_start (gdbstub_port);
616         gdb_handlesig(env, 0);
617     }
618     cpu_loop(env, bsd_type);
619     /* never exits */
620     return 0;
621 }