qemu/bsd-user/main.c
Peter Maydell a1362f392b pull-bsd-user-20210511 is the next round of cleanups to bsd-user in merging the
bsd-user fork into qemu. It contains a number of style commits, as well as 3
 commits that start to change things: Some unused files are deleted, building the
 sparc and sparc64 targets are removed, and a structure is renamed.
 
 The next set of pull requests will start to execute the following plan:
 1. Move existing code around to have a structure similar to the bsd-user fork.
 2. Incrementally merge groups of system calls, focused on making x86 work.
 3. Once the groups of system calls are all merged, additional platforms will
    be added back.
 4. Concurrently, as changes are requested as part of the merge happen, those
    changes will be merged into the fork. An experimental merge to tip of master
    is under test and is what will be updated.
 5. Eventually, there will be no diference, and the bsd-user fork will only
    be a staging area for cutting-edge features prior to upstreaming into
    qemu mainline.
 
 The bsd-user code in qemu has a lot of style issues. This cleans up a number in
 the files touched. However, the checkpatch.pl detects some incremental issues in
 the commits. The following are expected, but are corrected in later hashes in
 this branch. MAINTAINERS does not need to be updated, since all the files added
 or deleted are covered under existing regexp in MAINTAINERS.
 
 Checking all commits since f9a576a818044133f8564e0d243ebd97df0b3280...
 d60c3b932e2fa06aba5d7aa1c451b5d287095dc8:101: ERROR: consider using qemu_strtol in preference to strtol
 d60c3b932e2fa06aba5d7aa1c451b5d287095dc8:142: ERROR: braces {} are necessary for all arms of this statement
 d60c3b932e2fa06aba5d7aa1c451b5d287095dc8:145: ERROR: braces {} are necessary for all arms of this statement
 total: 3 errors, 0 warnings, 119 lines checked
 b4bebeee1dee8d333bfa105a6c28fec5eb34b147:148: WARNING: line over 80 characters
 b4bebeee1dee8d333bfa105a6c28fec5eb34b147:330: ERROR: braces {} are necessary for all arms of this statement
 b4bebeee1dee8d333bfa105a6c28fec5eb34b147:340: ERROR: braces {} are necessary for all arms of this statement
 b4bebeee1dee8d333bfa105a6c28fec5eb34b147:381: ERROR: space prohibited between function name and open parenthesis '('
 b4bebeee1dee8d333bfa105a6c28fec5eb34b147:390: ERROR: spaces required around that '<' (ctx:VxV)
 b4bebeee1dee8d333bfa105a6c28fec5eb34b147:408: WARNING: Block comments use a leading /* on a separate line
 b4bebeee1dee8d333bfa105a6c28fec5eb34b147:409: WARNING: Block comments use * on subsequent lines
 b4bebeee1dee8d333bfa105a6c28fec5eb34b147:409: WARNING: Block comments use a trailing */ on a separate line
 b4bebeee1dee8d333bfa105a6c28fec5eb34b147:441: WARNING: line over 80 characters
 b4bebeee1dee8d333bfa105a6c28fec5eb34b147:445: WARNING: line over 80 characters
 b4bebeee1dee8d333bfa105a6c28fec5eb34b147:502: ERROR: line over 90 characters
 b4bebeee1dee8d333bfa105a6c28fec5eb34b147:551: ERROR: space required after that ',' (ctx:VxV)
 b4bebeee1dee8d333bfa105a6c28fec5eb34b147:552: ERROR: space required after that ',' (ctx:VxV)
 b4bebeee1dee8d333bfa105a6c28fec5eb34b147:587: ERROR: space required after that ';' (ctx:VxV)
 b4bebeee1dee8d333bfa105a6c28fec5eb34b147:623: ERROR: suspect code indent for conditional statements (12, 14)
 total: 9 errors, 6 warnings, 664 lines checked
 86545e7afe3f822b8561c7ceee7540fc3b19c3f0:31: ERROR: space required after that ';' (ctx:VxV)
 86545e7afe3f822b8561c7ceee7540fc3b19c3f0:40: ERROR: space required after that ';' (ctx:VxV)
 total: 2 errors, 0 warnings, 60 lines checked
 Use of uninitialized value $acpi_testexpected in string eq at scripts/checkpatch.pl line 1529.
 65d58c91ef1a15ad945ece367983437576f8e82b:22: WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
 total: 0 errors, 1 warnings, 14 lines checked
 Use of uninitialized value $acpi_testexpected in string eq at scripts/checkpatch.pl line 1529.
 f8ce39701b5be032fb3f9c05e8adb4055f70eec2:21: WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
 total: 0 errors, 1 warnings, 0 lines checked
    FAIL one or more commits failed scripts/checkpatch.pl
 Cleaning up file based variables
 ERROR: Job failed: exit code 1
 -----BEGIN PGP SIGNATURE-----
 Comment: GPGTools - https://gpgtools.org
 
 iQIzBAABCgAdFiEEIDX4lLAKo898zeG3bBzRKH2wEQAFAmCcBQIACgkQbBzRKH2w
 EQD4+A/9E3Yr7cIBegz0km6pTHX89y6AW4+qd6QkQgYkZ9PloIVCjSn3NxvvPy3d
 sCVhZu2FutEuoI2EOjzBpgQCp8mhSfayPKiZIzvHeJn9yiTmNnuHd/Opr2NaxkUV
 WT9Eli7aB5AhYL1AD6RVEyYQYQJLq/ULdJXUVaOe5X2werk/FT7izhVqA4hkMFUm
 Rx3RrMZDAyhgPT1+EUrNMgw2/dQw7QEH3w0RREc/Btfs8Wwisqq+Uy0Z8p9+khSU
 K5ES6FnYAoOU9EP1LMjOzuh54B2Ta9J0Z4vfjlul8+WM9DBap5FogiNChVd/mjvL
 1NYJUPFIC9KaV+5xNAJanW7h531lFKwHzZka68Dmzsbjdj0sUg2GkPhneOXEH8lX
 Q6Kmoxpg8IpfwfB/VDPM4eL3SdNQd8d/1VSUkLOS0reOc8L9RmLXSYlJILzNkDR9
 yODoBgSktX2e4wnyuknyXi1JuIx2yHCsPHq+PtBYUnHEfc8W7V9yxwhAYshuRQDy
 sZO1AdwEQHzgNjnCPK8ru6DqrizHwKnlE1KUy5KxUOcmfaacZ//t9ezf8qSd19eC
 6deGfmfvp3Xxtz38eCLogKJXzIDjf+FMaN+0znOd0ut9qPPCqBgALZNnkeaDz+zx
 2ZGhjwcRAsKZmGSLQoKPN+YkM5POwzWvEPNApPsVJjpTXpoZcJ8=
 =pW6n
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/bsdimp/tags/pull-bsd-user-20210511' into staging

pull-bsd-user-20210511 is the next round of cleanups to bsd-user in merging the
bsd-user fork into qemu. It contains a number of style commits, as well as 3
commits that start to change things: Some unused files are deleted, building the
sparc and sparc64 targets are removed, and a structure is renamed.

The next set of pull requests will start to execute the following plan:
1. Move existing code around to have a structure similar to the bsd-user fork.
2. Incrementally merge groups of system calls, focused on making x86 work.
3. Once the groups of system calls are all merged, additional platforms will
   be added back.
4. Concurrently, as changes are requested as part of the merge happen, those
   changes will be merged into the fork. An experimental merge to tip of master
   is under test and is what will be updated.
5. Eventually, there will be no diference, and the bsd-user fork will only
   be a staging area for cutting-edge features prior to upstreaming into
   qemu mainline.

The bsd-user code in qemu has a lot of style issues. This cleans up a number in
the files touched. However, the checkpatch.pl detects some incremental issues in
the commits. The following are expected, but are corrected in later hashes in
this branch. MAINTAINERS does not need to be updated, since all the files added
or deleted are covered under existing regexp in MAINTAINERS.

Checking all commits since f9a576a818044133f8564e0d243ebd97df0b3280...
d60c3b932e2fa06aba5d7aa1c451b5d287095dc8:101: ERROR: consider using qemu_strtol in preference to strtol
d60c3b932e2fa06aba5d7aa1c451b5d287095dc8:142: ERROR: braces {} are necessary for all arms of this statement
d60c3b932e2fa06aba5d7aa1c451b5d287095dc8:145: ERROR: braces {} are necessary for all arms of this statement
total: 3 errors, 0 warnings, 119 lines checked
b4bebeee1dee8d333bfa105a6c28fec5eb34b147:148: WARNING: line over 80 characters
b4bebeee1dee8d333bfa105a6c28fec5eb34b147:330: ERROR: braces {} are necessary for all arms of this statement
b4bebeee1dee8d333bfa105a6c28fec5eb34b147:340: ERROR: braces {} are necessary for all arms of this statement
b4bebeee1dee8d333bfa105a6c28fec5eb34b147:381: ERROR: space prohibited between function name and open parenthesis '('
b4bebeee1dee8d333bfa105a6c28fec5eb34b147:390: ERROR: spaces required around that '<' (ctx:VxV)
b4bebeee1dee8d333bfa105a6c28fec5eb34b147:408: WARNING: Block comments use a leading /* on a separate line
b4bebeee1dee8d333bfa105a6c28fec5eb34b147:409: WARNING: Block comments use * on subsequent lines
b4bebeee1dee8d333bfa105a6c28fec5eb34b147:409: WARNING: Block comments use a trailing */ on a separate line
b4bebeee1dee8d333bfa105a6c28fec5eb34b147:441: WARNING: line over 80 characters
b4bebeee1dee8d333bfa105a6c28fec5eb34b147:445: WARNING: line over 80 characters
b4bebeee1dee8d333bfa105a6c28fec5eb34b147:502: ERROR: line over 90 characters
b4bebeee1dee8d333bfa105a6c28fec5eb34b147:551: ERROR: space required after that ',' (ctx:VxV)
b4bebeee1dee8d333bfa105a6c28fec5eb34b147:552: ERROR: space required after that ',' (ctx:VxV)
b4bebeee1dee8d333bfa105a6c28fec5eb34b147:587: ERROR: space required after that ';' (ctx:VxV)
b4bebeee1dee8d333bfa105a6c28fec5eb34b147:623: ERROR: suspect code indent for conditional statements (12, 14)
total: 9 errors, 6 warnings, 664 lines checked
86545e7afe3f822b8561c7ceee7540fc3b19c3f0:31: ERROR: space required after that ';' (ctx:VxV)
86545e7afe3f822b8561c7ceee7540fc3b19c3f0:40: ERROR: space required after that ';' (ctx:VxV)
total: 2 errors, 0 warnings, 60 lines checked
Use of uninitialized value $acpi_testexpected in string eq at scripts/checkpatch.pl line 1529.
65d58c91ef1a15ad945ece367983437576f8e82b:22: WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
total: 0 errors, 1 warnings, 14 lines checked
Use of uninitialized value $acpi_testexpected in string eq at scripts/checkpatch.pl line 1529.
f8ce39701b5be032fb3f9c05e8adb4055f70eec2:21: WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
total: 0 errors, 1 warnings, 0 lines checked
   FAIL one or more commits failed scripts/checkpatch.pl
