-
Notifications
You must be signed in to change notification settings - Fork 884
Enable setting CPUID feature for guest VCPUs #277
Changes from all commits
87e4f26
084657f
c7da519
72e53fa
8f042b0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,6 +32,15 @@ | |
|
||
#include "include/ia32.h" | ||
|
||
#define CPUID_CACHE_SIZE 6 | ||
|
||
typedef struct cpuid_cache_t { | ||
uint32_t data[CPUID_CACHE_SIZE]; // Host cached features | ||
cpuid_t host_supported; // Physical CPU supported features | ||
cpuid_t hax_supported; // Hypervisor supported features | ||
bool initialized; | ||
} cpuid_cache_t; | ||
|
||
typedef union cpuid_feature_t { | ||
struct { | ||
uint32_t index : 5; | ||
|
@@ -46,6 +55,14 @@ typedef union cpuid_feature_t { | |
uint32_t value; | ||
} cpuid_feature_t; | ||
|
||
static cpuid_cache_t cache = {0}; | ||
|
||
static hax_cpuid_entry * find_cpuid_entry(hax_cpuid *cpuid_info, | ||
uint32_t function, uint32_t index); | ||
static void cpuid_set_0000_0001(cpuid_t *cpuid, hax_cpuid *cpuid_info); | ||
static void cpuid_set_8000_0001(cpuid_t *cpuid, hax_cpuid *cpuid_info); | ||
static void cpuid_set_fixed_features(cpuid_t *cpuid); | ||
|
||
void cpuid_query_leaf(cpuid_args_t *args, uint32_t leaf) | ||
{ | ||
args->eax = leaf; | ||
|
@@ -59,10 +76,10 @@ void cpuid_query_subleaf(cpuid_args_t *args, uint32_t leaf, uint32_t subleaf) | |
asm_cpuid(args); | ||
} | ||
|
||
void cpuid_host_init(cpuid_cache_t *cache) | ||
void cpuid_host_init(void) | ||
{ | ||
cpuid_args_t res; | ||
uint32_t *data = cache->data; | ||
uint32_t *data = cache.data; | ||
|
||
cpuid_query_leaf(&res, 0x00000001); | ||
data[0] = res.ecx; | ||
|
@@ -76,19 +93,19 @@ void cpuid_host_init(cpuid_cache_t *cache) | |
data[4] = res.ecx; | ||
data[5] = res.edx; | ||
|
||
cache->initialized = 1; | ||
cache.initialized = true; | ||
} | ||
|
||
bool cpuid_host_has_feature(cpuid_cache_t *cache, uint32_t feature_key) | ||
bool cpuid_host_has_feature(uint32_t feature_key) | ||
{ | ||
cpuid_feature_t feature; | ||
uint32_t value; | ||
|
||
feature.value = feature_key; | ||
if (!cache->initialized || feature.index >= CPUID_CACHE_SIZE) { | ||
if (!cache.initialized || feature.index >= CPUID_CACHE_SIZE) { | ||
return cpuid_host_has_feature_uncached(feature_key); | ||
} | ||
value = cache->data[feature.index]; | ||
value = cache.data[feature.index]; | ||
if (value & (1 << feature.bit)) { | ||
return true; | ||
} | ||
|
@@ -114,3 +131,235 @@ bool cpuid_host_has_feature_uncached(uint32_t feature_key) | |
} | ||
return false; | ||
} | ||
|
||
void cpuid_init_supported_features(void) | ||
{ | ||
uint32_t bit, flag, function, x86_feature; | ||
|
||
// Initialize host supported features | ||
for (bit = 0; bit < sizeof(uint32_t) * 8; ++bit) { | ||
flag = 1 << bit; | ||
|
||
function = 0x01; | ||
x86_feature = FEATURE_KEY_LEAF(0, function, CPUID_REG_ECX, bit); | ||
if (cpuid_host_has_feature(x86_feature)) { | ||
cache.host_supported.feature_1_ecx |= flag; | ||
} | ||
|
||
x86_feature = FEATURE_KEY_LEAF(1, function, CPUID_REG_EDX, bit); | ||
if (cpuid_host_has_feature(x86_feature)) { | ||
cache.host_supported.feature_1_edx |= flag; | ||
} | ||
|
||
function = 0x80000001; | ||
x86_feature = FEATURE_KEY_LEAF(5, function, CPUID_REG_EDX, bit); | ||
if (cpuid_host_has_feature(x86_feature)) { | ||
cache.host_supported.feature_8000_0001_edx |= flag; | ||
} | ||
} | ||
|
||
hax_log(HAX_LOGI, "%s: host supported features:\n", __func__); | ||
hax_log(HAX_LOGI, "feature_1_ecx: %08lx, feature_1_edx: %08lx\n", | ||
cache.host_supported.feature_1_ecx, | ||
cache.host_supported.feature_1_edx); | ||
hax_log(HAX_LOGI, "feature_8000_0001_ecx: %08lx, " | ||
"feature_8000_0001_edx: %08lx\n", | ||
cache.host_supported.feature_8000_0001_ecx, | ||
cache.host_supported.feature_8000_0001_edx); | ||
|
||
// Initialize HAXM supported features | ||
cache.hax_supported = (cpuid_t){ | ||
.feature_1_ecx = | ||
FEATURE(SSE3) | | ||
FEATURE(SSSE3) | | ||
FEATURE(SSE41) | | ||
FEATURE(SSE42) | | ||
FEATURE(CMPXCHG16B) | | ||
FEATURE(MOVBE) | | ||
FEATURE(AESNI) | | ||
FEATURE(PCLMULQDQ) | | ||
FEATURE(POPCNT), | ||
.feature_1_edx = | ||
FEATURE(PAT) | | ||
FEATURE(FPU) | | ||
FEATURE(VME) | | ||
FEATURE(DE) | | ||
FEATURE(TSC) | | ||
FEATURE(MSR) | | ||
FEATURE(PAE) | | ||
FEATURE(MCE) | | ||
FEATURE(CX8) | | ||
FEATURE(APIC) | | ||
FEATURE(SEP) | | ||
FEATURE(MTRR) | | ||
FEATURE(PGE) | | ||
FEATURE(MCA) | | ||
FEATURE(CMOV) | | ||
FEATURE(CLFSH) | | ||
FEATURE(MMX) | | ||
FEATURE(FXSR) | | ||
FEATURE(SSE) | | ||
FEATURE(SSE2) | | ||
FEATURE(SS) | | ||
FEATURE(PSE) | | ||
FEATURE(HTT), | ||
.feature_8000_0001_ecx = 0, | ||
.feature_8000_0001_edx = | ||
FEATURE(NX) | | ||
FEATURE(SYSCALL) | | ||
FEATURE(RDTSCP) | | ||
FEATURE(EM64T) | ||
}; | ||
|
||
hax_log(HAX_LOGI, "%s: HAXM supported features:\n", __func__); | ||
hax_log(HAX_LOGI, "feature_1_ecx: %08lx, feature_1_edx: %08lx\n", | ||
cache.hax_supported.feature_1_ecx, | ||
cache.hax_supported.feature_1_edx); | ||
hax_log(HAX_LOGI, "feature_8000_0001_ecx: %08lx, " | ||
"feature_8000_0001_edx: %08lx\n", | ||
cache.hax_supported.feature_8000_0001_ecx, | ||
cache.hax_supported.feature_8000_0001_edx); | ||
} | ||
|
||
void cpuid_guest_init(cpuid_t *cpuid) | ||
{ | ||
*cpuid = cache.hax_supported; | ||
cpuid->features_mask = ~0ULL; | ||
} | ||
|
||
void cpuid_get_features_mask(cpuid_t *cpuid, uint64_t *features_mask) | ||
{ | ||
*features_mask = cpuid->features_mask; | ||
} | ||
|
||
void cpuid_set_features_mask(cpuid_t *cpuid, uint64_t features_mask) | ||
{ | ||
cpuid->features_mask = features_mask; | ||
} | ||
|
||
void cpuid_get_guest_features(cpuid_t *cpuid, uint32_t *cpuid_1_features_ecx, | ||
uint32_t *cpuid_1_features_edx, | ||
uint32_t *cpuid_8000_0001_features_ecx, | ||
uint32_t *cpuid_8000_0001_features_edx) | ||
{ | ||
*cpuid_1_features_ecx = cpuid->feature_1_ecx; | ||
*cpuid_1_features_edx = cpuid->feature_1_edx; | ||
*cpuid_8000_0001_features_ecx = cpuid->feature_8000_0001_ecx; | ||
*cpuid_8000_0001_features_edx = cpuid->feature_8000_0001_edx; | ||
} | ||
|
||
void cpuid_set_guest_features(cpuid_t *cpuid, hax_cpuid *cpuid_info) | ||
{ | ||
static void (*cpuid_set_guest_feature[])(cpuid_t *, hax_cpuid *) = { | ||
cpuid_set_0000_0001, | ||
cpuid_set_8000_0001 | ||
}; | ||
static size_t count = sizeof(cpuid_set_guest_feature) / | ||
sizeof(cpuid_set_guest_feature[0]); | ||
int i; | ||
|
||
hax_log(HAX_LOGI, "%s: before:\n", __func__); | ||
hax_log(HAX_LOGI, "feature_1_ecx: %08lx, feature_1_edx: %08lx\n", | ||
cpuid->feature_1_ecx, cpuid->feature_1_edx); | ||
hax_log(HAX_LOGI, "feature_8000_0001_ecx: %08lx, feature_8000_0001_edx: %08lx" | ||
"\n", cpuid->feature_8000_0001_ecx, cpuid->feature_8000_0001_edx); | ||
|
||
for (i = 0; i < count; ++i) { | ||
cpuid_set_guest_feature[i](cpuid, cpuid_info); | ||
} | ||
|
||
hax_log(HAX_LOGI, "%s: after:\n", __func__); | ||
hax_log(HAX_LOGI, "feature_1_ecx: %08lx, feature_1_edx: %08lx\n", | ||
cpuid->feature_1_ecx, cpuid->feature_1_edx); | ||
hax_log(HAX_LOGI, "feature_8000_0001_ecx: %08lx, feature_8000_0001_edx: %08lx" | ||
"\n", cpuid->feature_8000_0001_ecx, cpuid->feature_8000_0001_edx); | ||
} | ||
|
||
static hax_cpuid_entry * find_cpuid_entry(hax_cpuid *cpuid_info, | ||
uint32_t function, uint32_t index) | ||
{ | ||
int i; | ||
hax_cpuid_entry *entry, *found = NULL; | ||
|
||
for (i = 0; i < cpuid_info->total; ++i) { | ||
entry = &cpuid_info->entries[i]; | ||
if (entry->function == function && entry->index == index) { | ||
found = entry; | ||
break; | ||
} | ||
} | ||
|
||
return found; | ||
} | ||
|
||
static void cpuid_set_0000_0001(cpuid_t *cpuid, hax_cpuid *cpuid_info) | ||
{ | ||
const uint32_t kFunction = 0x01; | ||
hax_cpuid_entry *entry; | ||
|
||
entry = find_cpuid_entry(cpuid_info, kFunction, 0); | ||
if (entry == NULL) | ||
return; | ||
|
||
hax_log(HAX_LOGI, "%s: function: %08lx, index: %lu, flags: %08lx\n", | ||
__func__, entry->function, entry->index, entry->flags); | ||
hax_log(HAX_LOGI, "%s: eax: %08lx, ebx: %08lx, ecx: %08lx, edx: %08lx\n", | ||
__func__, entry->eax, entry->ebx, entry->ecx, entry->edx); | ||
|
||
cpuid->feature_1_ecx = entry->ecx; | ||
cpuid->feature_1_edx = entry->edx; | ||
|
||
// Filter the unsupported features | ||
cpuid->feature_1_ecx &= cache.host_supported.feature_1_ecx & | ||
cache.hax_supported.feature_1_ecx; | ||
cpuid->feature_1_edx &= cache.host_supported.feature_1_edx & | ||
cache.hax_supported.feature_1_edx; | ||
|
||
// Set fixed supported features | ||
cpuid_set_fixed_features(cpuid); | ||
|
||
if (entry->ecx != cpuid->feature_1_ecx || | ||
entry->edx != cpuid->feature_1_edx) { | ||
hax_log(HAX_LOGW, "%s: filtered or unchanged flags: ecx: %08lx, " | ||
"edx: %08lx\n", __func__, entry->ecx ^ cpuid->feature_1_ecx, | ||
entry->edx ^ cpuid->feature_1_edx); | ||
} | ||
} | ||
|
||
static void cpuid_set_8000_0001(cpuid_t *cpuid, hax_cpuid *cpuid_info) | ||
{ | ||
const uint32_t kFunction = 0x80000001; | ||
hax_cpuid_entry *entry; | ||
|
||
entry = find_cpuid_entry(cpuid_info, kFunction, 0); | ||
if (entry == NULL) | ||
return; | ||
|
||
hax_log(HAX_LOGI, "%s: function: %08lx, index: %lu, flags: %08lx\n", | ||
__func__, entry->function, entry->index, entry->flags); | ||
hax_log(HAX_LOGI, "%s: eax: %08lx, ebx: %08lx, ecx: %08lx, edx: %08lx\n", | ||
__func__, entry->eax, entry->ebx, entry->ecx, entry->edx); | ||
|
||
cpuid->feature_8000_0001_edx = entry->edx; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ecx is not merged. CUrrently supported by haxm bits are zero but this will change in future. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are right. This version is based on the current implementation and this feature will be easy to extend in future. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We will update the implementation of CPUID and include a hash table for feature set, which will add ecx or other register settings. It will not change the HAX_VCPU_IOCTL_SET_CPUID interface. The patch will be ready once the current release completes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A hash table for searching entries by find_cpuid_entry? There are currently only two codes with such flags, a vector is sufficient for this task. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same data structure hax_cpuid_entry vector for storing in hax_cpuid_t for future extensions in this version. The find_cpuid_entry() will not change. |
||
|
||
// Filter the unsupported features | ||
cpuid->feature_8000_0001_edx &= | ||
cache.host_supported.feature_8000_0001_edx & | ||
cache.hax_supported.feature_8000_0001_edx; | ||
|
||
if (entry->edx != cpuid->feature_8000_0001_edx) { | ||
hax_log(HAX_LOGW, "%s: filtered or unchanged flags: edx: %08lx\n", | ||
__func__, entry->edx ^ cpuid->feature_8000_0001_edx); | ||
} | ||
} | ||
|
||
static void cpuid_set_fixed_features(cpuid_t *cpuid) | ||
{ | ||
const uint32_t kFixedFeatures = | ||
FEATURE(MCE) | | ||
FEATURE(APIC) | | ||
FEATURE(MTRR) | | ||
FEATURE(PAT); | ||
|
||
cpuid->feature_1_edx |= kFixedFeatures; | ||
Comment on lines
+358
to
+364
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Silently adding flags to the ones provided by the user without even log this. I think when the user want to clear these bits this should be a error. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually these flags were referred from your commits. As for logs, it will always output the unchanged bits as warning in the caller functions, such as cpuid_set_0000_0001(). |
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are 3 types of action with the actual supported features and the ones provided in ioctl:
Would be good add this as flags to the interface, at least in future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your suggestion. We will consider these cases in future.