diff --git a/core/cpuid.c b/core/cpuid.c index aad795f6..96ea5050 100644 --- a/core/cpuid.c +++ b/core/cpuid.c @@ -35,7 +35,7 @@ #include "ia32.h" #define CPUID_CACHE_SIZE 6 -#define CPUID_FEATURE_SET_SIZE 2 +#define CPUID_FEATURE_SET_SIZE 3 #define MAX_BASIC_CPUID 0x16 #define MAX_EXTENDED_CPUID 0x80000008 @@ -70,22 +70,31 @@ typedef void (*set_leaf_t)(hax_cpuid_entry *, hax_cpuid_entry *); typedef struct cpuid_manager_t { uint32_t leaf; + uint32_t subleaf; execute_t execute; } cpuid_manager_t; typedef struct cpuid_controller_t { uint32_t leaf; + uint32_t subleaf; set_leaf_t set_leaf; } cpuid_controller_t; static cpuid_cache_t cache = {0}; +static inline uint32_t feature_leaf(uint32_t feature_key); +static inline uint32_t feature_subleaf(uint32_t feature_key); +static inline uint32_t feature_reg(uint32_t feature_key); +static inline uint32_t feature_bit(uint32_t feature_key); +static uint32_t * get_reg(hax_cpuid_entry *entry, uint32_t feature_key); +static bool is_feature_set(hax_cpuid_entry *entry, uint32_t feature_key); +static void update_feature(hax_cpuid_entry *entry, uint32_t feature_key, + bool set); static hax_cpuid_entry * find_cpuid_entry(hax_cpuid_entry *features, uint32_t size, uint32_t function, uint32_t index); static void dump_features(hax_cpuid_entry *features, uint32_t size); -static void get_guest_cache(cpuid_args_t *args, hax_cpuid_entry *entry); static void adjust_0000_0001(cpuid_args_t *args); static void adjust_8000_0001(cpuid_args_t *args); static void execute_0000_0000(cpuid_args_t *args); @@ -118,26 +127,27 @@ static void set_leaf_8000_0008(hax_cpuid_entry *dest, hax_cpuid_entry *src); static const cpuid_manager_t kCpuidManager[] = { // Basic CPUID Information - {0x00000000, execute_0000_0000}, // Maximum Basic Information - {0x00000001, execute_0000_0001}, // Version Information and Features - {0x00000002, execute_0000_0002}, // Cache and TLB Information - {0x00000007, execute_0000_0007}, // Structured Extended Feature Flags - {0x0000000a, execute_0000_000a}, // Architectural Performance Monitoring - {0x00000015, NULL}, // Time Stamp Counter and Nominal Core - // Crystal Clock Information - {0x00000016, NULL}, // Processor Frequency Information + {0x00000000, 0, execute_0000_0000}, // Maximum Basic Information + {0x00000001, 0, execute_0000_0001}, // Version Information and Features + {0x00000002, 0, execute_0000_0002}, // Cache and TLB Information + {0x00000007, 0, execute_0000_0007}, // Structured Extended Feature Flags + {0x00000007, 1, execute_0000_0007}, // Structured Extended Feature Flags + {0x0000000a, 0, execute_0000_000a}, // Architectural Performance Monitoring + {0x00000015, 0, NULL}, // Time Stamp Counter and Nominal + // Core Crystal Clock Information + {0x00000016, 0, NULL}, // Processor Frequency Information // Unimplemented CPUID Leaf Functions - {0x40000000, execute_4000_0000}, // Unimplemented by real Intel CPUs + {0x40000000, 0, execute_4000_0000}, // Unimplemented by real Intel CPUs // Extended Function CPUID Information - {0x80000000, execute_8000_0000}, // Maximum Extended Information - {0x80000001, execute_8000_0001}, // Extended Signature and Features - {0x80000002, execute_8000_0002}, // Processor Brand String - part 1 - {0x80000003, execute_8000_0003}, // Processor Brand String - part 2 - {0x80000004, execute_8000_0003}, // Processor Brand String - part 3 - {0x80000006, execute_8000_0006}, - {0x80000008, execute_8000_0008} // Virtual/Physical Address Size + {0x80000000, 0, execute_8000_0000}, // Maximum Extended Information + {0x80000001, 0, execute_8000_0001}, // Extended Signature and Features + {0x80000002, 0, execute_8000_0002}, // Processor Brand String - part 1 + {0x80000003, 0, execute_8000_0003}, // Processor Brand String - part 2 + {0x80000004, 0, execute_8000_0003}, // Processor Brand String - part 3 + {0x80000006, 0, execute_8000_0006}, + {0x80000008, 0, execute_8000_0008} // Virtual/Physical Address Size }; // ________ // 03H Reserved @@ -159,21 +169,22 @@ static const cpuid_manager_t kCpuidManager[] = { #define CPUID_TOTAL_LEAVES sizeof(kCpuidManager)/sizeof(kCpuidManager[0]) static const cpuid_controller_t kCpuidController[] = { - {0x00000000, set_leaf_0000_0000}, - {0x00000001, set_leaf_0000_0001}, - {0x00000002, NULL}, - {0x00000007, NULL}, - {0x0000000a, NULL}, - {0x00000015, set_leaf_0000_0015}, - {0x00000016, set_leaf_0000_0016}, - {0x40000000, NULL}, - {0x80000000, set_leaf_8000_0000}, - {0x80000001, set_leaf_8000_0001}, - {0x80000002, NULL}, - {0x80000003, NULL}, - {0x80000004, NULL}, - {0x80000006, set_leaf_8000_0006}, - {0x80000008, set_leaf_8000_0008} + {0x00000000, 0, set_leaf_0000_0000}, + {0x00000001, 0, set_leaf_0000_0001}, + {0x00000002, 0, NULL}, + {0x00000007, 0, NULL}, + {0x00000007, 1, NULL}, + {0x0000000a, 0, NULL}, + {0x00000015, 0, set_leaf_0000_0015}, + {0x00000016, 0, set_leaf_0000_0016}, + {0x40000000, 0, NULL}, + {0x80000000, 0, set_leaf_8000_0000}, + {0x80000001, 0, set_leaf_8000_0001}, + {0x80000002, 0, NULL}, + {0x80000003, 0, NULL}, + {0x80000004, 0, NULL}, + {0x80000006, 0, set_leaf_8000_0006}, + {0x80000008, 0, set_leaf_8000_0008} }; #define CPUID_TOTAL_CONTROLS \ @@ -260,6 +271,12 @@ void cpuid_init_supported_features(void) .edx = cache.data[1] }; host_supported[1] = (hax_cpuid_entry){ + .function = 0x07, + .index = 0, + .ebx = cache.data[3], + .ecx = cache.data[2] + }; + host_supported[2] = (hax_cpuid_entry){ .function = 0x80000001, .ecx = cache.data[4], .edx = cache.data[5] @@ -310,6 +327,12 @@ void cpuid_init_supported_features(void) FEATURE(HTT) }; hax_supported[1] = (hax_cpuid_entry){ + .function = 0x07, + .index = 0, + .ebx = + FEATURE(ERMS) + }; + hax_supported[2] = (hax_cpuid_entry){ .function = 0x80000001, .edx = FEATURE(NX) | @@ -346,7 +369,7 @@ void cpuid_guest_init(hax_cpuid_t *cpuid) cpuid_manager = &kCpuidManager[i]; args.eax = cpuid_manager->leaf; - args.ecx = 0; + args.ecx = cpuid_manager->subleaf; if (cpuid_manager->execute != NULL) { // Guest values or processed host values @@ -359,7 +382,7 @@ void cpuid_guest_init(hax_cpuid_t *cpuid) entry = &cpuid->features[i]; entry->function = cpuid_manager->leaf; - entry->index = 0; + entry->index = cpuid_manager->subleaf; entry->flags = 0; entry->eax = args.eax; entry->ebx = args.ebx; @@ -370,11 +393,28 @@ void cpuid_guest_init(hax_cpuid_t *cpuid) dump_features(cpuid->features, CPUID_TOTAL_LEAVES); } +bool cpuid_guest_has_feature(hax_cpuid_t *cpuid, uint32_t feature_key) +{ + hax_cpuid_entry *entry; + + if (cpuid == NULL) + return false; + + entry = find_cpuid_entry(cpuid->features, CPUID_TOTAL_LEAVES, + feature_leaf(feature_key), + feature_subleaf(feature_key)); + + if (entry == NULL) + return false; + + return is_feature_set(entry, feature_key); +} + void cpuid_execute(hax_cpuid_t *cpuid, cpuid_args_t *args) { - int i; uint32_t leaf, subleaf; - hax_cpuid_entry *entry, *supported = NULL; + int i, index, supported = 0; + hax_cpuid_entry *e, *entry = NULL; if (cpuid == NULL || args == NULL) return; @@ -382,23 +422,67 @@ void cpuid_execute(hax_cpuid_t *cpuid, cpuid_args_t *args) leaf = args->eax; subleaf = args->ecx; + // * If the entry is never found with the leaf, the CPUID instruction + // parameters are not supported (supported = 0); + // * If only a unique entry is found, the cached values will always be used + // regardless of whether the subleaf matches (supported = 1); + // * If multiple entries are found and the subleaf matches exactly, the + // cached values will be used (supported = 1); otherwise the instruction + // is regarded as uncached and needs to be re-executed (supported > 1). for (i = 0; i < CPUID_TOTAL_LEAVES; ++i) { - entry = &cpuid->features[i]; - if (entry->function == leaf) { - supported = entry; + e = &cpuid->features[i]; + + if (e->function != leaf) { + if (supported == 0) + continue; + else + break; + } + + if (e->index == subleaf) { + supported = 1; + entry = e; break; } + + if (supported++ != 0) + continue; + + index = i; + entry = e; } - // Return guest values cached during the initialization phase. If the CPUID - // leaf cannot be found, i.e., out of the kCpuidManager list, the processing - // is undecided: - // * Call get_guest_cache() with NULL to return all zeroes; - // * Call asm_cpuid() to return host values. - get_guest_cache(args, supported); + switch (supported) { + case 0: { // unsupported + // If the CPUID leaf cannot be found, i.e., out of the kCpuidManager + // list, the processing is undecided: + // * Return all zeroes; + // * Call asm_cpuid() to return host values. + args->eax = args->ebx = args->ecx = args->edx = 0; + break; + } + case 1: { // cached + // Return guest values cached during the initialization phase. + args->eax = entry->eax; + args->ebx = entry->ebx; + args->ecx = entry->ecx; + args->edx = entry->edx; + break; + } + default: { // uncached + // Call the primary execute() corresponding to the leaf for the + // CPUID instruction. + const cpuid_manager_t *cpuid_manager = &kCpuidManager[index]; + + if (cpuid_manager->execute != NULL) { + cpuid_manager->execute(args); + } + break; + } + } - hax_log(HAX_LOGD, "CPUID %08x %08x: %08x %08x %08x %08x\n", leaf, subleaf, - args->eax, args->ebx, args->ecx, args->edx); + hax_log(HAX_LOGD, "CPUID.(EAX=0x%lx,ECX=%d): %08lx %08lx %08lx %08lx\n", + leaf, subleaf, args->eax, args->ebx, args->ecx, args->edx); } void cpuid_get_features_mask(hax_cpuid_t *cpuid, uint64_t *features_mask) @@ -457,6 +541,109 @@ int cpuid_set_guest_features(hax_cpuid_t *cpuid, hax_cpuid *cpuid_info) return 0; } +static uint32_t feature_leaf(uint32_t feature_key) +{ + cpuid_feature_t feature; + + feature.value = feature_key; + + return feature.leaf_lo | (feature.leaf_hi << 30); +} + +static uint32_t feature_subleaf(uint32_t feature_key) +{ + cpuid_feature_t feature; + + feature.value = feature_key; + + return feature.subleaf_key; +} + +static uint32_t feature_reg(uint32_t feature_key) +{ + cpuid_feature_t feature; + + feature.value = feature_key; + + return feature.reg; +} + +static uint32_t feature_bit(uint32_t feature_key) +{ + cpuid_feature_t feature; + + feature.value = feature_key; + + return 1 << feature.bit; +} + +static uint32_t * get_reg(hax_cpuid_entry *entry, uint32_t feature_key) +{ + uint32_t *reg = NULL; + + if (entry == NULL) + return NULL; + + switch (feature_reg(feature_key)) { + case CPUID_REG_EAX: { + reg = &entry->eax; + break; + } + case CPUID_REG_EBX: { + reg = &entry->ebx; + break; + } + case CPUID_REG_ECX: { + reg = &entry->ecx; + break; + } + case CPUID_REG_EDX: { + reg = &entry->edx; + break; + } + default: { + break; + } + } + + return reg; +} + +static bool is_feature_set(hax_cpuid_entry *entry, uint32_t feature_key) +{ + uint32_t *reg; + + if (entry == NULL) + return false; + + reg = get_reg(entry, feature_key); + + if (reg == NULL) + return false; + + return !!(*reg & feature_bit(feature_key)); +} + +static void update_feature(hax_cpuid_entry *entry, uint32_t feature_key, + bool set) +{ + uint32_t *reg; + + if (entry == NULL) + return; + + reg = get_reg(entry, feature_key); + + if (reg == NULL) + return; + + if (set) { + *reg |= feature_bit(feature_key); + } else { + *reg &= ~feature_bit(feature_key); + } +} + static hax_cpuid_entry * find_cpuid_entry(hax_cpuid_entry *features, uint32_t size, uint32_t function, uint32_t index) @@ -493,22 +680,6 @@ static void dump_features(hax_cpuid_entry *features, uint32_t size) } } -static void get_guest_cache(cpuid_args_t *args, hax_cpuid_entry *entry) -{ - if (args == NULL) - return; - - if (entry == NULL) { - args->eax = args->ebx = args->ecx = args->edx = 0; - return; - } - - args->eax = entry->eax; - args->ebx = entry->ebx; - args->ecx = entry->ecx; - args->edx = entry->edx; -} - static void adjust_0000_0001(cpuid_args_t *args) { #define VIRT_FAMILY 0x06 @@ -590,7 +761,7 @@ static void adjust_8000_0001(cpuid_args_t *args) if (args == NULL) return; - hax_supported = &cache.hax_supported[1]; + hax_supported = &cache.hax_supported[2]; args->eax = args->ebx = 0; // Report only the features specified but turn off any features this @@ -636,18 +807,18 @@ static void execute_0000_0007(cpuid_args_t *args) return; switch (args->ecx) { - case 0: { // Sub-leaf 0 - // The maximum input value for supported leaf 7 sub-leaves - // Bit 09: Supports Enhanced REP MOVSB/STOSB if 1 - // TODO: Add sub-leaf in the CPUID manager so that the feature bits - // can be specified in cache.hax_supported[] during CPUID - // initialization. - args->ebx = cache.data[3] & 0x00000200; + case 0: { // Structured Extended Feature Flags Enumeration Leaf + hax_cpuid_entry *host_supported, *hax_supported; + + host_supported = &cache.host_supported[1]; + hax_supported = &cache.hax_supported[1]; + + args->ebx = host_supported->ebx & hax_supported->ebx; args->eax = args->ecx = args->edx = 0; break; } case 1: { // Structured Extended Feature Enumeration Sub-leaf - get_guest_cache(args, NULL); + args->eax = args->ebx = args->ecx = args->edx = 0; break; } default: { @@ -764,18 +935,20 @@ static void set_feature(hax_cpuid_entry *features, hax_cpuid *cpuid_info, const cpuid_controller_t *cpuid_controller) { hax_cpuid_entry *dest, *src; - uint32_t leaf; + uint32_t leaf, subleaf; if (features == NULL || cpuid_info == NULL) return; leaf = cpuid_controller->leaf; + subleaf = cpuid_controller->subleaf; - dest = find_cpuid_entry(features, CPUID_TOTAL_LEAVES, leaf, 0); + dest = find_cpuid_entry(features, CPUID_TOTAL_LEAVES, leaf, subleaf); if (dest == NULL) return; - src = find_cpuid_entry(cpuid_info->entries, cpuid_info->total, leaf, 0); + src = find_cpuid_entry(cpuid_info->entries, cpuid_info->total, leaf, + subleaf); if (src == NULL) return; @@ -790,8 +963,8 @@ static void set_feature(hax_cpuid_entry *features, hax_cpuid *cpuid_info, return; hax_log(HAX_LOGW, "%s: filtered or unchanged flags:\n", __func__); - hax_log(HAX_LOGW, "leaf: %08lx, eax: %08lx, ebx: %08lx, ecx: %08lx, " - "edx: %08lx\n", leaf, src->eax ^ dest->eax, src->ebx ^ dest->ebx, + hax_log(HAX_LOGW, "CPUID.(EAX=0x%lx,ECX=%d): %08lx %08lx %08lx %08lx\n", + leaf, subleaf, src->eax ^ dest->eax, src->ebx ^ dest->ebx, src->ecx ^ dest->ecx, src->edx ^ dest->edx); } diff --git a/core/include/cpuid.h b/core/include/cpuid.h index e646e676..ab2b2ebe 100644 --- a/core/include/cpuid.h +++ b/core/include/cpuid.h @@ -203,6 +203,7 @@ enum { X86_FEATURE_AVX2 = FEAT(5), /* 0x00000020 Advanced Vector Extensions 2 */ X86_FEATURE_SMEP = FEAT(7), /* 0x00000080 Supervisor-Mode Execution Prevention */ X86_FEATURE_BMI2 = FEAT(8), /* 0x00000100 Bit Manipulation Instruction Set 2 */ + X86_FEATURE_ERMS = FEAT(9), /* 0x00000200 Enhanced REP MOVSB/STOSB */ X86_FEATURE_INVPCID = FEAT(10), /* 0x00000400 INVPCID instruction */ X86_FEATURE_RTM = FEAT(11), /* 0x00000800 Transactional Synchronization Extensions */ X86_FEATURE_RDT_M = FEAT(12), /* 0x00001000 Resource Director Technology Monitoring */ @@ -261,6 +262,7 @@ bool cpuid_host_has_feature_uncached(uint32_t feature_key); void cpuid_init_supported_features(void); uint32_t cpuid_guest_get_size(void); void cpuid_guest_init(hax_cpuid_t *cpuid); +bool cpuid_guest_has_feature(hax_cpuid_t *cpuid, uint32_t feature_key); void cpuid_execute(hax_cpuid_t *cpuid, cpuid_args_t *args); void cpuid_get_features_mask(hax_cpuid_t *cpuid, uint64_t *features_mask); void cpuid_set_features_mask(hax_cpuid_t *cpuid, uint64_t features_mask);