mirror of
https://github.com/yuzu-emu/unicorn.git
synced 2024-12-23 17:35:33 +00:00
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:
parent
6861fe80cf
commit
37406874ea
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue