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.
562 lines
21 KiB
Diff
562 lines
21 KiB
Diff
diff --git a/linux-user/main.c b/linux-user/main.c
|
|
index a17fed045b..999b65bfc1 100644
|
|
--- a/linux-user/main.c
|
|
+++ b/linux-user/main.c
|
|
@@ -123,6 +123,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
|
|
@@ -362,6 +363,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;
|
|
@@ -464,6 +470,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/mmap.c b/linux-user/mmap.c
|
|
index 10f5079331..d2c6919f60 100644
|
|
--- a/linux-user/mmap.c
|
|
+++ b/linux-user/mmap.c
|
|
@@ -321,7 +321,7 @@ static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size,
|
|
* It must be called with mmap_lock() held.
|
|
* Return -1 if error.
|
|
*/
|
|
-abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong align)
|
|
+abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong align, bool map_32bit)
|
|
{
|
|
void *ptr, *prev;
|
|
abi_ulong addr;
|
|
@@ -356,7 +356,7 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong align)
|
|
* - shmat() with SHM_REMAP flag
|
|
*/
|
|
ptr = mmap(g2h_untagged(addr), size, PROT_NONE,
|
|
- MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0);
|
|
+ MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE|(map_32bit?MAP_32BIT:0), -1, 0);
|
|
|
|
/* ENOMEM, if host address space has no memory */
|
|
if (ptr == MAP_FAILED) {
|
|
@@ -367,7 +367,7 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong align)
|
|
This is used to modify the search algorithm below. */
|
|
repeat = (ptr == prev ? repeat + 1 : 0);
|
|
|
|
- if (h2g_valid(ptr + size - 1)) {
|
|
+ if (map_32bit ? h2g(ptr+size-1) == (uint32_t) (ptrdiff_t) (ptr+size-1): h2g_valid(ptr + size - 1)) {
|
|
addr = h2g(ptr);
|
|
|
|
if ((addr & (align - 1)) == 0) {
|
|
@@ -484,7 +484,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot,
|
|
if (!(flags & MAP_FIXED)) {
|
|
host_len = len + offset - host_offset;
|
|
host_len = HOST_PAGE_ALIGN(host_len);
|
|
- start = mmap_find_vma(real_start, host_len, TARGET_PAGE_SIZE);
|
|
+ start = mmap_find_vma(real_start, host_len, TARGET_PAGE_SIZE, flags & MAP_32BIT);
|
|
if (start == (abi_ulong)-1) {
|
|
errno = ENOMEM;
|
|
goto fail;
|
|
@@ -799,7 +799,7 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
|
|
} else if (flags & MREMAP_MAYMOVE) {
|
|
abi_ulong mmap_start;
|
|
|
|
- mmap_start = mmap_find_vma(0, new_size, TARGET_PAGE_SIZE);
|
|
+ mmap_start = mmap_find_vma(0, new_size, TARGET_PAGE_SIZE, flags & MAP_32BIT);
|
|
|
|
if (mmap_start == -1) {
|
|
errno = ENOMEM;
|
|
diff --git a/linux-user/strace.c b/linux-user/strace.c
|
|
index 9ae5a812cd..8ab56ff3b6 100644
|
|
--- a/linux-user/strace.c
|
|
+++ b/linux-user/strace.c
|
|
@@ -1086,6 +1086,9 @@ UNUSED static struct flags mmap_flags[] = {
|
|
#ifdef MAP_POPULATE
|
|
FLAG_TARGET(MAP_POPULATE),
|
|
#endif
|
|
+#ifdef TARGET_MAP_32BIT
|
|
+ FLAG_TARGET(MAP_32BIT),
|
|
+#endif
|
|
#ifdef TARGET_MAP_UNINITIALIZED
|
|
FLAG_TARGET(MAP_UNINITIALIZED),
|
|
#endif
|
|
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
|
|
index 24b25759be..9ce518bb7a 100644
|
|
--- a/linux-user/syscall.c
|
|
+++ b/linux-user/syscall.c
|
|
@@ -166,6 +166,7 @@ struct file_clone_range {
|
|
#include <libdrm/drm.h>
|
|
#include <libdrm/i915_drm.h>
|
|
#endif
|
|
+#include <linux/binfmts.h>
|
|
#include "linux_loop.h"
|
|
#include "uname.h"
|
|
|
|
@@ -1058,7 +1059,7 @@ static inline rlim_t target_to_host_rlim(abi_ulong target_rlim)
|
|
{
|
|
abi_ulong target_rlim_swap;
|
|
rlim_t result;
|
|
-
|
|
+
|
|
target_rlim_swap = tswapal(target_rlim);
|
|
if (target_rlim_swap == TARGET_RLIM_INFINITY)
|
|
return RLIM_INFINITY;
|
|
@@ -1066,7 +1067,7 @@ static inline rlim_t target_to_host_rlim(abi_ulong target_rlim)
|
|
result = target_rlim_swap;
|
|
if (target_rlim_swap != (rlim_t)result)
|
|
return RLIM_INFINITY;
|
|
-
|
|
+
|
|
return result;
|
|
}
|
|
#endif
|
|
@@ -1076,13 +1077,13 @@ static inline abi_ulong host_to_target_rlim(rlim_t rlim)
|
|
{
|
|
abi_ulong target_rlim_swap;
|
|
abi_ulong result;
|
|
-
|
|
+
|
|
if (rlim == RLIM_INFINITY || rlim != (abi_long)rlim)
|
|
target_rlim_swap = TARGET_RLIM_INFINITY;
|
|
else
|
|
target_rlim_swap = rlim;
|
|
result = tswapal(target_rlim_swap);
|
|
-
|
|
+
|
|
return result;
|
|
}
|
|
#endif
|
|
@@ -1809,9 +1810,9 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh,
|
|
abi_ulong target_cmsg_addr;
|
|
struct target_cmsghdr *target_cmsg, *target_cmsg_start;
|
|
socklen_t space = 0;
|
|
-
|
|
+
|
|
msg_controllen = tswapal(target_msgh->msg_controllen);
|
|
- if (msg_controllen < sizeof (struct target_cmsghdr))
|
|
+ if (msg_controllen < sizeof (struct target_cmsghdr))
|
|
goto the_end;
|
|
target_cmsg_addr = tswapal(target_msgh->msg_control);
|
|
target_cmsg = lock_user(VERIFY_READ, target_cmsg_addr, msg_controllen, 1);
|
|
@@ -1897,7 +1898,7 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh,
|
|
socklen_t space = 0;
|
|
|
|
msg_controllen = tswapal(target_msgh->msg_controllen);
|
|
- if (msg_controllen < sizeof (struct target_cmsghdr))
|
|
+ if (msg_controllen < sizeof (struct target_cmsghdr))
|
|
goto the_end;
|
|
target_cmsg_addr = tswapal(target_msgh->msg_control);
|
|
target_cmsg = lock_user(VERIFY_WRITE, target_cmsg_addr, msg_controllen, 0);
|
|
@@ -4591,7 +4592,7 @@ static inline abi_ulong do_shmat(CPUArchState *cpu_env,
|
|
abi_ulong mmap_start;
|
|
|
|
/* In order to use the host shmat, we need to honor host SHMLBA. */
|
|
- mmap_start = mmap_find_vma(0, shm_info.shm_segsz, MAX(SHMLBA, shmlba));
|
|
+ mmap_start = mmap_find_vma(0, shm_info.shm_segsz, MAX(SHMLBA, shmlba), false);
|
|
|
|
if (mmap_start == -1) {
|
|
errno = ENOMEM;
|
|
@@ -6015,6 +6016,9 @@ static const StructEntry struct_termios_def = {
|
|
};
|
|
|
|
static const bitmask_transtbl mmap_flags_tbl[] = {
|
|
+#ifdef TARGET_MAP_32BIT
|
|
+ { TARGET_MAP_32BIT, TARGET_MAP_32BIT, MAP_32BIT, MAP_32BIT },
|
|
+#endif
|
|
{ TARGET_MAP_SHARED, TARGET_MAP_SHARED, MAP_SHARED, MAP_SHARED },
|
|
{ TARGET_MAP_PRIVATE, TARGET_MAP_PRIVATE, MAP_PRIVATE, MAP_PRIVATE },
|
|
{ TARGET_MAP_FIXED, TARGET_MAP_FIXED, MAP_FIXED, MAP_FIXED },
|
|
@@ -6209,7 +6213,7 @@ abi_long do_set_thread_area(CPUX86State *env, abi_ulong ptr)
|
|
}
|
|
unlock_user_struct(target_ldt_info, ptr, 1);
|
|
|
|
- if (ldt_info.entry_number < TARGET_GDT_ENTRY_TLS_MIN ||
|
|
+ if (ldt_info.entry_number < TARGET_GDT_ENTRY_TLS_MIN ||
|
|
ldt_info.entry_number > TARGET_GDT_ENTRY_TLS_MAX)
|
|
return -TARGET_EINVAL;
|
|
seg_32bit = ldt_info.flags & 1;
|
|
@@ -6287,7 +6291,7 @@ static abi_long do_get_thread_area(CPUX86State *env, abi_ulong ptr)
|
|
lp = (uint32_t *)(gdt_table + idx);
|
|
entry_1 = tswap32(lp[0]);
|
|
entry_2 = tswap32(lp[1]);
|
|
-
|
|
+
|
|
read_exec_only = ((entry_2 >> 9) & 1) ^ 1;
|
|
contents = (entry_2 >> 10) & 3;
|
|
seg_not_present = ((entry_2 >> 15) & 1) ^ 1;
|
|
@@ -6303,8 +6307,8 @@ static abi_long do_get_thread_area(CPUX86State *env, abi_ulong ptr)
|
|
(read_exec_only << 3) | (limit_in_pages << 4) |
|
|
(seg_not_present << 5) | (useable << 6) | (lm << 7);
|
|
limit = (entry_1 & 0xffff) | (entry_2 & 0xf0000);
|
|
- base_addr = (entry_1 >> 16) |
|
|
- (entry_2 & 0xff000000) |
|
|
+ base_addr = (entry_1 >> 16) |
|
|
+ (entry_2 & 0xff000000) |
|
|
((entry_2 & 0xff) << 16);
|
|
target_ldt_info->base_addr = tswapal(base_addr);
|
|
target_ldt_info->limit = tswap32(limit);
|
|
@@ -8378,6 +8382,131 @@ static target_timer_t get_timer_id(abi_long arg)
|
|
return timerid;
|
|
}
|
|
|
|
+/* 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(char *filename, char *argv[],
|
|
+ char *envp[])
|
|
+{
|
|
+ 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(safe_execve(filename, argv, envp));
|
|
+ }
|
|
+
|
|
+ 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 target_to_host_cpu_mask(unsigned long *host_mask,
|
|
size_t host_size,
|
|
abi_ulong target_addr,
|
|
@@ -8922,20 +9051,10 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
|
|
|
|
if (!(p = lock_user_string(arg1)))
|
|
goto execve_efault;
|
|
- /* 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.
|
|
- */
|
|
if (is_proc_myself(p, "exe")) {
|
|
- ret = get_errno(safe_execve(exec_path, argp, envp));
|
|
+ ret = qemu_execve(exec_path, argp, envp);
|
|
} else {
|
|
- ret = get_errno(safe_execve(p, argp, envp));
|
|
+ ret = qemu_execve(p, argp, envp);
|
|
}
|
|
unlock_user(p, arg1, 0);
|
|
|
|
@@ -11473,7 +11592,7 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
|
|
return get_errno(fchown(arg1, low2highuid(arg2), low2highgid(arg3)));
|
|
#if defined(TARGET_NR_fchownat)
|
|
case TARGET_NR_fchownat:
|
|
- if (!(p = lock_user_string(arg2)))
|
|
+ if (!(p = lock_user_string(arg2)))
|
|
return -TARGET_EFAULT;
|
|
ret = get_errno(fchownat(arg1, p, low2highuid(arg3),
|
|
low2highgid(arg4), arg5));
|
|
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
|
|
index 77864de57f..c0bcfacd42 100644
|
|
--- a/linux-user/syscall_defs.h
|
|
+++ b/linux-user/syscall_defs.h
|
|
@@ -1330,6 +1330,7 @@ struct target_winsize {
|
|
#else
|
|
#define TARGET_MAP_FIXED 0x10 /* Interpret addr exactly */
|
|
#define TARGET_MAP_ANONYMOUS 0x20 /* don't use a file */
|
|
+#define TARGET_MAP_32BIT 0x40
|
|
#define TARGET_MAP_GROWSDOWN 0x0100 /* stack-like segment */
|
|
#define TARGET_MAP_DENYWRITE 0x0800 /* ETXTBSY */
|
|
#define TARGET_MAP_EXECUTABLE 0x1000 /* mark it as an executable */
|
|
diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h
|
|
index 0280e76add..7586971298 100644
|
|
--- a/linux-user/user-internals.h
|
|
+++ b/linux-user/user-internals.h
|
|
@@ -27,6 +27,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/linux-user/user-mmap.h b/linux-user/user-mmap.h
|
|
index 480ce1c114..042bd299dd 100644
|
|
--- a/linux-user/user-mmap.h
|
|
+++ b/linux-user/user-mmap.h
|
|
@@ -28,7 +28,7 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size,
|
|
abi_long target_madvise(abi_ulong start, abi_ulong len_in, int advice);
|
|
extern unsigned long last_brk;
|
|
extern abi_ulong mmap_next_start;
|
|
-abi_ulong mmap_find_vma(abi_ulong, abi_ulong, abi_ulong);
|
|
+abi_ulong mmap_find_vma(abi_ulong, abi_ulong, abi_ulong, bool);
|
|
void mmap_fork_start(void);
|
|
void mmap_fork_end(int child);
|
|
|
|
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
|
|
index 22b681ca37..8986804373 100644
|
|
--- a/target/i386/cpu.c
|
|
+++ b/target/i386/cpu.c
|
|
@@ -1608,6 +1608,7 @@ typedef struct X86CPUDefinition {
|
|
int family;
|
|
int model;
|
|
int stepping;
|
|
+ int brand_id;
|
|
FeatureWordArray features;
|
|
const char *model_id;
|
|
const CPUCaches *const cache_info;
|
|
@@ -2093,6 +2094,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,
|
|
@@ -4409,6 +4458,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);
|
|
@@ -5085,6 +5169,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];
|
|
}
|
|
@@ -5691,7 +5777,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];
|
|
|
|
@@ -7158,6 +7244,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 d4bc19577a..a8eff25d92 100644
|
|
--- a/target/i386/cpu.h
|
|
+++ b/target/i386/cpu.h
|
|
@@ -1749,6 +1749,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;
|