* [Qemu-devel] [PATCH 2/7] linux-user: Implement new ARM 64 bit cmpxchg kernel helper
2011-09-09 9:32 [Qemu-devel] [PATCH 0/7] pending linux-user patches riku.voipio
2011-09-09 9:32 ` [Qemu-devel] [PATCH 1/7] linux-user: Fix initialization of the heap contents when allocating new pages riku.voipio
@ 2011-09-09 9:32 ` riku.voipio
2011-09-09 9:32 ` [Qemu-devel] [PATCH 3/7] linux-user: Exit with an error if we couldn't set up gdbserver riku.voipio
` (4 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: riku.voipio @ 2011-09-09 9:32 UTC (permalink / raw)
To: qemu-devel; +Cc: Dr. David Alan Gilbert
From: Dr. David Alan Gilbert <david.gilbert@linaro.org>
linux-user: Implement new ARM 64 bit cmpxchg kernel helper
Linux 3.1 will have a new kernel-page helper for ARM implementing
64 bit cmpxchg. Implement this helper in QEMU linux-user mode:
* Provide kernel helper emulation for 64bit cmpxchg
* Allow guest to object to guest offset to ensure it can map a page
* Populate page with kernel helper version
Signed-off-by: Riku Voipio <riku.voipio@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Dr. David Alan Gilbert <david.gilbert@linaro.org>
---
linux-user/elfload.c | 56 ++++++++++++++++++++++++++++++-
linux-user/main.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++-
linux-user/qemu.h | 6 +++
3 files changed, 148 insertions(+), 3 deletions(-)
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index 04e8e6e..8677bba 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -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;
diff --git a/linux-user/main.c b/linux-user/main.c
index 89a51d7..25cb4dd 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -456,6 +456,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 +572,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 +833,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;
@@ -3180,6 +3260,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 */
/*
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index 627c8b3..55ad9d8 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -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 */
--
1.7.4.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [Qemu-devel] [PATCH 6/7] linux-user: Implement setxattr/getxattr/removexattr syscalls
2011-09-09 9:32 [Qemu-devel] [PATCH 0/7] pending linux-user patches riku.voipio
` (4 preceding siblings ...)
2011-09-09 9:32 ` [Qemu-devel] [PATCH 5/7] linux-user: Verify MIPS syscall arguments riku.voipio
@ 2011-09-09 9:32 ` riku.voipio
2011-09-09 9:32 ` [Qemu-devel] [PATCH 7/7] introduce environment variables for all qemu-user options riku.voipio
6 siblings, 0 replies; 9+ messages in thread
From: riku.voipio @ 2011-09-09 9:32 UTC (permalink / raw)
To: qemu-devel; +Cc: An-Cheng Huang
From: An-Cheng Huang <ancheng@ubnt.com>
This patch implements the setxattr, getxattr, and removexattr syscalls
if CONFIG_ATTR is enabled.
Note that since libattr uses indirect syscalls for these, this change
depends on the fix for indirect syscall handling on MIPS.
Signed-off-by: Riku Voipio <riku.voipio@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: An-Cheng Huang <ancheng@ubnt.com>
---
linux-user/syscall.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 51 insertions(+), 3 deletions(-)
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index ae08c9e..39a34db 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.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
@@ -7642,22 +7645,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)
--
1.7.4.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [Qemu-devel] [PATCH 7/7] introduce environment variables for all qemu-user options
2011-09-09 9:32 [Qemu-devel] [PATCH 0/7] pending linux-user patches riku.voipio
` (5 preceding siblings ...)
2011-09-09 9:32 ` [Qemu-devel] [PATCH 6/7] linux-user: Implement setxattr/getxattr/removexattr syscalls riku.voipio
@ 2011-09-09 9:32 ` riku.voipio
6 siblings, 0 replies; 9+ messages in thread
From: riku.voipio @ 2011-09-09 9:32 UTC (permalink / raw)
To: qemu-devel; +Cc: Johannes Schauer
From: Johannes Schauer <j.schauer@email.de>
(Edits by Riku Voipio to apply to current HEAD)
Rework option parsing code for linux-user in a table-driven manner to allow
environment variables for all commandline options.
Also generate usage() output from option table.
Fix complains from checkpatch.pl, also have envlist global
Signed-off-by: Johannes Schauer <j.schauer@email.de>
Signed-off-by: Riku Voipio <riku.voipio@linaro.org>
---
linux-user/main.c | 555 +++++++++++++++++++++++++++++++++--------------------
1 files changed, 348 insertions(+), 207 deletions(-)
diff --git a/linux-user/main.c b/linux-user/main.c
index 0cc9148..186358b 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -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;
@@ -2879,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)
@@ -2965,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;
@@ -3019,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));
--
1.7.4.1
^ permalink raw reply related [flat|nested] 9+ messages in thread