mirror of
https://github.com/xemu-project/xemu.git
synced 2025-01-19 18:35:15 +00:00
Merge remote-tracking branch 'riku/linux-user-for-upstream' into staging
This commit is contained in:
commit
11ddeea91a
@ -332,6 +332,49 @@ enum
|
||||
ARM_HWCAP_ARM_VFPv3D16 = 1 << 13,
|
||||
};
|
||||
|
||||
#define TARGET_HAS_GUEST_VALIDATE_BASE
|
||||
/* We want the opportunity to check the suggested base */
|
||||
bool guest_validate_base(unsigned long guest_base)
|
||||
{
|
||||
unsigned long real_start, test_page_addr;
|
||||
|
||||
/* We need to check that we can force a fault on access to the
|
||||
* commpage at 0xffff0fxx
|
||||
*/
|
||||
test_page_addr = guest_base + (0xffff0f00 & qemu_host_page_mask);
|
||||
/* Note it needs to be writeable to let us initialise it */
|
||||
real_start = (unsigned long)
|
||||
mmap((void *)test_page_addr, qemu_host_page_size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
|
||||
/* If we can't map it then try another address */
|
||||
if (real_start == -1ul) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (real_start != test_page_addr) {
|
||||
/* OS didn't put the page where we asked - unmap and reject */
|
||||
munmap((void *)real_start, qemu_host_page_size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Leave the page mapped
|
||||
* Populate it (mmap should have left it all 0'd)
|
||||
*/
|
||||
|
||||
/* Kernel helper versions */
|
||||
__put_user(5, (uint32_t *)g2h(0xffff0ffcul));
|
||||
|
||||
/* Now it's populated make it RO */
|
||||
if (mprotect((void *)test_page_addr, qemu_host_page_size, PROT_READ)) {
|
||||
perror("Protecting guest commpage");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
return 1; /* All good */
|
||||
}
|
||||
|
||||
#define ELF_HWCAP (ARM_HWCAP_ARM_SWP | ARM_HWCAP_ARM_HALF \
|
||||
| ARM_HWCAP_ARM_THUMB | ARM_HWCAP_ARM_FAST_MULT \
|
||||
| ARM_HWCAP_ARM_FPA | ARM_HWCAP_ARM_VFP \
|
||||
@ -1309,6 +1352,14 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
|
||||
return sp;
|
||||
}
|
||||
|
||||
#ifndef TARGET_HAS_GUEST_VALIDATE_BASE
|
||||
/* If the guest doesn't have a validation function just agree */
|
||||
bool guest_validate_base(unsigned long guest_base)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void probe_guest_base(const char *image_name,
|
||||
abi_ulong loaddr, abi_ulong hiaddr)
|
||||
{
|
||||
@ -1345,7 +1396,9 @@ static void probe_guest_base(const char *image_name,
|
||||
if (real_start == (unsigned long)-1) {
|
||||
goto exit_perror;
|
||||
}
|
||||
if (real_start == host_start) {
|
||||
guest_base = real_start - loaddr;
|
||||
if ((real_start == host_start) &&
|
||||
guest_validate_base(guest_base)) {
|
||||
break;
|
||||
}
|
||||
/* That address didn't work. Unmap and try a different one.
|
||||
@ -1368,7 +1421,6 @@ static void probe_guest_base(const char *image_name,
|
||||
qemu_log("Relocating guest address space from 0x"
|
||||
TARGET_ABI_FMT_lx " to 0x%lx\n",
|
||||
loaddr, real_start);
|
||||
guest_base = real_start - loaddr;
|
||||
}
|
||||
return;
|
||||
|
||||
|
@ -39,6 +39,11 @@
|
||||
char *exec_path;
|
||||
|
||||
int singlestep;
|
||||
const char *filename;
|
||||
const char *argv0;
|
||||
int gdbstub_port;
|
||||
envlist_t *envlist;
|
||||
const char *cpu_model;
|
||||
unsigned long mmap_min_addr;
|
||||
#if defined(CONFIG_USE_GUEST_BASE)
|
||||
unsigned long guest_base;
|
||||
@ -46,6 +51,8 @@ int have_guest_base;
|
||||
unsigned long reserved_va;
|
||||
#endif
|
||||
|
||||
static void usage(void);
|
||||
|
||||
static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
|
||||
const char *qemu_uname_release = CONFIG_UNAME_RELEASE;
|
||||
|
||||
@ -456,6 +463,83 @@ void cpu_loop(CPUX86State *env)
|
||||
|
||||
#ifdef TARGET_ARM
|
||||
|
||||
/*
|
||||
* See the Linux kernel's Documentation/arm/kernel_user_helpers.txt
|
||||
* Input:
|
||||
* r0 = pointer to oldval
|
||||
* r1 = pointer to newval
|
||||
* r2 = pointer to target value
|
||||
*
|
||||
* Output:
|
||||
* r0 = 0 if *ptr was changed, non-0 if no exchange happened
|
||||
* C set if *ptr was changed, clear if no exchange happened
|
||||
*
|
||||
* Note segv's in kernel helpers are a bit tricky, we can set the
|
||||
* data address sensibly but the PC address is just the entry point.
|
||||
*/
|
||||
static void arm_kernel_cmpxchg64_helper(CPUARMState *env)
|
||||
{
|
||||
uint64_t oldval, newval, val;
|
||||
uint32_t addr, cpsr;
|
||||
target_siginfo_t info;
|
||||
|
||||
/* Based on the 32 bit code in do_kernel_trap */
|
||||
|
||||
/* XXX: This only works between threads, not between processes.
|
||||
It's probably possible to implement this with native host
|
||||
operations. However things like ldrex/strex are much harder so
|
||||
there's not much point trying. */
|
||||
start_exclusive();
|
||||
cpsr = cpsr_read(env);
|
||||
addr = env->regs[2];
|
||||
|
||||
if (get_user_u64(oldval, env->regs[0])) {
|
||||
env->cp15.c6_data = env->regs[0];
|
||||
goto segv;
|
||||
};
|
||||
|
||||
if (get_user_u64(newval, env->regs[1])) {
|
||||
env->cp15.c6_data = env->regs[1];
|
||||
goto segv;
|
||||
};
|
||||
|
||||
if (get_user_u64(val, addr)) {
|
||||
env->cp15.c6_data = addr;
|
||||
goto segv;
|
||||
}
|
||||
|
||||
if (val == oldval) {
|
||||
val = newval;
|
||||
|
||||
if (put_user_u64(val, addr)) {
|
||||
env->cp15.c6_data = addr;
|
||||
goto segv;
|
||||
};
|
||||
|
||||
env->regs[0] = 0;
|
||||
cpsr |= CPSR_C;
|
||||
} else {
|
||||
env->regs[0] = -1;
|
||||
cpsr &= ~CPSR_C;
|
||||
}
|
||||
cpsr_write(env, cpsr, CPSR_C);
|
||||
end_exclusive();
|
||||
return;
|
||||
|
||||
segv:
|
||||
end_exclusive();
|
||||
/* We get the PC of the entry address - which is as good as anything,
|
||||
on a real kernel what you get depends on which mode it uses. */
|
||||
info.si_signo = SIGSEGV;
|
||||
info.si_errno = 0;
|
||||
/* XXX: check env->error_code */
|
||||
info.si_code = TARGET_SEGV_MAPERR;
|
||||
info._sifields._sigfault._addr = env->cp15.c6_data;
|
||||
queue_signal(env, info.si_signo, &info);
|
||||
|
||||
end_exclusive();
|
||||
}
|
||||
|
||||
/* Handle a jump to the kernel code page. */
|
||||
static int
|
||||
do_kernel_trap(CPUARMState *env)
|
||||
@ -495,6 +579,10 @@ do_kernel_trap(CPUARMState *env)
|
||||
case 0xffff0fe0: /* __kernel_get_tls */
|
||||
env->regs[0] = env->cp15.c13_tls2;
|
||||
break;
|
||||
case 0xffff0f60: /* __kernel_cmpxchg64 */
|
||||
arm_kernel_cmpxchg64_helper(env);
|
||||
break;
|
||||
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
@ -752,7 +840,6 @@ void cpu_loop(CPUARMState *env)
|
||||
goto do_segv;
|
||||
case EXCP_DATA_ABORT:
|
||||
addr = env->cp15.c6_data;
|
||||
goto do_segv;
|
||||
do_segv:
|
||||
{
|
||||
info.si_signo = SIGSEGV;
|
||||
@ -1669,7 +1756,7 @@ void cpu_loop(CPUPPCState *env)
|
||||
#define MIPS_SYS(name, args) args,
|
||||
|
||||
static const uint8_t mips_syscall_args[] = {
|
||||
MIPS_SYS(sys_syscall , 0) /* 4000 */
|
||||
MIPS_SYS(sys_syscall , 8) /* 4000 */
|
||||
MIPS_SYS(sys_exit , 1)
|
||||
MIPS_SYS(sys_fork , 0)
|
||||
MIPS_SYS(sys_read , 3)
|
||||
@ -2090,11 +2177,22 @@ void cpu_loop(CPUMIPSState *env)
|
||||
sp_reg = env->active_tc.gpr[29];
|
||||
switch (nb_args) {
|
||||
/* these arguments are taken from the stack */
|
||||
/* FIXME - what to do if get_user() fails? */
|
||||
case 8: get_user_ual(arg8, sp_reg + 28);
|
||||
case 7: get_user_ual(arg7, sp_reg + 24);
|
||||
case 6: get_user_ual(arg6, sp_reg + 20);
|
||||
case 5: get_user_ual(arg5, sp_reg + 16);
|
||||
case 8:
|
||||
if ((ret = get_user_ual(arg8, sp_reg + 28)) != 0) {
|
||||
goto done_syscall;
|
||||
}
|
||||
case 7:
|
||||
if ((ret = get_user_ual(arg7, sp_reg + 24)) != 0) {
|
||||
goto done_syscall;
|
||||
}
|
||||
case 6:
|
||||
if ((ret = get_user_ual(arg6, sp_reg + 20)) != 0) {
|
||||
goto done_syscall;
|
||||
}
|
||||
case 5:
|
||||
if ((ret = get_user_ual(arg5, sp_reg + 16)) != 0) {
|
||||
goto done_syscall;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -2105,6 +2203,7 @@ void cpu_loop(CPUMIPSState *env)
|
||||
env->active_tc.gpr[7],
|
||||
arg5, arg6, arg7, arg8);
|
||||
}
|
||||
done_syscall:
|
||||
if (ret == -TARGET_QEMU_ESIGRETURN) {
|
||||
/* Returning from a successful sigreturn syscall.
|
||||
Avoid clobbering register state. */
|
||||
@ -2787,57 +2886,6 @@ void cpu_loop(CPUS390XState *env)
|
||||
|
||||
#endif /* TARGET_S390X */
|
||||
|
||||
static void version(void)
|
||||
{
|
||||
printf("qemu-" TARGET_ARCH " version " QEMU_VERSION QEMU_PKGVERSION
|
||||
", Copyright (c) 2003-2008 Fabrice Bellard\n");
|
||||
}
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
version();
|
||||
printf("usage: qemu-" TARGET_ARCH " [options] program [arguments...]\n"
|
||||
"Linux CPU emulator (compiled for %s emulation)\n"
|
||||
"\n"
|
||||
"Standard options:\n"
|
||||
"-h print this help\n"
|
||||
"-version display version information and exit\n"
|
||||
"-g port wait gdb connection to port\n"
|
||||
"-L path set the elf interpreter prefix (default=%s)\n"
|
||||
"-s size set the stack size in bytes (default=%ld)\n"
|
||||
"-cpu model select CPU (-cpu ? for list)\n"
|
||||
"-drop-ld-preload drop LD_PRELOAD for target process\n"
|
||||
"-E var=value sets/modifies targets environment variable(s)\n"
|
||||
"-U var unsets targets environment variable(s)\n"
|
||||
"-0 argv0 forces target process argv[0] to be argv0\n"
|
||||
#if defined(CONFIG_USE_GUEST_BASE)
|
||||
"-B address set guest_base address to address\n"
|
||||
"-R size reserve size bytes for guest virtual address space\n"
|
||||
#endif
|
||||
"\n"
|
||||
"Debug options:\n"
|
||||
"-d options activate log (logfile=%s)\n"
|
||||
"-p pagesize set the host page size to 'pagesize'\n"
|
||||
"-singlestep always run in singlestep mode\n"
|
||||
"-strace log system calls\n"
|
||||
"\n"
|
||||
"Environment variables:\n"
|
||||
"QEMU_STRACE Print system calls and arguments similar to the\n"
|
||||
" 'strace' program. Enable by setting to any value.\n"
|
||||
"You can use -E and -U options to set/unset environment variables\n"
|
||||
"for target process. It is possible to provide several variables\n"
|
||||
"by repeating the option. For example:\n"
|
||||
" -E var1=val2 -E var2=val2 -U LD_PRELOAD -U LD_DEBUG\n"
|
||||
"Note that if you provide several changes to single variable\n"
|
||||
"last change will stay in effect.\n"
|
||||
,
|
||||
TARGET_ARCH,
|
||||
interp_prefix,
|
||||
guest_stack_size,
|
||||
DEBUG_LOGFILE);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
THREAD CPUState *thread_env;
|
||||
|
||||
void task_settid(TaskState *ts)
|
||||
@ -2873,26 +2921,358 @@ void init_task_state(TaskState *ts)
|
||||
}
|
||||
ts->sigqueue_table[i].next = NULL;
|
||||
}
|
||||
|
||||
|
||||
static void handle_arg_help(const char *arg)
|
||||
{
|
||||
usage();
|
||||
}
|
||||
|
||||
static void handle_arg_log(const char *arg)
|
||||
{
|
||||
int mask;
|
||||
const CPULogItem *item;
|
||||
|
||||
mask = cpu_str_to_log_mask(arg);
|
||||
if (!mask) {
|
||||
printf("Log items (comma separated):\n");
|
||||
for (item = cpu_log_items; item->mask != 0; item++) {
|
||||
printf("%-10s %s\n", item->name, item->help);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
cpu_set_log(mask);
|
||||
}
|
||||
|
||||
static void handle_arg_set_env(const char *arg)
|
||||
{
|
||||
char *r, *p, *token;
|
||||
r = p = strdup(arg);
|
||||
while ((token = strsep(&p, ",")) != NULL) {
|
||||
if (envlist_setenv(envlist, token) != 0) {
|
||||
usage();
|
||||
}
|
||||
}
|
||||
free(r);
|
||||
}
|
||||
|
||||
static void handle_arg_unset_env(const char *arg)
|
||||
{
|
||||
char *r, *p, *token;
|
||||
r = p = strdup(arg);
|
||||
while ((token = strsep(&p, ",")) != NULL) {
|
||||
if (envlist_unsetenv(envlist, token) != 0) {
|
||||
usage();
|
||||
}
|
||||
}
|
||||
free(r);
|
||||
}
|
||||
|
||||
static void handle_arg_argv0(const char *arg)
|
||||
{
|
||||
argv0 = strdup(arg);
|
||||
}
|
||||
|
||||
static void handle_arg_stack_size(const char *arg)
|
||||
{
|
||||
char *p;
|
||||
guest_stack_size = strtoul(arg, &p, 0);
|
||||
if (guest_stack_size == 0) {
|
||||
usage();
|
||||
}
|
||||
|
||||
if (*p == 'M') {
|
||||
guest_stack_size *= 1024 * 1024;
|
||||
} else if (*p == 'k' || *p == 'K') {
|
||||
guest_stack_size *= 1024;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_arg_ld_prefix(const char *arg)
|
||||
{
|
||||
interp_prefix = strdup(arg);
|
||||
}
|
||||
|
||||
static void handle_arg_pagesize(const char *arg)
|
||||
{
|
||||
qemu_host_page_size = atoi(arg);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_arg_gdb(const char *arg)
|
||||
{
|
||||
gdbstub_port = atoi(arg);
|
||||
}
|
||||
|
||||
static void handle_arg_uname(const char *arg)
|
||||
{
|
||||
qemu_uname_release = strdup(arg);
|
||||
}
|
||||
|
||||
static void handle_arg_cpu(const char *arg)
|
||||
{
|
||||
cpu_model = strdup(arg);
|
||||
if (cpu_model == NULL || strcmp(cpu_model, "?") == 0) {
|
||||
/* XXX: implement xxx_cpu_list for targets that still miss it */
|
||||
#if defined(cpu_list_id)
|
||||
cpu_list_id(stdout, &fprintf, "");
|
||||
#elif defined(cpu_list)
|
||||
cpu_list(stdout, &fprintf); /* deprecated */
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_USE_GUEST_BASE)
|
||||
static void handle_arg_guest_base(const char *arg)
|
||||
{
|
||||
guest_base = strtol(arg, NULL, 0);
|
||||
have_guest_base = 1;
|
||||
}
|
||||
|
||||
static void handle_arg_reserved_va(const char *arg)
|
||||
{
|
||||
char *p;
|
||||
int shift = 0;
|
||||
reserved_va = strtoul(arg, &p, 0);
|
||||
switch (*p) {
|
||||
case 'k':
|
||||
case 'K':
|
||||
shift = 10;
|
||||
break;
|
||||
case 'M':
|
||||
shift = 20;
|
||||
break;
|
||||
case 'G':
|
||||
shift = 30;
|
||||
break;
|
||||
}
|
||||
if (shift) {
|
||||
unsigned long unshifted = reserved_va;
|
||||
p++;
|
||||
reserved_va <<= shift;
|
||||
if (((reserved_va >> shift) != unshifted)
|
||||
#if HOST_LONG_BITS > TARGET_VIRT_ADDR_SPACE_BITS
|
||||
|| (reserved_va > (1ul << TARGET_VIRT_ADDR_SPACE_BITS))
|
||||
#endif
|
||||
) {
|
||||
fprintf(stderr, "Reserved virtual address too big\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (*p) {
|
||||
fprintf(stderr, "Unrecognised -R size suffix '%s'\n", p);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void handle_arg_singlestep(const char *arg)
|
||||
{
|
||||
singlestep = 1;
|
||||
}
|
||||
|
||||
static void handle_arg_strace(const char *arg)
|
||||
{
|
||||
do_strace = 1;
|
||||
}
|
||||
|
||||
static void handle_arg_version(const char *arg)
|
||||
{
|
||||
printf("qemu-" TARGET_ARCH " version " QEMU_VERSION QEMU_PKGVERSION
|
||||
", Copyright (c) 2003-2008 Fabrice Bellard\n");
|
||||
}
|
||||
|
||||
struct qemu_argument {
|
||||
const char *argv;
|
||||
const char *env;
|
||||
bool has_arg;
|
||||
void (*handle_opt)(const char *arg);
|
||||
const char *example;
|
||||
const char *help;
|
||||
};
|
||||
|
||||
struct qemu_argument arg_table[] = {
|
||||
{"h", "", false, handle_arg_help,
|
||||
"", "print this help"},
|
||||
{"g", "QEMU_GDB", true, handle_arg_gdb,
|
||||
"port", "wait gdb connection to 'port'"},
|
||||
{"L", "QEMU_LD_PREFIX", true, handle_arg_ld_prefix,
|
||||
"path", "set the elf interpreter prefix to 'path'"},
|
||||
{"s", "QEMU_STACK_SIZE", true, handle_arg_stack_size,
|
||||
"size", "set the stack size to 'size' bytes"},
|
||||
{"cpu", "QEMU_CPU", true, handle_arg_cpu,
|
||||
"model", "select CPU (-cpu ? for list)"},
|
||||
{"E", "QEMU_SET_ENV", true, handle_arg_set_env,
|
||||
"var=value", "sets targets environment variable (see below)"},
|
||||
{"U", "QEMU_UNSET_ENV", true, handle_arg_unset_env,
|
||||
"var", "unsets targets environment variable (see below)"},
|
||||
{"0", "QEMU_ARGV0", true, handle_arg_argv0,
|
||||
"argv0", "forces target process argv[0] to be 'argv0'"},
|
||||
{"r", "QEMU_UNAME", true, handle_arg_uname,
|
||||
"uname", "set qemu uname release string to 'uname'"},
|
||||
#if defined(CONFIG_USE_GUEST_BASE)
|
||||
{"B", "QEMU_GUEST_BASE", true, handle_arg_guest_base,
|
||||
"address", "set guest_base address to 'address'"},
|
||||
{"R", "QEMU_RESERVED_VA", true, handle_arg_reserved_va,
|
||||
"size", "reserve 'size' bytes for guest virtual address space"},
|
||||
#endif
|
||||
{"d", "QEMU_LOG", true, handle_arg_log,
|
||||
"options", "activate log"},
|
||||
{"p", "QEMU_PAGESIZE", true, handle_arg_pagesize,
|
||||
"pagesize", "set the host page size to 'pagesize'"},
|
||||
{"singlestep", "QEMU_SINGLESTEP", false, handle_arg_singlestep,
|
||||
"", "run in singlestep mode"},
|
||||
{"strace", "QEMU_STRACE", false, handle_arg_strace,
|
||||
"", "log system calls"},
|
||||
{"version", "QEMU_VERSION", false, handle_arg_version,
|
||||
"", "log system calls"},
|
||||
{NULL, NULL, false, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
struct qemu_argument *arginfo;
|
||||
int maxarglen;
|
||||
int maxenvlen;
|
||||
|
||||
printf("usage: qemu-" TARGET_ARCH " [options] program [arguments...]\n"
|
||||
"Linux CPU emulator (compiled for " TARGET_ARCH " emulation)\n"
|
||||
"\n"
|
||||
"Options and associated environment variables:\n"
|
||||
"\n");
|
||||
|
||||
maxarglen = maxenvlen = 0;
|
||||
|
||||
for (arginfo = arg_table; arginfo->handle_opt != NULL; arginfo++) {
|
||||
if (strlen(arginfo->env) > maxenvlen) {
|
||||
maxenvlen = strlen(arginfo->env);
|
||||
}
|
||||
if (strlen(arginfo->argv) > maxarglen) {
|
||||
maxarglen = strlen(arginfo->argv);
|
||||
}
|
||||
}
|
||||
|
||||
printf("%-*s%-*sDescription\n", maxarglen+3, "Argument",
|
||||
maxenvlen+1, "Env-variable");
|
||||
|
||||
for (arginfo = arg_table; arginfo->handle_opt != NULL; arginfo++) {
|
||||
if (arginfo->has_arg) {
|
||||
printf("-%s %-*s %-*s %s\n", arginfo->argv,
|
||||
(int)(maxarglen-strlen(arginfo->argv)), arginfo->example,
|
||||
maxenvlen, arginfo->env, arginfo->help);
|
||||
} else {
|
||||
printf("-%-*s %-*s %s\n", maxarglen+1, arginfo->argv,
|
||||
maxenvlen, arginfo->env,
|
||||
arginfo->help);
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n"
|
||||
"Defaults:\n"
|
||||
"QEMU_LD_PREFIX = %s\n"
|
||||
"QEMU_STACK_SIZE = %ld byte\n"
|
||||
"QEMU_LOG = %s\n",
|
||||
interp_prefix,
|
||||
guest_stack_size,
|
||||
DEBUG_LOGFILE);
|
||||
|
||||
printf("\n"
|
||||
"You can use -E and -U options or the QEMU_SET_ENV and\n"
|
||||
"QEMU_UNSET_ENV environment variables to set and unset\n"
|
||||
"environment variables for the target process.\n"
|
||||
"It is possible to provide several variables by separating them\n"
|
||||
"by commas in getsubopt(3) style. Additionally it is possible to\n"
|
||||
"provide the -E and -U options multiple times.\n"
|
||||
"The following lines are equivalent:\n"
|
||||
" -E var1=val2 -E var2=val2 -U LD_PRELOAD -U LD_DEBUG\n"
|
||||
" -E var1=val2,var2=val2 -U LD_PRELOAD,LD_DEBUG\n"
|
||||
" QEMU_SET_ENV=var1=val2,var2=val2 QEMU_UNSET_ENV=LD_PRELOAD,LD_DEBUG\n"
|
||||
"Note that if you provide several changes to a single variable\n"
|
||||
"the last change will stay in effect.\n");
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int parse_args(int argc, char **argv)
|
||||
{
|
||||
const char *r;
|
||||
int optind;
|
||||
struct qemu_argument *arginfo;
|
||||
|
||||
for (arginfo = arg_table; arginfo->handle_opt != NULL; arginfo++) {
|
||||
if (arginfo->env == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
r = getenv(arginfo->env);
|
||||
if (r != NULL) {
|
||||
arginfo->handle_opt(r);
|
||||
}
|
||||
}
|
||||
|
||||
optind = 1;
|
||||
for (;;) {
|
||||
if (optind >= argc) {
|
||||
break;
|
||||
}
|
||||
r = argv[optind];
|
||||
if (r[0] != '-') {
|
||||
break;
|
||||
}
|
||||
optind++;
|
||||
r++;
|
||||
if (!strcmp(r, "-")) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (arginfo = arg_table; arginfo->handle_opt != NULL; arginfo++) {
|
||||
if (!strcmp(r, arginfo->argv)) {
|
||||
if (optind >= argc) {
|
||||
usage();
|
||||
}
|
||||
|
||||
arginfo->handle_opt(argv[optind]);
|
||||
|
||||
if (arginfo->has_arg) {
|
||||
optind++;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* no option matched the current argv */
|
||||
if (arginfo->handle_opt == NULL) {
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
if (optind >= argc) {
|
||||
usage();
|
||||
}
|
||||
|
||||
filename = argv[optind];
|
||||
exec_path = argv[optind];
|
||||
|
||||
return optind;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
const char *filename;
|
||||
const char *cpu_model;
|
||||
const char *log_file = DEBUG_LOGFILE;
|
||||
const char *log_mask = NULL;
|
||||
struct target_pt_regs regs1, *regs = ®s1;
|
||||
struct image_info info1, *info = &info1;
|
||||
struct linux_binprm bprm;
|
||||
TaskState *ts;
|
||||
CPUState *env;
|
||||
int optind;
|
||||
const char *r;
|
||||
int gdbstub_port = 0;
|
||||
char **target_environ, **wrk;
|
||||
char **target_argv;
|
||||
int target_argc;
|
||||
envlist_t *envlist = NULL;
|
||||
const char *argv0 = NULL;
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
@ -2927,156 +3307,9 @@ int main(int argc, char **argv, char **envp)
|
||||
cpudef_setup(); /* parse cpu definitions in target config file (TBD) */
|
||||
#endif
|
||||
|
||||
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);
|
||||
if ((envlist = envlist_create()) == NULL) {
|
||||
(void) fprintf(stderr, "Unable to allocate envlist\n");
|
||||
exit(1);
|
||||
}
|
||||
} else if (!strcmp(r, "U")) {
|
||||
r = argv[optind++];
|
||||
if (envlist_unsetenv(envlist, r) != 0)
|
||||
usage();
|
||||
} else if (!strcmp(r, "0")) {
|
||||
r = argv[optind++];
|
||||
argv0 = r;
|
||||
} else if (!strcmp(r, "s")) {
|
||||
if (optind >= argc)
|
||||
break;
|
||||
r = argv[optind++];
|
||||
guest_stack_size = strtoul(r, (char **)&r, 0);
|
||||
if (guest_stack_size == 0)
|
||||
usage();
|
||||
if (*r == 'M')
|
||||
guest_stack_size *= 1024 * 1024;
|
||||
else if (*r == 'k' || *r == 'K')
|
||||
guest_stack_size *= 1024;
|
||||
} else if (!strcmp(r, "L")) {
|
||||
interp_prefix = argv[optind++];
|
||||
} else if (!strcmp(r, "p")) {
|
||||
if (optind >= argc)
|
||||
break;
|
||||
qemu_host_page_size = atoi(argv[optind++]);
|
||||
if (qemu_host_page_size == 0 ||
|
||||
(qemu_host_page_size & (qemu_host_page_size - 1)) != 0) {
|
||||
fprintf(stderr, "page size must be a power of two\n");
|
||||
exit(1);
|
||||
}
|
||||
} else if (!strcmp(r, "g")) {
|
||||
if (optind >= argc)
|
||||
break;
|
||||
gdbstub_port = atoi(argv[optind++]);
|
||||
} else if (!strcmp(r, "r")) {
|
||||
qemu_uname_release = argv[optind++];
|
||||
} else if (!strcmp(r, "cpu")) {
|
||||
cpu_model = argv[optind++];
|
||||
if (cpu_model == NULL || strcmp(cpu_model, "?") == 0) {
|
||||
/* XXX: implement xxx_cpu_list for targets that still miss it */
|
||||
#if defined(cpu_list_id)
|
||||
cpu_list_id(stdout, &fprintf, "");
|
||||
#elif defined(cpu_list)
|
||||
cpu_list(stdout, &fprintf); /* deprecated */
|
||||
#endif
|
||||
exit(1);
|
||||
}
|
||||
#if defined(CONFIG_USE_GUEST_BASE)
|
||||
} else if (!strcmp(r, "B")) {
|
||||
guest_base = strtol(argv[optind++], NULL, 0);
|
||||
have_guest_base = 1;
|
||||
} else if (!strcmp(r, "R")) {
|
||||
char *p;
|
||||
int shift = 0;
|
||||
reserved_va = strtoul(argv[optind++], &p, 0);
|
||||
switch (*p) {
|
||||
case 'k':
|
||||
case 'K':
|
||||
shift = 10;
|
||||
break;
|
||||
case 'M':
|
||||
shift = 20;
|
||||
break;
|
||||
case 'G':
|
||||
shift = 30;
|
||||
break;
|
||||
}
|
||||
if (shift) {
|
||||
unsigned long unshifted = reserved_va;
|
||||
p++;
|
||||
reserved_va <<= shift;
|
||||
if (((reserved_va >> shift) != unshifted)
|
||||
#if HOST_LONG_BITS > TARGET_VIRT_ADDR_SPACE_BITS
|
||||
|| (reserved_va > (1ul << TARGET_VIRT_ADDR_SPACE_BITS))
|
||||
#endif
|
||||
) {
|
||||
fprintf(stderr, "Reserved virtual address too big\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (*p) {
|
||||
fprintf(stderr, "Unrecognised -R size suffix '%s'\n", p);
|
||||
exit(1);
|
||||
}
|
||||
#endif
|
||||
} else if (!strcmp(r, "drop-ld-preload")) {
|
||||
(void) envlist_unsetenv(envlist, "LD_PRELOAD");
|
||||
} else if (!strcmp(r, "singlestep")) {
|
||||
singlestep = 1;
|
||||
} else if (!strcmp(r, "strace")) {
|
||||
do_strace = 1;
|
||||
} else if (!strcmp(r, "version")) {
|
||||
version();
|
||||
exit(0);
|
||||
} else {
|
||||
usage();
|
||||
}
|
||||
}
|
||||
/* init debug */
|
||||
cpu_set_log_filename(log_file);
|
||||
if (log_mask) {
|
||||
int mask;
|
||||
const CPULogItem *item;
|
||||
|
||||
mask = cpu_str_to_log_mask(log_mask);
|
||||
if (!mask) {
|
||||
printf("Log items (comma separated):\n");
|
||||
for (item = cpu_log_items; item->mask != 0; item++) {
|
||||
printf("%-10s %s\n", item->name, item->help);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
cpu_set_log(mask);
|
||||
}
|
||||
|
||||
if (optind >= argc) {
|
||||
usage();
|
||||
}
|
||||
filename = argv[optind];
|
||||
exec_path = argv[optind];
|
||||
optind = parse_args(argc, argv);
|
||||
|
||||
/* Zero out regs */
|
||||
memset(regs, 0, sizeof(struct target_pt_regs));
|
||||
@ -3180,6 +3413,13 @@ int main(int argc, char **argv, char **envp)
|
||||
}
|
||||
qemu_log("Reserved 0x%lx bytes of guest address space\n", reserved_va);
|
||||
}
|
||||
|
||||
if (reserved_va || have_guest_base) {
|
||||
if (!guest_validate_base(guest_base)) {
|
||||
fprintf(stderr, "Guest base/Reserved VA rejected by guest code\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_USE_GUEST_BASE */
|
||||
|
||||
/*
|
||||
@ -3568,7 +3808,11 @@ int main(int argc, char **argv, char **envp)
|
||||
#endif
|
||||
|
||||
if (gdbstub_port) {
|
||||
gdbserver_start (gdbstub_port);
|
||||
if (gdbserver_start(gdbstub_port) < 0) {
|
||||
fprintf(stderr, "qemu: could not open gdbserver on port %d\n",
|
||||
gdbstub_port);
|
||||
exit(1);
|
||||
}
|
||||
gdb_handlesig(env, 0);
|
||||
}
|
||||
cpu_loop(env);
|
||||
|
@ -202,6 +202,12 @@ int get_osversion(void);
|
||||
void fork_start(void);
|
||||
void fork_end(int child);
|
||||
|
||||
/* Return true if the proposed guest_base is suitable for the guest.
|
||||
* The guest code may leave a page mapped and populate it if the
|
||||
* address is suitable.
|
||||
*/
|
||||
bool guest_validate_base(unsigned long guest_base);
|
||||
|
||||
#include "qemu-log.h"
|
||||
|
||||
/* strace.c */
|
||||
|
@ -70,6 +70,9 @@ int __clone2(int (*fn)(void *), void *child_stack_base,
|
||||
#ifdef CONFIG_EPOLL
|
||||
#include <sys/epoll.h>
|
||||
#endif
|
||||
#ifdef CONFIG_ATTR
|
||||
#include <attr/xattr.h>
|
||||
#endif
|
||||
|
||||
#define termios host_termios
|
||||
#define winsize host_winsize
|
||||
@ -796,6 +799,15 @@ abi_long do_brk(abi_ulong new_brk)
|
||||
MAP_ANON|MAP_PRIVATE, 0, 0));
|
||||
|
||||
if (mapped_addr == brk_page) {
|
||||
/* Heap contents are initialized to zero, as for anonymous
|
||||
* mapped pages. Technically the new pages are already
|
||||
* initialized to zero since they *are* anonymous mapped
|
||||
* pages, however we have to take care with the contents that
|
||||
* come from the remaining part of the previous page: it may
|
||||
* contains garbage data due to a previous heap usage (grown
|
||||
* then shrunken). */
|
||||
memset(g2h(target_brk), 0, brk_page - target_brk);
|
||||
|
||||
target_brk = new_brk;
|
||||
brk_page = HOST_PAGE_ALIGN(target_brk);
|
||||
DEBUGF_BRK("%#010x (mapped_addr == brk_page)\n", target_brk);
|
||||
@ -7632,22 +7644,67 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
||||
#endif
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_ATTR
|
||||
#ifdef TARGET_NR_setxattr
|
||||
case TARGET_NR_setxattr:
|
||||
case TARGET_NR_lsetxattr:
|
||||
case TARGET_NR_fsetxattr:
|
||||
case TARGET_NR_getxattr:
|
||||
case TARGET_NR_lgetxattr:
|
||||
case TARGET_NR_fgetxattr:
|
||||
case TARGET_NR_listxattr:
|
||||
case TARGET_NR_llistxattr:
|
||||
case TARGET_NR_flistxattr:
|
||||
case TARGET_NR_removexattr:
|
||||
case TARGET_NR_lremovexattr:
|
||||
case TARGET_NR_fremovexattr:
|
||||
ret = -TARGET_EOPNOTSUPP;
|
||||
break;
|
||||
case TARGET_NR_setxattr:
|
||||
{
|
||||
void *p, *n, *v;
|
||||
p = lock_user_string(arg1);
|
||||
n = lock_user_string(arg2);
|
||||
v = lock_user(VERIFY_READ, arg3, arg4, 1);
|
||||
if (p && n && v) {
|
||||
ret = get_errno(setxattr(p, n, v, arg4, arg5));
|
||||
} else {
|
||||
ret = -TARGET_EFAULT;
|
||||
}
|
||||
unlock_user(p, arg1, 0);
|
||||
unlock_user(n, arg2, 0);
|
||||
unlock_user(v, arg3, 0);
|
||||
}
|
||||
break;
|
||||
case TARGET_NR_getxattr:
|
||||
{
|
||||
void *p, *n, *v;
|
||||
p = lock_user_string(arg1);
|
||||
n = lock_user_string(arg2);
|
||||
v = lock_user(VERIFY_WRITE, arg3, arg4, 0);
|
||||
if (p && n && v) {
|
||||
ret = get_errno(getxattr(p, n, v, arg4));
|
||||
} else {
|
||||
ret = -TARGET_EFAULT;
|
||||
}
|
||||
unlock_user(p, arg1, 0);
|
||||
unlock_user(n, arg2, 0);
|
||||
unlock_user(v, arg3, arg4);
|
||||
}
|
||||
break;
|
||||
case TARGET_NR_removexattr:
|
||||
{
|
||||
void *p, *n;
|
||||
p = lock_user_string(arg1);
|
||||
n = lock_user_string(arg2);
|
||||
if (p && n) {
|
||||
ret = get_errno(removexattr(p, n));
|
||||
} else {
|
||||
ret = -TARGET_EFAULT;
|
||||
}
|
||||
unlock_user(p, arg1, 0);
|
||||
unlock_user(n, arg2, 0);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#endif /* CONFIG_ATTR */
|
||||
#ifdef TARGET_NR_set_thread_area
|
||||
case TARGET_NR_set_thread_area:
|
||||
#if defined(TARGET_MIPS)
|
||||
|
Loading…
x
Reference in New Issue
Block a user