diff options
Diffstat (limited to 'target-i386')
-rw-r--r-- | target-i386/cpu-qom.h | 1 | ||||
-rw-r--r-- | target-i386/cpu.c | 146 | ||||
-rw-r--r-- | target-i386/cpu.h | 13 | ||||
-rw-r--r-- | target-i386/kvm.c | 2 | ||||
-rw-r--r-- | target-i386/topology.h | 134 |
5 files changed, 240 insertions, 56 deletions
diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h index 31a0c1e776..b557b619cf 100644 --- a/target-i386/cpu-qom.h +++ b/target-i386/cpu-qom.h @@ -93,7 +93,6 @@ typedef struct X86CPU { bool expose_kvm; bool migratable; bool host_features; - int64_t apic_id; /* if true the CPUID code directly forward host cache leaves to the guest */ bool cache_info_passthrough; diff --git a/target-i386/cpu.c b/target-i386/cpu.c index 14c6c4abaf..d543e2b537 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -25,6 +25,7 @@ #include "sysemu/kvm.h" #include "sysemu/cpus.h" #include "kvm_i386.h" +#include "topology.h" #include "qemu/option.h" #include "qemu/config-file.h" @@ -1689,7 +1690,7 @@ static void x86_cpuid_get_apic_id(Object *obj, Visitor *v, void *opaque, const char *name, Error **errp) { X86CPU *cpu = X86_CPU(obj); - int64_t value = cpu->apic_id; + int64_t value = cpu->env.cpuid_apic_id; visit_type_int(v, &value, name, errp); } @@ -1722,11 +1723,11 @@ static void x86_cpuid_set_apic_id(Object *obj, Visitor *v, void *opaque, return; } - if ((value != cpu->apic_id) && cpu_exists(value)) { + if ((value != cpu->env.cpuid_apic_id) && cpu_exists(value)) { error_setg(errp, "CPU with APIC ID %" PRIi64 " exists", value); return; } - cpu->apic_id = value; + cpu->env.cpuid_apic_id = value; } /* Generic getter for "feature-words" and "filtered-features" properties */ @@ -1910,19 +1911,34 @@ static void x86_cpu_parse_featurestr(CPUState *cs, char *features, } } -/* Print all cpuid feature names in featureset +/* generate a composite string into buf of all cpuid names in featureset + * selected by fbits. indicate truncation at bufsize in the event of overflow. + * if flags, suppress names undefined in featureset. */ -static void listflags(FILE *f, fprintf_function print, const char **featureset) -{ - int bit; - bool first = true; - - for (bit = 0; bit < 32; bit++) { - if (featureset[bit]) { - print(f, "%s%s", first ? "" : " ", featureset[bit]); - first = false; +static void listflags(char *buf, int bufsize, uint32_t fbits, + const char **featureset, uint32_t flags) +{ + const char **p = &featureset[31]; + char *q, *b, bit; + int nc; + + b = 4 <= bufsize ? buf + (bufsize -= 3) - 1 : NULL; + *buf = '\0'; + for (q = buf, bit = 31; fbits && bufsize; --p, fbits &= ~(1 << bit), --bit) + if (fbits & 1 << bit && (*p || !flags)) { + if (*p) + nc = snprintf(q, bufsize, "%s%s", q == buf ? "" : " ", *p); + else + nc = snprintf(q, bufsize, "%s[%d]", q == buf ? "" : " ", bit); + if (bufsize <= nc) { + if (b) { + memcpy(b, "...", sizeof("...")); + } + return; + } + q += nc; + bufsize -= nc; } - } } /* generate CPU information. */ @@ -1947,9 +1963,8 @@ void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf) for (i = 0; i < ARRAY_SIZE(feature_word_info); i++) { FeatureWordInfo *fw = &feature_word_info[i]; - (*cpu_fprintf)(f, " "); - listflags(f, cpu_fprintf, fw->feat_names); - (*cpu_fprintf)(f, "\n"); + listflags(buf, sizeof(buf), (uint32_t)~0, fw->feat_names, 1); + (*cpu_fprintf)(f, " %s\n", buf); } } @@ -2134,35 +2149,27 @@ out: return cpu; } -CPUX86State *cpu_x86_init_user(const char *cpu_model) +X86CPU *cpu_x86_init(const char *cpu_model) { Error *error = NULL; X86CPU *cpu; cpu = cpu_x86_create(cpu_model, NULL, &error); if (error) { - goto error; - } - - object_property_set_int(OBJECT(cpu), CPU(cpu)->cpu_index, "apic-id", - &error); - if (error) { - goto error; + goto out; } object_property_set_bool(OBJECT(cpu), true, "realized", &error); - if (error) { - goto error; - } - return &cpu->env; - -error: - error_report_err(error); - if (cpu != NULL) { - object_unref(OBJECT(cpu)); +out: + if (error) { + error_report_err(error); + if (cpu != NULL) { + object_unref(OBJECT(cpu)); + cpu = NULL; + } } - return NULL; + return cpu; } static void x86_cpu_cpudef_class_init(ObjectClass *oc, void *data) @@ -2220,6 +2227,14 @@ void x86_cpudef_setup(void) } } +static void get_cpuid_vendor(CPUX86State *env, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + *ebx = env->cpuid_vendor1; + *edx = env->cpuid_vendor2; + *ecx = env->cpuid_vendor3; +} + void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) @@ -2253,14 +2268,11 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, switch(index) { case 0: *eax = env->cpuid_level; - *ebx = env->cpuid_vendor1; - *edx = env->cpuid_vendor2; - *ecx = env->cpuid_vendor3; + get_cpuid_vendor(env, ebx, ecx, edx); break; case 1: *eax = env->cpuid_version; - *ebx = (cpu->apic_id << 24) | - 8 << 8; /* CLFLUSH size in quad words, Linux wants it. */ + *ebx = (env->cpuid_apic_id << 24) | 8 << 8; /* CLFLUSH size in quad words, Linux wants it. */ *ecx = env->features[FEAT_1_ECX]; *edx = env->features[FEAT_1_EDX]; if (cs->nr_cores * cs->nr_threads > 1) { @@ -2449,9 +2461,11 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, * So dont set it here for Intel to make Linux guests happy. */ if (cs->nr_cores * cs->nr_threads > 1) { - if (env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1 || - env->cpuid_vendor2 != CPUID_VENDOR_INTEL_2 || - env->cpuid_vendor3 != CPUID_VENDOR_INTEL_3) { + uint32_t tebx, tecx, tedx; + get_cpuid_vendor(env, &tebx, &tecx, &tedx); + if (tebx != CPUID_VENDOR_INTEL_1 || + tedx != CPUID_VENDOR_INTEL_2 || + tecx != CPUID_VENDOR_INTEL_3) { *ecx |= 1 << 1; /* CmpLegacy bit */ } } @@ -2707,6 +2721,7 @@ static void mce_init(X86CPU *cpu) #ifndef CONFIG_USER_ONLY static void x86_cpu_apic_create(X86CPU *cpu, Error **errp) { + CPUX86State *env = &cpu->env; DeviceState *dev = DEVICE(cpu); APICCommonState *apic; const char *apic_type = "apic"; @@ -2725,7 +2740,7 @@ static void x86_cpu_apic_create(X86CPU *cpu, Error **errp) object_property_add_child(OBJECT(cpu), "apic", OBJECT(cpu->apic_state), NULL); - qdev_prop_set_uint8(cpu->apic_state, "id", cpu->apic_id); + qdev_prop_set_uint8(cpu->apic_state, "id", env->cpuid_apic_id); /* TODO: convert to link<> */ apic = APIC_COMMON(cpu->apic_state); apic->cpu = cpu; @@ -2765,11 +2780,6 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) Error *local_err = NULL; static bool ht_warned; - if (cpu->apic_id < 0) { - error_setg(errp, "apic-id property was not initialized properly"); - return; - } - if (env->features[FEAT_7_0_EBX] && env->cpuid_level < 7) { env->cpuid_level = 7; } @@ -2834,6 +2844,39 @@ out: } } +/* Enables contiguous-apic-ID mode, for compatibility */ +static bool compat_apic_id_mode; + +void enable_compat_apic_id_mode(void) +{ + compat_apic_id_mode = true; +} + +/* Calculates initial APIC ID for a specific CPU index + * + * Currently we need to be able to calculate the APIC ID from the CPU index + * alone (without requiring a CPU object), as the QEMU<->Seabios interfaces have + * no concept of "CPU index", and the NUMA tables on fw_cfg need the APIC ID of + * all CPUs up to max_cpus. + */ +uint32_t x86_cpu_apic_id_from_index(unsigned int cpu_index) +{ + uint32_t correct_id; + static bool warned; + + correct_id = x86_apicid_from_cpu_idx(smp_cores, smp_threads, cpu_index); + if (compat_apic_id_mode) { + if (cpu_index != correct_id && !warned) { + error_report("APIC IDs set in compatibility mode, " + "CPU topology won't match the configuration"); + warned = true; + } + return cpu_index; + } else { + return correct_id; + } +} + static void x86_cpu_initfn(Object *obj) { CPUState *cs = CPU(obj); @@ -2880,7 +2923,7 @@ static void x86_cpu_initfn(Object *obj) NULL, NULL, (void *)cpu->filtered_features, NULL); cpu->hyperv_spinlock_attempts = HYPERV_SPINLOCK_NEVER_RETRY; - cpu->apic_id = -1; + env->cpuid_apic_id = x86_cpu_apic_id_from_index(cs->cpu_index); x86_cpu_load_def(cpu, xcc->cpu_def, &error_abort); @@ -2894,8 +2937,9 @@ static void x86_cpu_initfn(Object *obj) static int64_t x86_cpu_get_arch_id(CPUState *cs) { X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; - return cpu->apic_id; + return env->cpuid_apic_id; } static bool x86_cpu_get_paging_enabled(const CPUState *cs) diff --git a/target-i386/cpu.h b/target-i386/cpu.h index f0e6ca4843..478450cfb6 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -944,6 +944,7 @@ typedef struct CPUX86State { uint32_t cpuid_version; FeatureWordArray features; uint32_t cpuid_model[12]; + uint32_t cpuid_apic_id; /* MTRRs */ uint64_t mtrr_fixed[11]; @@ -981,6 +982,7 @@ typedef struct CPUX86State { #include "cpu-qom.h" +X86CPU *cpu_x86_init(const char *cpu_model); X86CPU *cpu_x86_create(const char *cpu_model, DeviceState *icc_bridge, Error **errp); int cpu_x86_exec(CPUX86State *s); @@ -1169,9 +1171,14 @@ uint64_t cpu_get_tsc(CPUX86State *env); # define PHYS_ADDR_MASK 0xfffffffffLL # endif -/* CPU creation function for *-user */ -CPUX86State *cpu_x86_init_user(const char *cpu_model); -#define cpu_init cpu_x86_init_user +static inline CPUX86State *cpu_init(const char *cpu_model) +{ + X86CPU *cpu = cpu_x86_init(cpu_model); + if (cpu == NULL) { + return NULL; + } + return &cpu->env; +} #define cpu_exec cpu_x86_exec #define cpu_gen_code cpu_x86_gen_code diff --git a/target-i386/kvm.c b/target-i386/kvm.c index 27fe2be653..40d6a14c85 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -430,7 +430,7 @@ static void cpu_update_state(void *opaque, int running, RunState state) unsigned long kvm_arch_vcpu_id(CPUState *cs) { X86CPU *cpu = X86_CPU(cs); - return cpu->apic_id; + return cpu->env.cpuid_apic_id; } #ifndef KVM_CPUID_SIGNATURE_NEXT diff --git a/target-i386/topology.h b/target-i386/topology.h new file mode 100644 index 0000000000..07a6c5fb55 --- /dev/null +++ b/target-i386/topology.h @@ -0,0 +1,134 @@ +/* + * x86 CPU topology data structures and functions + * + * Copyright (c) 2012 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef TARGET_I386_TOPOLOGY_H +#define TARGET_I386_TOPOLOGY_H + +/* This file implements the APIC-ID-based CPU topology enumeration logic, + * documented at the following document: + * IntelĀ® 64 Architecture Processor Topology Enumeration + * http://software.intel.com/en-us/articles/intel-64-architecture-processor-topology-enumeration/ + * + * This code should be compatible with AMD's "Extended Method" described at: + * AMD CPUID Specification (Publication #25481) + * Section 3: Multiple Core Calcuation + * as long as: + * nr_threads is set to 1; + * OFFSET_IDX is assumed to be 0; + * CPUID Fn8000_0008_ECX[ApicIdCoreIdSize[3:0]] is set to apicid_core_width(). + */ + +#include <stdint.h> +#include <string.h> + +#include "qemu/bitops.h" + +/* APIC IDs can be 32-bit, but beware: APIC IDs > 255 require x2APIC support + */ +typedef uint32_t apic_id_t; + +/* Return the bit width needed for 'count' IDs + */ +static unsigned apicid_bitwidth_for_count(unsigned count) +{ + g_assert(count >= 1); + count -= 1; + return count ? 32 - clz32(count) : 0; +} + +/* Bit width of the SMT_ID (thread ID) field on the APIC ID + */ +static inline unsigned apicid_smt_width(unsigned nr_cores, unsigned nr_threads) +{ + return apicid_bitwidth_for_count(nr_threads); +} + +/* Bit width of the Core_ID field + */ +static inline unsigned apicid_core_width(unsigned nr_cores, unsigned nr_threads) +{ + return apicid_bitwidth_for_count(nr_cores); +} + +/* Bit offset of the Core_ID field + */ +static inline unsigned apicid_core_offset(unsigned nr_cores, + unsigned nr_threads) +{ + return apicid_smt_width(nr_cores, nr_threads); +} + +/* Bit offset of the Pkg_ID (socket ID) field + */ +static inline unsigned apicid_pkg_offset(unsigned nr_cores, unsigned nr_threads) +{ + return apicid_core_offset(nr_cores, nr_threads) + + apicid_core_width(nr_cores, nr_threads); +} + +/* Make APIC ID for the CPU based on Pkg_ID, Core_ID, SMT_ID + * + * The caller must make sure core_id < nr_cores and smt_id < nr_threads. + */ +static inline apic_id_t apicid_from_topo_ids(unsigned nr_cores, + unsigned nr_threads, + unsigned pkg_id, + unsigned core_id, + unsigned smt_id) +{ + return (pkg_id << apicid_pkg_offset(nr_cores, nr_threads)) | + (core_id << apicid_core_offset(nr_cores, nr_threads)) | + smt_id; +} + +/* Calculate thread/core/package IDs for a specific topology, + * based on (contiguous) CPU index + */ +static inline void x86_topo_ids_from_idx(unsigned nr_cores, + unsigned nr_threads, + unsigned cpu_index, + unsigned *pkg_id, + unsigned *core_id, + unsigned *smt_id) +{ + unsigned core_index = cpu_index / nr_threads; + *smt_id = cpu_index % nr_threads; + *core_id = core_index % nr_cores; + *pkg_id = core_index / nr_cores; +} + +/* Make APIC ID for the CPU 'cpu_index' + * + * 'cpu_index' is a sequential, contiguous ID for the CPU. + */ +static inline apic_id_t x86_apicid_from_cpu_idx(unsigned nr_cores, + unsigned nr_threads, + unsigned cpu_index) +{ + unsigned pkg_id, core_id, smt_id; + x86_topo_ids_from_idx(nr_cores, nr_threads, cpu_index, + &pkg_id, &core_id, &smt_id); + return apicid_from_topo_ids(nr_cores, nr_threads, pkg_id, core_id, smt_id); +} + +#endif /* TARGET_I386_TOPOLOGY_H */ |