You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
375 lines
13 KiB
Diff
375 lines
13 KiB
Diff
diff --git a/linux-user/main.c b/linux-user/main.c
|
|
index 96be354897..bc4c8864c7 100644
|
|
--- a/linux-user/main.c
|
|
+++ b/linux-user/main.c
|
|
@@ -124,6 +124,7 @@ static void usage(int exitcode);
|
|
|
|
static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX;
|
|
const char *qemu_uname_release;
|
|
+const char *qemu_execve_path;
|
|
|
|
#if !defined(TARGET_DEFAULT_STACK_SIZE)
|
|
/* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
|
|
@@ -370,6 +371,11 @@ static void handle_arg_guest_base(const char *arg)
|
|
have_guest_base = true;
|
|
}
|
|
|
|
+static void handle_arg_execve(const char *arg)
|
|
+{
|
|
+ qemu_execve_path = strdup(arg);
|
|
+}
|
|
+
|
|
static void handle_arg_reserved_va(const char *arg)
|
|
{
|
|
char *p;
|
|
@@ -486,6 +492,8 @@ static const struct qemu_argument arg_table[] = {
|
|
"uname", "set qemu uname release string to 'uname'"},
|
|
{"B", "QEMU_GUEST_BASE", true, handle_arg_guest_base,
|
|
"address", "set guest_base address to 'address'"},
|
|
+ {"execve", "QEMU_EXECVE", true, handle_arg_execve,
|
|
+ "", "use this interpreter when a process calls execve()"},
|
|
{"R", "QEMU_RESERVED_VA", true, handle_arg_reserved_va,
|
|
"size", "reserve 'size' bytes for guest virtual address space"},
|
|
{"d", "QEMU_LOG", true, handle_arg_log,
|
|
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
|
|
index 9353268cc1..790744adfb 100644
|
|
--- a/linux-user/syscall.c
|
|
+++ b/linux-user/syscall.c
|
|
@@ -125,6 +125,7 @@
|
|
#include <libdrm/drm.h>
|
|
#include <libdrm/i915_drm.h>
|
|
#endif
|
|
+#include <linux/binfmts.h>
|
|
#include "linux_loop.h"
|
|
#include "uname.h"
|
|
|
|
@@ -8680,6 +8681,137 @@ ssize_t do_guest_readlink(const char *pathname, char *buf, size_t bufsiz)
|
|
return ret;
|
|
}
|
|
|
|
+/* qemu_execve() Must return target values and target errnos.
|
|
+ *
|
|
+ * Although execve() is not an interruptible syscall it is
|
|
+ * a special case where we must use the safe_syscall wrapper:
|
|
+ * if we allow a signal to happen before we make the host
|
|
+ * syscall then we will 'lose' it, because at the point of
|
|
+ * execve the process leaves QEMU's control. So we use the
|
|
+ * safe syscall wrapper to ensure that we either take the
|
|
+ * signal as a guest signal, or else it does not happen
|
|
+ * before the execve completes and makes it the other
|
|
+ * program's problem.
|
|
+ */
|
|
+static abi_long qemu_execve(int dirfd, char *filename, char *argv[],
|
|
+ char *envp[], int flags, bool is_execveat)
|
|
+{
|
|
+ char *i_arg = NULL, *i_name = NULL;
|
|
+ char **new_argp;
|
|
+ int argc, fd, ret, i, offset = 5;
|
|
+ char *cp;
|
|
+ char buf[BINPRM_BUF_SIZE];
|
|
+
|
|
+ /* normal execve case */
|
|
+ if (qemu_execve_path == NULL || *qemu_execve_path == 0) {
|
|
+ return get_errno(is_execveat ?
|
|
+ safe_execveat(dirfd, filename, argv, envp, flags) :
|
|
+ safe_execve(filename, argv, envp));
|
|
+ }
|
|
+
|
|
+ if (is_execveat) {
|
|
+ return -TARGET_ENOSYS;
|
|
+ }
|
|
+
|
|
+ for (argc = 0; argv[argc] != NULL; argc++) {
|
|
+ /* nothing */ ;
|
|
+ }
|
|
+
|
|
+ fd = open(filename, O_RDONLY);
|
|
+ if (fd == -1) {
|
|
+ return get_errno(fd);
|
|
+ }
|
|
+
|
|
+ ret = read(fd, buf, BINPRM_BUF_SIZE);
|
|
+ if (ret == -1) {
|
|
+ close(fd);
|
|
+ return get_errno(ret);
|
|
+ }
|
|
+
|
|
+ /* if we have less than 2 bytes, we can guess it is not executable */
|
|
+ if (ret < 2) {
|
|
+ close(fd);
|
|
+ return -host_to_target_errno(ENOEXEC);
|
|
+ }
|
|
+
|
|
+ close(fd);
|
|
+
|
|
+ /* adapted from the kernel
|
|
+ * https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/binfmt_script.c
|
|
+ */
|
|
+ if ((buf[0] == '#') && (buf[1] == '!')) {
|
|
+ /*
|
|
+ * This section does the #! interpretation.
|
|
+ * Sorta complicated, but hopefully it will work. -TYT
|
|
+ */
|
|
+
|
|
+ buf[BINPRM_BUF_SIZE - 1] = '\0';
|
|
+ cp = strchr(buf, '\n');
|
|
+ if (cp == NULL) {
|
|
+ cp = buf + BINPRM_BUF_SIZE - 1;
|
|
+ }
|
|
+ *cp = '\0';
|
|
+ while (cp > buf) {
|
|
+ cp--;
|
|
+ if ((*cp == ' ') || (*cp == '\t')) {
|
|
+ *cp = '\0';
|
|
+ } else {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ for (cp = buf + 2; (*cp == ' ') || (*cp == '\t'); cp++) {
|
|
+ /* nothing */ ;
|
|
+ }
|
|
+ if (*cp == '\0') {
|
|
+ return -ENOEXEC; /* No interpreter name found */
|
|
+ }
|
|
+ i_name = cp;
|
|
+ i_arg = NULL;
|
|
+ for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) {
|
|
+ /* nothing */ ;
|
|
+ }
|
|
+ while ((*cp == ' ') || (*cp == '\t')) {
|
|
+ *cp++ = '\0';
|
|
+ }
|
|
+ if (*cp) {
|
|
+ i_arg = cp;
|
|
+ }
|
|
+
|
|
+ if (i_arg) {
|
|
+ offset += 2;
|
|
+ } else {
|
|
+ offset += 1;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ new_argp = alloca((argc + offset + 1) * sizeof(void *));
|
|
+
|
|
+ /* Copy the original arguments with offset */
|
|
+ for (i = 0; i < argc; i++) {
|
|
+ new_argp[i + offset] = argv[i];
|
|
+ }
|
|
+
|
|
+ new_argp[0] = strdup(qemu_execve_path);
|
|
+ new_argp[1] = strdup("--execve");
|
|
+ new_argp[2] = strdup(qemu_execve_path);
|
|
+ new_argp[3] = strdup("-0");
|
|
+ new_argp[offset] = filename;
|
|
+ new_argp[argc + offset] = NULL;
|
|
+
|
|
+ if (i_name) {
|
|
+ new_argp[4] = i_name;
|
|
+ new_argp[5] = i_name;
|
|
+
|
|
+ if (i_arg) {
|
|
+ new_argp[6] = i_arg;
|
|
+ }
|
|
+ } else {
|
|
+ new_argp[4] = argv[0];
|
|
+ }
|
|
+
|
|
+ return get_errno(safe_execve(qemu_execve_path, new_argp, envp));
|
|
+}
|
|
+
|
|
static int do_execv(CPUArchState *cpu_env, int dirfd,
|
|
abi_long pathname, abi_long guest_argp,
|
|
abi_long guest_envp, int flags, bool is_execveat)
|
|
@@ -8745,17 +8877,6 @@ static int do_execv(CPUArchState *cpu_env, int dirfd,
|
|
}
|
|
*q = NULL;
|
|
|
|
- /*
|
|
- * Although execve() is not an interruptible syscall it is
|
|
- * a special case where we must use the safe_syscall wrapper:
|
|
- * if we allow a signal to happen before we make the host
|
|
- * syscall then we will 'lose' it, because at the point of
|
|
- * execve the process leaves QEMU's control. So we use the
|
|
- * safe syscall wrapper to ensure that we either take the
|
|
- * signal as a guest signal, or else it does not happen
|
|
- * before the execve completes and makes it the other
|
|
- * program's problem.
|
|
- */
|
|
p = lock_user_string(pathname);
|
|
if (!p) {
|
|
goto execve_efault;
|
|
@@ -8765,10 +8886,7 @@ static int do_execv(CPUArchState *cpu_env, int dirfd,
|
|
if (is_proc_myself(p, "exe")) {
|
|
exe = exec_path;
|
|
}
|
|
- ret = is_execveat
|
|
- ? safe_execveat(dirfd, exe, argp, envp, flags)
|
|
- : safe_execve(exe, argp, envp);
|
|
- ret = get_errno(ret);
|
|
+ ret = qemu_execve(dirfd, exe, argp, envp, flags, is_execveat);
|
|
|
|
unlock_user(p, pathname, 0);
|
|
|
|
diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h
|
|
index c63ef45fc7..125e499370 100644
|
|
--- a/linux-user/user-internals.h
|
|
+++ b/linux-user/user-internals.h
|
|
@@ -28,6 +28,7 @@ void init_task_state(TaskState *ts);
|
|
void task_settid(TaskState *);
|
|
void stop_all_tasks(void);
|
|
extern const char *qemu_uname_release;
|
|
+extern const char *qemu_execve_path;
|
|
extern unsigned long mmap_min_addr;
|
|
|
|
typedef struct IOCTLEntry IOCTLEntry;
|
|
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
|
|
index 97ad229d8b..408db3365d 100644
|
|
--- a/target/i386/cpu.c
|
|
+++ b/target/i386/cpu.c
|
|
@@ -1759,6 +1759,7 @@ typedef struct X86CPUDefinition {
|
|
int family;
|
|
int model;
|
|
int stepping;
|
|
+ int brand_id;
|
|
FeatureWordArray features;
|
|
const char *model_id;
|
|
const CPUCaches *const cache_info;
|
|
@@ -2444,6 +2445,54 @@ static const X86CPUDefinition builtin_x86_defs[] = {
|
|
.xlevel = 0,
|
|
.model_id = "",
|
|
},
|
|
+ {
|
|
+ /* http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0000F4A_K8_Clawhammer_CPUID.txt */
|
|
+ .name = "athlon-64",
|
|
+ .level = 1,
|
|
+ .vendor = CPUID_VENDOR_AMD,
|
|
+ .family = 15,
|
|
+ .model = 4,
|
|
+ .stepping = 10,
|
|
+ .features[FEAT_1_EDX] =
|
|
+ CPUID_FP87 | CPUID_VME | CPUID_DE | CPUID_PSE |
|
|
+ CPUID_TSC | CPUID_MSR | CPUID_PAE | CPUID_MCE |
|
|
+ CPUID_CX8 | CPUID_APIC | CPUID_SEP |
|
|
+ CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV |
|
|
+ CPUID_PAT | CPUID_PSE36 | /*no: CPUID_PN |*/ CPUID_CLFLUSH |
|
|
+ /*no: CPUID_DTS | CPUID_ACPI |*/ CPUID_MMX |
|
|
+ CPUID_FXSR | CPUID_SSE | CPUID_SSE2,
|
|
+ .features[FEAT_8000_0001_EDX] =
|
|
+ (CPUID_EXT2_FPU | CPUID_EXT2_VME | CPUID_EXT2_DE | CPUID_EXT2_PSE |
|
|
+ CPUID_EXT2_TSC | CPUID_EXT2_MSR | CPUID_EXT2_PAE | CPUID_EXT2_MCE |
|
|
+ CPUID_EXT2_CX8 | CPUID_EXT2_APIC | CPUID_EXT2_SYSCALL |
|
|
+ CPUID_EXT2_MTRR | CPUID_EXT2_PGE | CPUID_EXT2_MCA | CPUID_EXT2_CMOV |
|
|
+ CPUID_EXT2_PAT | CPUID_EXT2_PSE36 | /*no: CPUID_EXT2_MP */
|
|
+ CPUID_EXT2_NX | CPUID_EXT2_MMXEXT | CPUID_EXT2_MMX |
|
|
+ CPUID_EXT2_FXSR | /*no: CPUID_EXT2_FFXSR | CPUID_EXT2_PDPE1G | CPUID_EXT2_RTDSCP*/
|
|
+ CPUID_EXT2_LM | CPUID_EXT2_3DNOWEXT | CPUID_EXT2_3DNOW) &
|
|
+ ~CPUID_EXT2_AMD_ALIASES,
|
|
+ /* todo cache info 0x80000005-6 */
|
|
+ .features[FEAT_8000_0007_EDX] = 0x0f, /*??*/
|
|
+ /*.phys_bits = 0x00003028,*/
|
|
+ .xlevel = 0x80000018,
|
|
+ .brand_id = 0x106,
|
|
+ .model_id = "AMD Athlon(tm) 64 Processor 2800+",
|
|
+ },
|
|
+ {
|
|
+ .name = "u3-64bit-minimal",
|
|
+ .level = 1,
|
|
+ .vendor = CPUID_VENDOR_AMD,
|
|
+ .family = 15,
|
|
+ .model = 4,
|
|
+ .stepping = 10,
|
|
+ .features[FEAT_1_EDX] =
|
|
+ PENTIUM2_FEATURES | PPRO_FEATURES | CPUID_CLFLUSH /*sse2?*/,
|
|
+ .features[FEAT_8000_0001_EDX] =
|
|
+ /* NX: amd: since amd64, intel: P4 Prescott+ */
|
|
+ CPUID_EXT2_SYSCALL | CPUID_EXT2_NX | CPUID_EXT2_LM,
|
|
+ .xlevel = 0x80000018,
|
|
+ .model_id = "Fuck off",
|
|
+ },
|
|
{
|
|
.name = "athlon",
|
|
.level = 2,
|
|
@@ -5165,6 +5214,41 @@ static void x86_cpuid_version_set_stepping(Object *obj, Visitor *v,
|
|
env->cpuid_version |= value & 0xf;
|
|
}
|
|
|
|
+static void x86_cpuid_version_get_brand_id(Object *obj, Visitor *v,
|
|
+ const char *name, void *opaque,
|
|
+ Error **errp)
|
|
+{
|
|
+ X86CPU *cpu = X86_CPU(obj);
|
|
+ CPUX86State *env = &cpu->env;
|
|
+ int64_t value;
|
|
+
|
|
+ value = env->cpuid_brand_id;
|
|
+ visit_type_int(v, name, &value, errp);
|
|
+}
|
|
+
|
|
+static void x86_cpuid_version_set_brand_id(Object *obj, Visitor *v,
|
|
+ const char *name, void *opaque,
|
|
+ Error **errp)
|
|
+{
|
|
+ X86CPU *cpu = X86_CPU(obj);
|
|
+ CPUX86State *env = &cpu->env;
|
|
+ const int64_t min = 0;
|
|
+ const int64_t max = 0xffff;
|
|
+ int64_t value;
|
|
+
|
|
+ if (!visit_type_int(v, name, &value, errp)) {
|
|
+ return;
|
|
+ }
|
|
+ if (value < min || value > max) {
|
|
+ error_setg(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE, "",
|
|
+ name ? name : "null", value, min, max);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ env->cpuid_brand_id = value;
|
|
+}
|
|
+
|
|
+
|
|
static char *x86_cpuid_get_vendor(Object *obj, Error **errp)
|
|
{
|
|
X86CPU *cpu = X86_CPU(obj);
|
|
@@ -5877,6 +5961,8 @@ static void x86_cpu_load_model(X86CPU *cpu, X86CPUModel *model)
|
|
&error_abort);
|
|
object_property_set_str(OBJECT(cpu), "model-id", def->model_id,
|
|
&error_abort);
|
|
+ object_property_set_int(OBJECT(cpu), "brand-id", def->brand_id,
|
|
+ &error_abort);
|
|
for (w = 0; w < FEATURE_WORDS; w++) {
|
|
env->features[w] = def->features[w];
|
|
}
|
|
@@ -6488,7 +6574,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count,
|
|
break;
|
|
case 0x80000001:
|
|
*eax = env->cpuid_version;
|
|
- *ebx = 0;
|
|
+ *ebx = env->cpuid_brand_id;
|
|
*ecx = env->features[FEAT_8000_0001_ECX];
|
|
*edx = env->features[FEAT_8000_0001_EDX];
|
|
|
|
@@ -7982,6 +8068,9 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data)
|
|
object_class_property_add(oc, "stepping", "int",
|
|
x86_cpuid_version_get_stepping,
|
|
x86_cpuid_version_set_stepping, NULL, NULL);
|
|
+ object_class_property_add(oc, "brand-id", "int",
|
|
+ x86_cpuid_version_get_brand_id,
|
|
+ x86_cpuid_version_set_brand_id, NULL, NULL);
|
|
object_class_property_add_str(oc, "vendor",
|
|
x86_cpuid_get_vendor,
|
|
x86_cpuid_set_vendor);
|
|
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
|
|
index e0771a1043..2b9a1aef24 100644
|
|
--- a/target/i386/cpu.h
|
|
+++ b/target/i386/cpu.h
|
|
@@ -1803,6 +1803,7 @@ typedef struct CPUArchState {
|
|
uint32_t cpuid_vendor2;
|
|
uint32_t cpuid_vendor3;
|
|
uint32_t cpuid_version;
|
|
+ uint32_t cpuid_brand_id;
|
|
FeatureWordArray features;
|
|
/* Features that were explicitly enabled/disabled */
|
|
FeatureWordArray user_features;
|