Cleaning up file based variables
ERROR: Job failed: exit code 1

# gpg: Signature made Wed 12 May 2021 17:40:34 BST
# gpg:                using RSA key 2035F894B00AA3CF7CCDE1B76C1CD1287DB01100
# gpg: Good signature from "Warner Losh <wlosh@netflix.com>" [unknown]
# gpg:                 aka "Warner Losh <imp@bsdimp.com>" [unknown]
# gpg:                 aka "Warner Losh <imp@freebsd.org>" [unknown]
# gpg:                 aka "Warner Losh <imp@village.org>" [unknown]
# gpg:                 aka "Warner Losh <wlosh@bsdimp.com>" [unknown]
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: 2035 F894 B00A A3CF 7CCD  E1B7 6C1C D128 7DB0 1100

* remotes/bsdimp/tags/pull-bsd-user-20210511:
  bsd-user: rename linux_binprm to bsd_binprm
  bsd-user: Stop building the sparc targets
  bsd-user: remove target_signal.h, it's unused
  bsd-user: style tweak: keyword space (
  bsd-user: style tweak: Remove #if 0'd code
  bsd-user: style tweak: keyword space (
  bsd-user: style tweak: keyword space (
  bsd-user: whitespace changes
  bsd-user: use qemu_strtoul in preference to strtol
  bsd-user: style tweak: use {} consistently in for / if / else statements
  bsd-user: style tweak: use {} for all if statements, format else correctly
  bsd-user: style tweak: don't assign in if statements
  bsd-user: style tweak: Use preferred block comments
  bsd-user: style tweak: remove spacing after '*' and add after }
  bsd-user: style tweak: move extern to header file
  bsd-user: Remove commented out code
  bsd-user: style tweak: Use preferred block comments
  bsd-user: style tweak: Remove #if 0'd code
  bsd-user: style tweak: use C not C++ comments
  bsd-user: whitespace changes

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
2021-05-20 15:37:44 +01:00

1030 lines
31 KiB
C

/*
* qemu user main
*
* Copyright (c) 2003-2008 Fabrice Bellard
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qemu/units.h"
#include "qemu/accel.h"
#include "sysemu/tcg.h"
#include "qemu-version.h"
#include <machine/trap.h>
#include "qapi/error.h"
#include "qemu.h"
#include "qemu/config-file.h"
#include "qemu/error-report.h"
#include "qemu/path.h"
#include "qemu/help_option.h"
#include "qemu/module.h"
#include "exec/exec-all.h"
#include "tcg/tcg.h"
#include "qemu/timer.h"
#include "qemu/envlist.h"
#include "qemu/cutils.h"
#include "exec/log.h"
#include "trace/control.h"
int singlestep;
unsigned long mmap_min_addr;
uintptr_t guest_base;
bool have_guest_base;
unsigned long reserved_va;
static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
const char *qemu_uname_release;
enum BSDType bsd_type;
/*
* 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;
void gemu_log(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}
#if defined(TARGET_I386)
int cpu_get_pic_interrupt(CPUX86State *env)
{
return -1;
}
#endif
void fork_start(void)
{
}
void fork_end(int child)
{
if (child) {
gdbserver_fork(thread_cpu);
}
}
#ifdef TARGET_I386
/***********************************************************/
/* CPUX86 core interface */
uint64_t cpu_get_tsc(CPUX86State *env)
{
return cpu_get_host_ticks();
}
static void write_dt(void *ptr, unsigned long addr, unsigned long limit,
int flags)
{
unsigned int e1, e2;
uint32_t *p;
e1 = (addr << 16) | (limit & 0xffff);
e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000);
e2 |= flags;
p = ptr;
p[0] = tswap32(e1);
p[1] = tswap32(e2);
}
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)
{
uint32_t *p, e1, e2;
e1 = (addr & 0xffff) | (sel << 16);
e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8);
p = ptr;
p[0] = tswap32(e1);
p[1] = tswap32(e2);
p[2] = tswap32(addr >> 32);
p[3] = 0;
}
/* only dpl matters as we do only user space emulation */
static void set_idt(int n, unsigned int dpl)
{
set_gate64(idt_table + n * 2, 0, dpl, 0, 0);
}
#else
static void set_gate(void *ptr, unsigned int type, unsigned int dpl,
uint32_t addr, unsigned int sel)
{
uint32_t *p, e1, e2;
e1 = (addr & 0xffff) | (sel << 16);
e2 = (addr & 0xffff0000) | 0x8000 | (dpl << 13) | (type << 8);
p = ptr;
p[0] = tswap32(e1);
p[1] = tswap32(e2);
}
/* only dpl matters as we do only user space emulation */
static void set_idt(int n, unsigned int dpl)
{
set_gate(idt_table + n, 0, dpl, 0, 0);
}
#endif
void cpu_loop(CPUX86State *env)
{
CPUState *cs = env_cpu(env);
int trapnr;
abi_ulong pc;
/* target_siginfo_t info; */
for (;;) {
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
switch (trapnr) {
case 0x80:
/* syscall from int $0x80 */
if (bsd_type == target_freebsd) {
abi_ulong params = (abi_ulong) env->regs[R_ESP] +
sizeof(int32_t);
int32_t syscall_nr = env->regs[R_EAX];
int32_t arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8;
if (syscall_nr == TARGET_FREEBSD_NR_syscall) {
get_user_s32(syscall_nr, params);
params += sizeof(int32_t);
} else if (syscall_nr == TARGET_FREEBSD_NR___syscall) {
get_user_s32(syscall_nr, params);
params += sizeof(int64_t);
}
get_user_s32(arg1, params);
params += sizeof(int32_t);
get_user_s32(arg2, params);
params += sizeof(int32_t);
get_user_s32(arg3, params);
params += sizeof(int32_t);
get_user_s32(arg4, params);
params += sizeof(int32_t);
get_user_s32(arg5, params);
params += sizeof(int32_t);
get_user_s32(arg6, params);
params += sizeof(int32_t);
get_user_s32(arg7, params);
params += sizeof(int32_t);
get_user_s32(arg8, params);
env->regs[R_EAX] = do_freebsd_syscall(env,
syscall_nr,
arg1,
arg2,
arg3,
arg4,
arg5,
arg6,
arg7,
arg8);
} else { /* if (bsd_type == target_openbsd) */
env->regs[R_EAX] = do_openbsd_syscall(env,
env->regs[R_EAX],
env->regs[R_EBX],
env->regs[R_ECX],
env->regs[R_EDX],
env->regs[R_ESI],
env->regs[R_EDI],
env->regs[R_EBP]);
}
if (((abi_ulong)env->regs[R_EAX]) >= (abi_ulong)(-515)) {
env->regs[R_EAX] = -env->regs[R_EAX];
env->eflags |= CC_C;
} else {
env->eflags &= ~CC_C;
}
break;
#ifndef TARGET_ABI32
case EXCP_SYSCALL:
/* syscall from syscall instruction */
if (bsd_type == target_freebsd) {
env->regs[R_EAX] = do_freebsd_syscall(env,
env->regs[R_EAX],
env->regs[R_EDI],
env->regs[R_ESI],
env->regs[R_EDX],
env->regs[R_ECX],
env->regs[8],
env->regs[9], 0, 0);
} else { /* if (bsd_type == target_openbsd) */
env->regs[R_EAX] = do_openbsd_syscall(env,
env->regs[R_EAX],
env->regs[R_EDI],
env->regs[R_ESI],
env->regs[R_EDX],
env->regs[10],
env->regs[8],
env->regs[9]);
}
env->eip = env->exception_next_eip;
if (((abi_ulong)env->regs[R_EAX]) >= (abi_ulong)(-515)) {
env->regs[R_EAX] = -env->regs[R_EAX];
env->eflags |= CC_C;
} else {
env->eflags &= ~CC_C;
}
break;
#endif
case EXCP_INTERRUPT:
/* just indicate that signals should be handled asap */
break;
default:
pc = env->segs[R_CS].base + env->eip;
fprintf(stderr,
"qemu: 0x%08lx: unhandled CPU exception 0x%x - aborting\n",
(long)pc, trapnr);
abort();
}
process_pending_signals(env);
}
}
#endif
#ifdef TARGET_SPARC
#define SPARC64_STACK_BIAS 2047
/* #define DEBUG_WIN */
/*
* WARNING: dealing with register windows _is_ complicated. More info
* 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 * env->nwindows);
/*
* wrap handling : if cwp is on the last window, then we use the
* registers 'after' the end
*/
if (index < 8 && env->cwp == env->nwindows - 1) {
index += 16 * env->nwindows;
}
return index;
}
/* save the register window 'cwp1' */
static inline void save_window_offset(CPUSPARCState *env, int cwp1)
{
unsigned int i;
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" 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? */
put_user_ual(env->regbase[get_reg_index(env, cwp1, 8 + i)], sp_ptr);
sp_ptr += sizeof(abi_ulong);
}
}
static void save_window(CPUSPARCState *env)
{
#ifndef TARGET_SPARC64
unsigned int new_wim;
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
/*
* cansave is zero if the spill trap handler is triggered by `save` and
* nonzero if triggered by a `flushw`
*/
save_window_offset(env, cpu_cwp_dec(env, env->cwp - env->cansave - 2));
env->cansave++;
env->canrestore--;
#endif
}
static void restore_window(CPUSPARCState *env)
{
#ifndef TARGET_SPARC64
unsigned int new_wim;
#endif
unsigned int i, cwp1;
abi_ulong sp_ptr;
#ifndef TARGET_SPARC64
new_wim = ((env->wim << 1) | (env->wim >> (env->nwindows - 1))) &
((1LL << env->nwindows) - 1);
#endif
/* restore the invalid window */
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" 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);
}
#ifdef TARGET_SPARC64
env->canrestore++;
if (env->cleanwin < env->nwindows - 1) {
env->cleanwin++;
}
env->cansave--;
#else
env->wim = new_wim;
#endif
}
static void flush_windows(CPUSPARCState *env)
{
int offset, cwp1;
offset = 1;
for (;;) {
/* if restore would invoke restore_window(), then we can stop */
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 */
env->wim = 1 << cwp1;
#endif
#if defined(DEBUG_WIN)
printf("flush_windows: nb=%d\n", offset - 1);
#endif
}
void cpu_loop(CPUSPARCState *env)
{
CPUState *cs = env_cpu(env);
int trapnr, ret, syscall_nr;
/* target_siginfo_t info; */
while (1) {
cpu_exec_start(cs);
trapnr = cpu_exec(cs);
cpu_exec_end(cs);
process_queued_cpu_work(cs);
switch (trapnr) {
#ifndef TARGET_SPARC64
case 0x80:
#else
/* FreeBSD uses 0x141 for syscalls too */
case 0x141:
if (bsd_type != target_freebsd) {
goto badtrap;
}
/* fallthrough */
case 0x100:
#endif
syscall_nr = env->gregs[1];
if (bsd_type == target_freebsd)
ret = do_freebsd_syscall(env, syscall_nr,
env->regwptr[0], env->regwptr[1],
env->regwptr[2], env->regwptr[3],
env->regwptr[4], env->regwptr[5],
0, 0);
else if (bsd_type == target_netbsd)
ret = do_netbsd_syscall(env, syscall_nr,
env->regwptr[0], env->regwptr[1],
env->regwptr[2], env->regwptr[3],
env->regwptr[4], env->regwptr[5]);
else { /* if (bsd_type == target_openbsd) */
#if defined(TARGET_SPARC64)
syscall_nr &= ~(TARGET_OPENBSD_SYSCALL_G7RFLAG |
TARGET_OPENBSD_SYSCALL_G2RFLAG);
#endif
ret = do_openbsd_syscall(env, syscall_nr,
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)) {
ret = -ret;
#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
env->xcc |= PSR_CARRY;
#else
env->psr |= PSR_CARRY;
#endif
} else {
#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
env->xcc &= ~PSR_CARRY;
#else
env->psr &= ~PSR_CARRY;
#endif
}
env->regwptr[0] = ret;
/* next instruction */
#if defined(TARGET_SPARC64)
if (bsd_type == target_openbsd &&
env->gregs[1] & TARGET_OPENBSD_SYSCALL_G2RFLAG) {
env->pc = env->gregs[2];
env->npc = env->pc + 4;
} else if (bsd_type == target_openbsd &&
env->gregs[1] & TARGET_OPENBSD_SYSCALL_G7RFLAG) {
env->pc = env->gregs[7];
env->npc = env->pc + 4;
} else {
env->pc = env->npc;
env->npc = env->npc + 4;
}
#else
env->pc = env->npc;
env->npc = env->npc + 4;
#endif
break;
case 0x83: /* flush windows */
#ifdef TARGET_ABI32
case 0x103:
#endif
flush_windows(env);
/* next instruction */
env->pc = env->npc;
env->npc = env->npc + 4;
break;
#ifndef TARGET_SPARC64
case TT_WIN_OVF: /* window overflow */
save_window(env);
break;
case TT_WIN_UNF: /* window underflow */
restore_window(env);
break;
case TT_TFAULT:
case TT_DFAULT:
break;
#else
case TT_SPILL: /* window overflow */
save_window(env);
break;
case TT_FILL: /* window underflow */
restore_window(env);
break;
case TT_TFAULT:
case TT_DFAULT:
break;
#endif
case EXCP_INTERRUPT:
/* just indicate that signals should be handled asap */
break;
case EXCP_DEBUG:
{
gdb_handlesig(cs, TARGET_SIGTRAP);
}
break;
default:
#ifdef TARGET_SPARC64
badtrap:
#endif
printf("Unhandled trap: 0x%x\n", trapnr);
cpu_dump_state(cs, stderr, 0);
exit(1);
}
process_pending_signals(env);
}
}
#endif
static void usage(void)
{
printf("qemu-" TARGET_NAME " version " QEMU_FULL_VERSION
"\n" QEMU_COPYRIGHT "\n"
"usage: qemu-" TARGET_NAME " [options] program [arguments...]\n"
"BSD CPU emulator (compiled for %s emulation)\n"
"\n"
"Standard options:\n"
"-h print this help\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 help 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"
"-B address set guest_base address to address\n"
"-bsd type select emulated BSD type FreeBSD/NetBSD/OpenBSD (default)\n"
"\n"
"Debug options:\n"
"-d item1[,...] enable logging of specified items\n"
" (use '-d help' for a list of log items)\n"
"-D logfile write logs to 'logfile' (default stderr)\n"
"-p pagesize set the host page size to 'pagesize'\n"
"-singlestep always run in singlestep mode\n"
"-strace log system calls\n"
"-trace [[enable=]<pattern>][,events=<file>][,file=<file>]\n"
" specify tracing options\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"
"\n"
QEMU_HELP_BOTTOM "\n"
,
TARGET_NAME,
interp_prefix,
x86_stack_size);
exit(1);
}
THREAD CPUState *thread_cpu;
bool qemu_cpu_is_self(CPUState *cpu)
{
return thread_cpu == cpu;
}
void qemu_cpu_kick(CPUState *cpu)
{
cpu_exit(cpu);
}
/* 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)
{
const char *filename;
const char *cpu_model;
const char *cpu_type;
const char *log_file = NULL;
const char *log_mask = NULL;
struct target_pt_regs regs1, *regs = &regs1;
struct image_info info1, *info = &info1;
TaskState ts1, *ts = &ts1;
CPUArchState *env;
CPUState *cpu;
int optind, rv;
const char *r;
const char *gdbstub = NULL;
char **target_environ, **wrk;
envlist_t *envlist = NULL;
bsd_type = target_openbsd;
if (argc <= 1) {
usage();
}
error_init(argv[0]);
module_call_init(MODULE_INIT_TRACE);
qemu_init_cpu_list();
module_call_init(MODULE_INIT_QOM);
envlist = envlist_create();
/* add current environment into the list */
for (wrk = environ; *wrk != NULL; wrk++) {
(void) envlist_setenv(envlist, *wrk);
}
cpu_model = NULL;
qemu_add_opts(&qemu_trace_opts);
optind = 1;
for (;;) {
if (optind >= argc) {
break;
}
r = argv[optind];
if (r[0] != '-') {
break;
}
optind++;
r++;
if (!strcmp(r, "-")) {
break;
} else if (!strcmp(r, "d")) {
if (optind >= argc) {
break;
}
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);
envlist = envlist_create();
} else if (!strcmp(r, "U")) {
r = argv[optind++];
if (envlist_unsetenv(envlist, r) != 0) {
usage();
}
} else if (!strcmp(r, "s")) {
r = argv[optind++];
rv = qemu_strtoul(r, &r, 0, &x86_stack_size);
if (rv < 0 || x86_stack_size <= 0) {
usage();
}
if (*r == 'M') {
x86_stack_size *= MiB;
} else if (*r == 'k' || *r == 'K') {
x86_stack_size *= KiB;
}
} else if (!strcmp(r, "L")) {
interp_prefix = argv[optind++];
} else if (!strcmp(r, "p")) {
qemu_host_page_size = atoi(argv[optind++]);
if (qemu_host_page_size == 0 ||
(qemu_host_page_size & (qemu_host_page_size - 1)) != 0) {
fprintf(stderr, "page size must be a power of two\n");
exit(1);
}
} else if (!strcmp(r, "g")) {
gdbstub = g_strdup(argv[optind++]);
} else if (!strcmp(r, "r")) {
qemu_uname_release = argv[optind++];
} else if (!strcmp(r, "cpu")) {
cpu_model = argv[optind++];
if (is_help_option(cpu_model)) {
/* XXX: implement xxx_cpu_list for targets that still miss it */
#if defined(cpu_list)
cpu_list();
#endif
exit(1);
}
} else if (!strcmp(r, "B")) {
rv = qemu_strtoul(argv[optind++], NULL, 0, &guest_base);
if (rv < 0) {
usage();
}
have_guest_base = true;
} else if (!strcmp(r, "drop-ld-preload")) {
(void) envlist_unsetenv(envlist, "LD_PRELOAD");
} else if (!strcmp(r, "bsd")) {
if (!strcasecmp(argv[optind], "freebsd")) {
bsd_type = target_freebsd;
} else if (!strcasecmp(argv[optind], "netbsd")) {
bsd_type = target_netbsd;
} else if (!strcasecmp(argv[optind], "openbsd")) {
bsd_type = target_openbsd;
} else {
usage();
}
optind++;
} else if (!strcmp(r, "singlestep")) {
singlestep = 1;
} else if (!strcmp(r, "strace")) {
do_strace = 1;
} else if (!strcmp(r, "trace")) {
trace_opt_parse(optarg);
} else {
usage();
}
}
/* init debug */
qemu_log_needs_buffers();
qemu_set_log_filename(log_file, &error_fatal);
if (log_mask) {
int mask;
mask = qemu_str_to_log_mask(log_mask);
if (!mask) {
qemu_print_log_usage(stdout);
exit(1);
}
qemu_set_log(mask);
}
if (optind >= argc) {
usage();
}
filename = argv[optind];
if (!trace_init_backends()) {
exit(1);
}
trace_init_file();
/* Zero out regs */
memset(regs, 0, sizeof(struct target_pt_regs));
/* Zero out image_info */
memset(info, 0, sizeof(struct image_info));
/* Scan interp_prefix dir for replacement files. */
init_paths(interp_prefix);
if (cpu_model == NULL) {
#if defined(TARGET_I386)
#ifdef TARGET_X86_64
cpu_model = "qemu64";
#else
cpu_model = "qemu32";
#endif
#elif defined(TARGET_SPARC)
#ifdef TARGET_SPARC64
cpu_model = "TI UltraSparc II";
#else
cpu_model = "Fujitsu MB86904";
#endif
#else
cpu_model = "any";
#endif
}
cpu_type = parse_cpu_option(cpu_model);
/* init tcg before creating CPUs and to get qemu_host_page_size */
{
AccelClass *ac = ACCEL_GET_CLASS(current_accel());
accel_init_interfaces(ac);
ac->init_machine(NULL);
}
cpu = cpu_create(cpu_type);
env = cpu->env_ptr;
#if defined(TARGET_SPARC) || defined(TARGET_PPC)
cpu_reset(cpu);
#endif
thread_cpu = cpu;
if (getenv("QEMU_STRACE")) {
do_strace = 1;
}
target_environ = envlist_to_environ(envlist, NULL);
envlist_free(envlist);
/*
* Now that page sizes are configured in tcg_exec_init() we can do
* proper page alignment for guest_base.
*/
guest_base = HOST_PAGE_ALIGN(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.
*
* When user has explicitly set the quest base, we skip this
* test.
*/
if (!have_guest_base) {
FILE *fp;
fp = fopen("/proc/sys/vm/mmap_min_addr", "r");
if (fp != NULL) {
unsigned long tmp;
if (fscanf(fp, "%lu", &tmp) == 1) {
mmap_min_addr = tmp;
qemu_log_mask(CPU_LOG_PAGE, "host mmap_min_addr=0x%lx\n",
mmap_min_addr);
}
fclose(fp);
}
}
if (loader_exec(filename, argv + optind, target_environ, regs, info) != 0) {
printf("Error loading %s\n", filename);
_exit(1);
}
for (wrk = target_environ; *wrk; wrk++) {
g_free(*wrk);
}
g_free(target_environ);
if (qemu_loglevel_mask(CPU_LOG_PAGE)) {
qemu_log("guest_base %p\n", (void *)guest_base);
log_page_dump("binary load");
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();
/*
* 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);
tcg_region_init();
/* build Task State */
memset(ts, 0, sizeof(TaskState));
init_task_state(ts);
ts->info = info;
cpu->opaque = ts;
#if defined(TARGET_I386)
env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK;
env->hflags |= HF_PE_MASK | HF_CPL_MASK;
if (env->features[FEAT_1_EDX] & CPUID_SSE) {
env->cr[4] |= CR4_OSFXSR_MASK;
env->hflags |= HF_OSFXSR_MASK;
}
#ifndef TARGET_ABI32
/* enable 64 bit mode if possible */
if (!(env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM)) {
fprintf(stderr, "The selected x86 CPU does not support 64 bit mode\n");
exit(1);
}
env->cr[4] |= CR4_PAE_MASK;
env->efer |= MSR_EFER_LMA | MSR_EFER_LME;
env->hflags |= HF_LMA_MASK;
#endif
/* flags setup : we activate the IRQs by default as in user mode */
env->eflags |= IF_MASK;
/* linux register setup */
#ifndef TARGET_ABI32
env->regs[R_EAX] = regs->rax;
env->regs[R_EBX] = regs->rbx;
env->regs[R_ECX] = regs->rcx;
env->regs[R_EDX] = regs->rdx;
env->regs[R_ESI] = regs->rsi;
env->regs[R_EDI] = regs->rdi;
env->regs[R_EBP] = regs->rbp;
env->regs[R_ESP] = regs->rsp;
env->eip = regs->rip;
#else
env->regs[R_EAX] = regs->eax;
env->regs[R_EBX] = regs->ebx;
env->regs[R_ECX] = regs->ecx;
env->regs[R_EDX] = regs->edx;
env->regs[R_ESI] = regs->esi;
env->regs[R_EDI] = regs->edi;
env->regs[R_EBP] = regs->ebp;
env->regs[R_ESP] = regs->esp;
env->eip = regs->eip;
#endif
/* linux interrupt setup */
#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_untagged(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, 0);
set_idt(6, 0);
set_idt(7, 0);
set_idt(8, 0);
set_idt(9, 0);
set_idt(10, 0);
set_idt(11, 0);
set_idt(12, 0);
set_idt(13, 0);
set_idt(14, 0);
set_idt(15, 0);
set_idt(16, 0);
set_idt(17, 0);
set_idt(18, 0);
set_idt(19, 0);
set_idt(0x80, 3);
/* linux segment setup */
{
uint64_t *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_untagged(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 |
(3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
#else
/* 64 bit code segment */
write_dt(&gdt_table[__USER_CS >> 3], 0, 0xfffff,
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
DESC_L_MASK |
(3 << DESC_DPL_SHIFT) | (0xa << DESC_TYPE_SHIFT));
#endif
write_dt(&gdt_table[__USER_DS >> 3], 0, 0xfffff,
DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | DESC_S_MASK |
(3 << DESC_DPL_SHIFT) | (0x2 << DESC_TYPE_SHIFT));
}
cpu_x86_load_seg(env, R_CS, __USER_CS);
cpu_x86_load_seg(env, R_SS, __USER_DS);
#ifdef TARGET_ABI32
cpu_x86_load_seg(env, R_DS, __USER_DS);
cpu_x86_load_seg(env, R_ES, __USER_DS);
cpu_x86_load_seg(env, R_FS, __USER_DS);
cpu_x86_load_seg(env, R_GS, __USER_DS);
/* This hack makes Wine work... */
env->segs[R_FS].selector = 0;
#else
cpu_x86_load_seg(env, R_DS, 0);
cpu_x86_load_seg(env, R_ES, 0);
cpu_x86_load_seg(env, R_FS, 0);
cpu_x86_load_seg(env, R_GS, 0);
#endif
#elif defined(TARGET_SPARC)
{
int i;
env->pc = regs->pc;
env->npc = regs->npc;
env->y = regs->y;
for (i = 0; i < 8; i++) {
env->gregs[i] = regs->u_regs[i];
}
for (i = 0; i < 8; i++) {
env->regwptr[i] = regs->u_regs[i + 8];
}
}
#else
#error unsupported target CPU
#endif
if (gdbstub) {
gdbserver_start(gdbstub);
gdb_handlesig(cpu, 0);
}
cpu_loop(env);
/* never exits */
return 0;
}