target-i386: Automatically set level/xlevel/xlevel2 when needed

Instead of requiring users and management software to be aware of
required CPUID level/xlevel/xlevel2 values for each feature,
automatically increase those values when features need them.

This was already done for CPUID[7].EBX, and is now made generic
for all CPUID feature flags. Unit test included, to make sure we
don't break ABI on older machine-types and don't mess with the
CPUID level values if they are explicitly set by the user.

Backports commit c39c0edf9bb3b968ba95484465a50c7b19f4aa3a from qemu
This commit is contained in:
Eduardo Habkost 2018-02-26 04:03:02 -05:00 committed by Lioncash
parent 6861fe80cf
commit 37406874ea
No known key found for this signature in database
GPG key ID: 4E3C3CC1031BA9C7
2 changed files with 68 additions and 8 deletions

View file

@ -3006,6 +3006,38 @@ static QEMU_UNUSED_FUNC uint32_t x86_host_phys_bits(void)
return host_phys_bits;
}
static void x86_cpu_adjust_level(X86CPU *cpu, uint32_t *min, uint32_t value)
{
if (*min < value) {
*min = value;
}
}
/* Increase cpuid_min_{level,xlevel,xlevel2} automatically, if appropriate */
static void x86_cpu_adjust_feat_level(X86CPU *cpu, FeatureWord w)
{
CPUX86State *env = &cpu->env;
FeatureWordInfo *fi = &feature_word_info[w];
uint32_t eax = fi->cpuid_eax;
uint32_t region = eax & 0xF0000000;
if (!env->features[w]) {
return;
}
switch (region) {
case 0x00000000:
x86_cpu_adjust_level(cpu, &env->cpuid_min_level, eax);
break;
case 0x80000000:
x86_cpu_adjust_level(cpu, &env->cpuid_min_xlevel, eax);
break;
case 0xC0000000:
x86_cpu_adjust_level(cpu, &env->cpuid_min_xlevel2, eax);
break;
}
}
#define IS_INTEL_CPU(env) ((env)->cpuid_vendor1 == CPUID_VENDOR_INTEL_1 && \
(env)->cpuid_vendor2 == CPUID_VENDOR_INTEL_2 && \
(env)->cpuid_vendor3 == CPUID_VENDOR_INTEL_3)
@ -3026,8 +3058,30 @@ static int x86_cpu_realizefn(struct uc_struct *uc, DeviceState *dev, Error **err
return -1;
}
if (env->features[FEAT_7_0_EBX] && env->cpuid_level < 7) {
env->cpuid_level = 7;
/* CPUID[EAX=7,ECX=0].EBX always increased level automatically: */
x86_cpu_adjust_feat_level(cpu, FEAT_7_0_EBX);
if (cpu->full_cpuid_auto_level) {
x86_cpu_adjust_feat_level(cpu, FEAT_1_EDX);
x86_cpu_adjust_feat_level(cpu, FEAT_1_ECX);
x86_cpu_adjust_feat_level(cpu, FEAT_6_EAX);
x86_cpu_adjust_feat_level(cpu, FEAT_7_0_ECX);
x86_cpu_adjust_feat_level(cpu, FEAT_8000_0001_EDX);
x86_cpu_adjust_feat_level(cpu, FEAT_8000_0001_ECX);
x86_cpu_adjust_feat_level(cpu, FEAT_8000_0007_EDX);
x86_cpu_adjust_feat_level(cpu, FEAT_C000_0001_EDX);
x86_cpu_adjust_feat_level(cpu, FEAT_SVM);
x86_cpu_adjust_feat_level(cpu, FEAT_XSAVE);
}
/* Set cpuid_*level* based on cpuid_min_*level, if not explicitly set */
if (env->cpuid_level == UINT32_MAX) {
env->cpuid_level = env->cpuid_min_level;
}
if (env->cpuid_xlevel == UINT32_MAX) {
env->cpuid_xlevel = env->cpuid_min_xlevel;
}
if (env->cpuid_xlevel2 == UINT32_MAX) {
env->cpuid_xlevel2 = env->cpuid_min_xlevel2;
}
/*TODO: cpu->host_features incorrectly overwrites features

View file

@ -1104,9 +1104,12 @@ typedef struct CPUX86State {
struct {} end_reset_fields;
/* processor features (e.g. for CPUID insn) */
uint32_t cpuid_level;
uint32_t cpuid_xlevel;
uint32_t cpuid_xlevel2;
/* Minimum level/xlevel/xlevel2, based on CPU model + features */
uint32_t cpuid_min_level, cpuid_min_xlevel, cpuid_min_xlevel2;
/* Maximum level/xlevel/xlevel2 value for auto-assignment: */
uint32_t cpuid_max_level, cpuid_max_xlevel, cpuid_max_xlevel2;
/* Actual level/xlevel/xlevel2 value: */
uint32_t cpuid_level, cpuid_xlevel, cpuid_xlevel2;
uint32_t cpuid_vendor1;
uint32_t cpuid_vendor2;
uint32_t cpuid_vendor3;
@ -1206,15 +1209,18 @@ typedef struct X86CPU {
/* Compatibility bits for old machine types: */
bool enable_cpuid_0xb;
/* Enable auto level-increase for all CPUID leaves */
bool full_cpuid_auto_level;
/* if true fill the top bits of the MTRR_PHYSMASKn variable range */
bool fill_mtrr_mask;
/* if true override the phys_bits value with a value read from the host */
bool host_phys_bits;
/* Number of physical address bits supported */
uint32_t phys_bits;
/* if true fill the top bits of the MTRR_PHYSMASKn variable range */
bool fill_mtrr_mask;
/* in order to simplify APIC support, we leave this pointer to the
user */
struct DeviceState *apic_state;