qemu-7.0.0.patch (21011B)
1 diff --git a/linux-user/main.c b/linux-user/main.c 2 index fbc9bcfd5f..8d5d8e8546 100644 3 --- a/linux-user/main.c 4 +++ b/linux-user/main.c 5 @@ -119,6 +119,7 @@ static void usage(int exitcode); 6 7 static const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX; 8 const char *qemu_uname_release; 9 +const char *qemu_execve_path; 10 11 /* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so 12 we allocate a bigger stack. Need a better solution, for example 13 @@ -352,6 +353,11 @@ static void handle_arg_guest_base(const char *arg) 14 have_guest_base = true; 15 } 16 17 +static void handle_arg_execve(const char *arg) 18 +{ 19 + qemu_execve_path = strdup(arg); 20 +} 21 + 22 static void handle_arg_reserved_va(const char *arg) 23 { 24 char *p; 25 @@ -454,6 +460,8 @@ static const struct qemu_argument arg_table[] = { 26 "uname", "set qemu uname release string to 'uname'"}, 27 {"B", "QEMU_GUEST_BASE", true, handle_arg_guest_base, 28 "address", "set guest_base address to 'address'"}, 29 + {"execve", "QEMU_EXECVE", true, handle_arg_execve, 30 + "", "use this interpreter when a process calls execve()"}, 31 {"R", "QEMU_RESERVED_VA", true, handle_arg_reserved_va, 32 "size", "reserve 'size' bytes for guest virtual address space"}, 33 {"d", "QEMU_LOG", true, handle_arg_log, 34 diff --git a/linux-user/mmap.c b/linux-user/mmap.c 35 index c125031b90..692781ff18 100644 36 --- a/linux-user/mmap.c 37 +++ b/linux-user/mmap.c 38 @@ -313,7 +313,7 @@ static abi_ulong mmap_find_vma_reserved(abi_ulong start, abi_ulong size, 39 * It must be called with mmap_lock() held. 40 * Return -1 if error. 41 */ 42 -abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong align) 43 +abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong align, bool map_32bit) 44 { 45 void *ptr, *prev; 46 abi_ulong addr; 47 @@ -348,7 +348,7 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong align) 48 * - shmat() with SHM_REMAP flag 49 */ 50 ptr = mmap(g2h_untagged(addr), size, PROT_NONE, 51 - MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE, -1, 0); 52 + MAP_ANONYMOUS|MAP_PRIVATE|MAP_NORESERVE|(map_32bit?MAP_32BIT:0), -1, 0); 53 54 /* ENOMEM, if host address space has no memory */ 55 if (ptr == MAP_FAILED) { 56 @@ -359,7 +359,7 @@ abi_ulong mmap_find_vma(abi_ulong start, abi_ulong size, abi_ulong align) 57 This is used to modify the search algorithm below. */ 58 repeat = (ptr == prev ? repeat + 1 : 0); 59 60 - if (h2g_valid(ptr + size - 1)) { 61 + if (map_32bit ? h2g(ptr+size-1) == (uint32_t) (ptrdiff_t) (ptr+size-1): h2g_valid(ptr + size - 1)) { 62 addr = h2g(ptr); 63 64 if ((addr & (align - 1)) == 0) { 65 @@ -475,7 +475,7 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int target_prot, 66 if (!(flags & MAP_FIXED)) { 67 host_len = len + offset - host_offset; 68 host_len = HOST_PAGE_ALIGN(host_len); 69 - start = mmap_find_vma(real_start, host_len, TARGET_PAGE_SIZE); 70 + start = mmap_find_vma(real_start, host_len, TARGET_PAGE_SIZE, flags & MAP_32BIT); 71 if (start == (abi_ulong)-1) { 72 errno = ENOMEM; 73 goto fail; 74 @@ -772,7 +772,7 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, 75 } else if (flags & MREMAP_MAYMOVE) { 76 abi_ulong mmap_start; 77 78 - mmap_start = mmap_find_vma(0, new_size, TARGET_PAGE_SIZE); 79 + mmap_start = mmap_find_vma(0, new_size, TARGET_PAGE_SIZE, flags & MAP_32BIT); 80 81 if (mmap_start == -1) { 82 errno = ENOMEM; 83 diff --git a/linux-user/strace.c b/linux-user/strace.c 84 index 2cdbf030ba..75f0c9a1b7 100644 85 --- a/linux-user/strace.c 86 +++ b/linux-user/strace.c 87 @@ -1074,6 +1074,9 @@ UNUSED static struct flags mmap_flags[] = { 88 #ifdef MAP_POPULATE 89 FLAG_TARGET(MAP_POPULATE), 90 #endif 91 +#ifdef TARGET_MAP_32BIT 92 + FLAG_TARGET(MAP_32BIT), 93 +#endif 94 #ifdef TARGET_MAP_UNINITIALIZED 95 FLAG_TARGET(MAP_UNINITIALIZED), 96 #endif 97 diff --git a/linux-user/syscall.c b/linux-user/syscall.c 98 index f65045efe6..9ec4f5b128 100644 99 --- a/linux-user/syscall.c 100 +++ b/linux-user/syscall.c 101 @@ -123,6 +123,7 @@ 102 #include <libdrm/drm.h> 103 #include <libdrm/i915_drm.h> 104 #endif 105 +#include <linux/binfmts.h> 106 #include "linux_loop.h" 107 #include "uname.h" 108 109 @@ -995,7 +996,7 @@ static inline rlim_t target_to_host_rlim(abi_ulong target_rlim) 110 { 111 abi_ulong target_rlim_swap; 112 rlim_t result; 113 - 114 + 115 target_rlim_swap = tswapal(target_rlim); 116 if (target_rlim_swap == TARGET_RLIM_INFINITY) 117 return RLIM_INFINITY; 118 @@ -1003,7 +1004,7 @@ static inline rlim_t target_to_host_rlim(abi_ulong target_rlim) 119 result = target_rlim_swap; 120 if (target_rlim_swap != (rlim_t)result) 121 return RLIM_INFINITY; 122 - 123 + 124 return result; 125 } 126 #endif 127 @@ -1013,13 +1014,13 @@ static inline abi_ulong host_to_target_rlim(rlim_t rlim) 128 { 129 abi_ulong target_rlim_swap; 130 abi_ulong result; 131 - 132 + 133 if (rlim == RLIM_INFINITY || rlim != (abi_long)rlim) 134 target_rlim_swap = TARGET_RLIM_INFINITY; 135 else 136 target_rlim_swap = rlim; 137 result = tswapal(target_rlim_swap); 138 - 139 + 140 return result; 141 } 142 #endif 143 @@ -1753,9 +1754,9 @@ static inline abi_long target_to_host_cmsg(struct msghdr *msgh, 144 abi_ulong target_cmsg_addr; 145 struct target_cmsghdr *target_cmsg, *target_cmsg_start; 146 socklen_t space = 0; 147 - 148 + 149 msg_controllen = tswapal(target_msgh->msg_controllen); 150 - if (msg_controllen < sizeof (struct target_cmsghdr)) 151 + if (msg_controllen < sizeof (struct target_cmsghdr)) 152 goto the_end; 153 target_cmsg_addr = tswapal(target_msgh->msg_control); 154 target_cmsg = lock_user(VERIFY_READ, target_cmsg_addr, msg_controllen, 1); 155 @@ -1841,7 +1842,7 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, 156 socklen_t space = 0; 157 158 msg_controllen = tswapal(target_msgh->msg_controllen); 159 - if (msg_controllen < sizeof (struct target_cmsghdr)) 160 + if (msg_controllen < sizeof (struct target_cmsghdr)) 161 goto the_end; 162 target_cmsg_addr = tswapal(target_msgh->msg_control); 163 target_cmsg = lock_user(VERIFY_WRITE, target_cmsg_addr, msg_controllen, 0); 164 @@ -4534,7 +4535,7 @@ static inline abi_ulong do_shmat(CPUArchState *cpu_env, 165 abi_ulong mmap_start; 166 167 /* In order to use the host shmat, we need to honor host SHMLBA. */ 168 - mmap_start = mmap_find_vma(0, shm_info.shm_segsz, MAX(SHMLBA, shmlba)); 169 + mmap_start = mmap_find_vma(0, shm_info.shm_segsz, MAX(SHMLBA, shmlba), false); 170 171 if (mmap_start == -1) { 172 errno = ENOMEM; 173 @@ -5958,6 +5959,9 @@ static const StructEntry struct_termios_def = { 174 }; 175 176 static const bitmask_transtbl mmap_flags_tbl[] = { 177 +#ifdef TARGET_MAP_32BIT 178 + { TARGET_MAP_32BIT, TARGET_MAP_32BIT, MAP_32BIT, MAP_32BIT }, 179 +#endif 180 { TARGET_MAP_SHARED, TARGET_MAP_SHARED, MAP_SHARED, MAP_SHARED }, 181 { TARGET_MAP_PRIVATE, TARGET_MAP_PRIVATE, MAP_PRIVATE, MAP_PRIVATE }, 182 { TARGET_MAP_FIXED, TARGET_MAP_FIXED, MAP_FIXED, MAP_FIXED }, 183 @@ -6152,7 +6156,7 @@ abi_long do_set_thread_area(CPUX86State *env, abi_ulong ptr) 184 } 185 unlock_user_struct(target_ldt_info, ptr, 1); 186 187 - if (ldt_info.entry_number < TARGET_GDT_ENTRY_TLS_MIN || 188 + if (ldt_info.entry_number < TARGET_GDT_ENTRY_TLS_MIN || 189 ldt_info.entry_number > TARGET_GDT_ENTRY_TLS_MAX) 190 return -TARGET_EINVAL; 191 seg_32bit = ldt_info.flags & 1; 192 @@ -6230,7 +6234,7 @@ static abi_long do_get_thread_area(CPUX86State *env, abi_ulong ptr) 193 lp = (uint32_t *)(gdt_table + idx); 194 entry_1 = tswap32(lp[0]); 195 entry_2 = tswap32(lp[1]); 196 - 197 + 198 read_exec_only = ((entry_2 >> 9) & 1) ^ 1; 199 contents = (entry_2 >> 10) & 3; 200 seg_not_present = ((entry_2 >> 15) & 1) ^ 1; 201 @@ -6246,8 +6250,8 @@ static abi_long do_get_thread_area(CPUX86State *env, abi_ulong ptr) 202 (read_exec_only << 3) | (limit_in_pages << 4) | 203 (seg_not_present << 5) | (useable << 6) | (lm << 7); 204 limit = (entry_1 & 0xffff) | (entry_2 & 0xf0000); 205 - base_addr = (entry_1 >> 16) | 206 - (entry_2 & 0xff000000) | 207 + base_addr = (entry_1 >> 16) | 208 + (entry_2 & 0xff000000) | 209 ((entry_2 & 0xff) << 16); 210 target_ldt_info->base_addr = tswapal(base_addr); 211 target_ldt_info->limit = tswap32(limit); 212 @@ -8300,6 +8304,131 @@ static target_timer_t get_timer_id(abi_long arg) 213 return timerid; 214 } 215 216 +/* qemu_execve() Must return target values and target errnos. 217 + * 218 + * Although execve() is not an interruptible syscall it is 219 + * a special case where we must use the safe_syscall wrapper: 220 + * if we allow a signal to happen before we make the host 221 + * syscall then we will 'lose' it, because at the point of 222 + * execve the process leaves QEMU's control. So we use the 223 + * safe syscall wrapper to ensure that we either take the 224 + * signal as a guest signal, or else it does not happen 225 + * before the execve completes and makes it the other 226 + * program's problem. 227 + */ 228 +static abi_long qemu_execve(char *filename, char *argv[], 229 + char *envp[]) 230 +{ 231 + char *i_arg = NULL, *i_name = NULL; 232 + char **new_argp; 233 + int argc, fd, ret, i, offset = 5; 234 + char *cp; 235 + char buf[BINPRM_BUF_SIZE]; 236 + 237 + /* normal execve case */ 238 + if (qemu_execve_path == NULL || *qemu_execve_path == 0) { 239 + return get_errno(safe_execve(filename, argv, envp)); 240 + } 241 + 242 + for (argc = 0; argv[argc] != NULL; argc++) { 243 + /* nothing */ ; 244 + } 245 + 246 + fd = open(filename, O_RDONLY); 247 + if (fd == -1) { 248 + return get_errno(fd); 249 + } 250 + 251 + ret = read(fd, buf, BINPRM_BUF_SIZE); 252 + if (ret == -1) { 253 + close(fd); 254 + return get_errno(ret); 255 + } 256 + 257 + /* if we have less than 2 bytes, we can guess it is not executable */ 258 + if (ret < 2) { 259 + close(fd); 260 + return -host_to_target_errno(ENOEXEC); 261 + } 262 + 263 + close(fd); 264 + 265 + /* adapted from the kernel 266 + * https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/fs/binfmt_script.c 267 + */ 268 + if ((buf[0] == '#') && (buf[1] == '!')) { 269 + /* 270 + * This section does the #! interpretation. 271 + * Sorta complicated, but hopefully it will work. -TYT 272 + */ 273 + 274 + buf[BINPRM_BUF_SIZE - 1] = '\0'; 275 + cp = strchr(buf, '\n'); 276 + if (cp == NULL) { 277 + cp = buf + BINPRM_BUF_SIZE - 1; 278 + } 279 + *cp = '\0'; 280 + while (cp > buf) { 281 + cp--; 282 + if ((*cp == ' ') || (*cp == '\t')) { 283 + *cp = '\0'; 284 + } else { 285 + break; 286 + } 287 + } 288 + for (cp = buf + 2; (*cp == ' ') || (*cp == '\t'); cp++) { 289 + /* nothing */ ; 290 + } 291 + if (*cp == '\0') { 292 + return -ENOEXEC; /* No interpreter name found */ 293 + } 294 + i_name = cp; 295 + i_arg = NULL; 296 + for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++) { 297 + /* nothing */ ; 298 + } 299 + while ((*cp == ' ') || (*cp == '\t')) { 300 + *cp++ = '\0'; 301 + } 302 + if (*cp) { 303 + i_arg = cp; 304 + } 305 + 306 + if (i_arg) { 307 + offset += 2; 308 + } else { 309 + offset += 1; 310 + } 311 + } 312 + 313 + new_argp = alloca((argc + offset + 1) * sizeof(void *)); 314 + 315 + /* Copy the original arguments with offset */ 316 + for (i = 0; i < argc; i++) { 317 + new_argp[i + offset] = argv[i]; 318 + } 319 + 320 + new_argp[0] = strdup(qemu_execve_path); 321 + new_argp[1] = strdup("--execve"); 322 + new_argp[2] = strdup(qemu_execve_path); 323 + new_argp[3] = strdup("-0"); 324 + new_argp[offset] = filename; 325 + new_argp[argc + offset] = NULL; 326 + 327 + if (i_name) { 328 + new_argp[4] = i_name; 329 + new_argp[5] = i_name; 330 + 331 + if (i_arg) { 332 + new_argp[6] = i_arg; 333 + } 334 + } else { 335 + new_argp[4] = argv[0]; 336 + } 337 + 338 + return get_errno(safe_execve(qemu_execve_path, new_argp, envp)); 339 +} 340 + 341 static int target_to_host_cpu_mask(unsigned long *host_mask, 342 size_t host_size, 343 abi_ulong target_addr, 344 @@ -8803,17 +8932,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, 345 346 if (!(p = lock_user_string(arg1))) 347 goto execve_efault; 348 - /* Although execve() is not an interruptible syscall it is 349 - * a special case where we must use the safe_syscall wrapper: 350 - * if we allow a signal to happen before we make the host 351 - * syscall then we will 'lose' it, because at the point of 352 - * execve the process leaves QEMU's control. So we use the 353 - * safe syscall wrapper to ensure that we either take the 354 - * signal as a guest signal, or else it does not happen 355 - * before the execve completes and makes it the other 356 - * program's problem. 357 - */ 358 - ret = get_errno(safe_execve(p, argp, envp)); 359 + ret = qemu_execve(p, argp, envp); 360 unlock_user(p, arg1, 0); 361 362 goto execve_end; 363 @@ -11330,7 +11449,7 @@ static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, 364 return get_errno(fchown(arg1, low2highuid(arg2), low2highgid(arg3))); 365 #if defined(TARGET_NR_fchownat) 366 case TARGET_NR_fchownat: 367 - if (!(p = lock_user_string(arg2))) 368 + if (!(p = lock_user_string(arg2))) 369 return -TARGET_EFAULT; 370 ret = get_errno(fchownat(arg1, p, low2highuid(arg3), 371 low2highgid(arg4), arg5)); 372 diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h 373 index 4587b62ac9..4753bce1dc 100644 374 --- a/linux-user/syscall_defs.h 375 +++ b/linux-user/syscall_defs.h 376 @@ -1330,6 +1330,7 @@ struct target_winsize { 377 #else 378 #define TARGET_MAP_FIXED 0x10 /* Interpret addr exactly */ 379 #define TARGET_MAP_ANONYMOUS 0x20 /* don't use a file */ 380 +#define TARGET_MAP_32BIT 0x40 381 #define TARGET_MAP_GROWSDOWN 0x0100 /* stack-like segment */ 382 #define TARGET_MAP_DENYWRITE 0x0800 /* ETXTBSY */ 383 #define TARGET_MAP_EXECUTABLE 0x1000 /* mark it as an executable */ 384 diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h 385 index ee152ccfaa..e9e0fef472 100644 386 --- a/linux-user/user-internals.h 387 +++ b/linux-user/user-internals.h 388 @@ -27,6 +27,7 @@ void init_task_state(TaskState *ts); 389 void task_settid(TaskState *); 390 void stop_all_tasks(void); 391 extern const char *qemu_uname_release; 392 +extern const char *qemu_execve_path; 393 extern unsigned long mmap_min_addr; 394 395 typedef struct IOCTLEntry IOCTLEntry; 396 diff --git a/linux-user/user-mmap.h b/linux-user/user-mmap.h 397 index d1dec99c02..b73c448b9a 100644 398 --- a/linux-user/user-mmap.h 399 +++ b/linux-user/user-mmap.h 400 @@ -27,7 +27,7 @@ abi_long target_mremap(abi_ulong old_addr, abi_ulong old_size, 401 abi_ulong new_addr); 402 extern unsigned long last_brk; 403 extern abi_ulong mmap_next_start; 404 -abi_ulong mmap_find_vma(abi_ulong, abi_ulong, abi_ulong); 405 +abi_ulong mmap_find_vma(abi_ulong, abi_ulong, abi_ulong, bool); 406 void mmap_fork_start(void); 407 void mmap_fork_end(int child); 408 409 diff --git a/target/i386/cpu.c b/target/i386/cpu.c 410 index cb6b5467d0..08647e5cab 100644 411 --- a/target/i386/cpu.c 412 +++ b/target/i386/cpu.c 413 @@ -1556,6 +1556,7 @@ typedef struct X86CPUDefinition { 414 int family; 415 int model; 416 int stepping; 417 + int brand_id; 418 FeatureWordArray features; 419 const char *model_id; 420 const CPUCaches *const cache_info; 421 @@ -2041,6 +2042,54 @@ static const X86CPUDefinition builtin_x86_defs[] = { 422 .xlevel = 0, 423 .model_id = "", 424 }, 425 + { 426 + /* http://users.atw.hu/instlatx64/AuthenticAMD/AuthenticAMD0000F4A_K8_Clawhammer_CPUID.txt */ 427 + .name = "athlon-64", 428 + .level = 1, 429 + .vendor = CPUID_VENDOR_AMD, 430 + .family = 15, 431 + .model = 4, 432 + .stepping = 10, 433 + .features[FEAT_1_EDX] = 434 + CPUID_FP87 | CPUID_VME | CPUID_DE | CPUID_PSE | 435 + CPUID_TSC | CPUID_MSR | CPUID_PAE | CPUID_MCE | 436 + CPUID_CX8 | CPUID_APIC | CPUID_SEP | 437 + CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV | 438 + CPUID_PAT | CPUID_PSE36 | /*no: CPUID_PN |*/ CPUID_CLFLUSH | 439 + /*no: CPUID_DTS | CPUID_ACPI |*/ CPUID_MMX | 440 + CPUID_FXSR | CPUID_SSE | CPUID_SSE2, 441 + .features[FEAT_8000_0001_EDX] = 442 + (CPUID_EXT2_FPU | CPUID_EXT2_VME | CPUID_EXT2_DE | CPUID_EXT2_PSE | 443 + CPUID_EXT2_TSC | CPUID_EXT2_MSR | CPUID_EXT2_PAE | CPUID_EXT2_MCE | 444 + CPUID_EXT2_CX8 | CPUID_EXT2_APIC | CPUID_EXT2_SYSCALL | 445 + CPUID_EXT2_MTRR | CPUID_EXT2_PGE | CPUID_EXT2_MCA | CPUID_EXT2_CMOV | 446 + CPUID_EXT2_PAT | CPUID_EXT2_PSE36 | /*no: CPUID_EXT2_MP */ 447 + CPUID_EXT2_NX | CPUID_EXT2_MMXEXT | CPUID_EXT2_MMX | 448 + CPUID_EXT2_FXSR | /*no: CPUID_EXT2_FFXSR | CPUID_EXT2_PDPE1G | CPUID_EXT2_RTDSCP*/ 449 + CPUID_EXT2_LM | CPUID_EXT2_3DNOWEXT | CPUID_EXT2_3DNOW) & 450 + ~CPUID_EXT2_AMD_ALIASES, 451 + /* todo cache info 0x80000005-6 */ 452 + .features[FEAT_8000_0007_EDX] = 0x0f, /*??*/ 453 + /*.phys_bits = 0x00003028,*/ 454 + .xlevel = 0x80000018, 455 + .brand_id = 0x106, 456 + .model_id = "AMD Athlon(tm) 64 Processor 2800+", 457 + }, 458 + { 459 + .name = "u3-64bit-minimal", 460 + .level = 1, 461 + .vendor = CPUID_VENDOR_AMD, 462 + .family = 15, 463 + .model = 4, 464 + .stepping = 10, 465 + .features[FEAT_1_EDX] = 466 + PENTIUM2_FEATURES | PPRO_FEATURES | CPUID_CLFLUSH /*sse2?*/, 467 + .features[FEAT_8000_0001_EDX] = 468 + /* NX: amd: since amd64, intel: P4 Prescott+ */ 469 + CPUID_EXT2_SYSCALL | CPUID_EXT2_NX | CPUID_EXT2_LM, 470 + .xlevel = 0x80000018, 471 + .model_id = "Fuck off", 472 + }, 473 { 474 .name = "athlon", 475 .level = 2, 476 @@ -4479,6 +4528,41 @@ static void x86_cpuid_version_set_stepping(Object *obj, Visitor *v, 477 env->cpuid_version |= value & 0xf; 478 } 479 480 +static void x86_cpuid_version_get_brand_id(Object *obj, Visitor *v, 481 + const char *name, void *opaque, 482 + Error **errp) 483 +{ 484 + X86CPU *cpu = X86_CPU(obj); 485 + CPUX86State *env = &cpu->env; 486 + int64_t value; 487 + 488 + value = env->cpuid_brand_id; 489 + visit_type_int(v, name, &value, errp); 490 +} 491 + 492 +static void x86_cpuid_version_set_brand_id(Object *obj, Visitor *v, 493 + const char *name, void *opaque, 494 + Error **errp) 495 +{ 496 + X86CPU *cpu = X86_CPU(obj); 497 + CPUX86State *env = &cpu->env; 498 + const int64_t min = 0; 499 + const int64_t max = 0xffff; 500 + int64_t value; 501 + 502 + if (!visit_type_int(v, name, &value, errp)) { 503 + return; 504 + } 505 + if (value < min || value > max) { 506 + error_setg(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE, "", 507 + name ? name : "null", value, min, max); 508 + return; 509 + } 510 + 511 + env->cpuid_brand_id = value; 512 +} 513 + 514 + 515 static char *x86_cpuid_get_vendor(Object *obj, Error **errp) 516 { 517 X86CPU *cpu = X86_CPU(obj); 518 @@ -5097,6 +5181,8 @@ static void x86_cpu_load_model(X86CPU *cpu, X86CPUModel *model) 519 &error_abort); 520 object_property_set_str(OBJECT(cpu), "model-id", def->model_id, 521 &error_abort); 522 + object_property_set_int(OBJECT(cpu), "brand-id", def->brand_id, 523 + &error_abort); 524 for (w = 0; w < FEATURE_WORDS; w++) { 525 env->features[w] = def->features[w]; 526 } 527 @@ -5675,7 +5761,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, 528 break; 529 case 0x80000001: 530 *eax = env->cpuid_version; 531 - *ebx = 0; 532 + *ebx = env->cpuid_brand_id; 533 *ecx = env->features[FEAT_8000_0001_ECX]; 534 *edx = env->features[FEAT_8000_0001_EDX]; 535 536 @@ -7065,6 +7151,9 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data) 537 object_class_property_add(oc, "stepping", "int", 538 x86_cpuid_version_get_stepping, 539 x86_cpuid_version_set_stepping, NULL, NULL); 540 + object_class_property_add(oc, "brand-id", "int", 541 + x86_cpuid_version_get_brand_id, 542 + x86_cpuid_version_set_brand_id, NULL, NULL); 543 object_class_property_add_str(oc, "vendor", 544 x86_cpuid_get_vendor, 545 x86_cpuid_set_vendor); 546 diff --git a/target/i386/cpu.h b/target/i386/cpu.h 547 index 982c532353..29d6f92a03 100644 548 --- a/target/i386/cpu.h 549 +++ b/target/i386/cpu.h 550 @@ -1676,6 +1676,7 @@ typedef struct CPUArchState { 551 uint32_t cpuid_vendor2; 552 uint32_t cpuid_vendor3; 553 uint32_t cpuid_version; 554 + uint32_t cpuid_brand_id; 555 FeatureWordArray features; 556 /* Features that were explicitly enabled/disabled */ 557 FeatureWordArray user_features;