Skip to content
This repository has been archived by the owner on Jan 28, 2023. It is now read-only.

Enable setting CPUID feature for guest VCPUs #277

Merged
merged 5 commits into from
Mar 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 1 addition & 13 deletions core/cpu.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,6 @@
#include "include/intr.h"
#include "include/ept.h"

static cpuid_cache_t cache = {
.initialized = 0
};

static void cpu_vmentry_failed(struct vcpu_t *vcpu, vmx_result_t result);
static int cpu_vmexit_handler(struct vcpu_t *vcpu, exit_reason_t exit_reason,
struct hax_tunnel *htun);
Expand All @@ -66,15 +62,7 @@ static int cpu_nx_enable(void)

bool cpu_has_feature(uint32_t feature)
{
if (!cache.initialized) {
cpuid_host_init(&cache);
}
return cpuid_host_has_feature(&cache, feature);
}

void cpu_init_feature_cache(void)
{
cpuid_host_init(&cache);
return cpuid_host_has_feature(feature);
}

void cpu_init_vmx(void *arg)
Expand Down
261 changes: 255 additions & 6 deletions core/cpuid.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
}
Expand All @@ -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) {
Comment on lines +321 to +322
Copy link
Contributor

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:

  1. Use intersection and ignore mismatch. This is currently used.
  2. Use intersection and return error in case of mismatch. The user wanted the provided features and we can't support these.
  3. Use provided features, no intersection with supported. It is users responsibility when the provided features are not supported by the cpu. This is the case of KVM, but it supports a lot of features, so the problems will be in case when the CPU does not support them.
    Would be good add this as flags to the interface, at least in future.

Copy link
Contributor Author

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.

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;
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

@wcwang wcwang Apr 7, 2020

Choose a reason for hiding this comment

The 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
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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().

}
6 changes: 5 additions & 1 deletion core/hax.c
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@ int hax_get_capability(void *buf, int bufLeng, int *outLength)
cap->winfo |= HAX_CAP_TUNNEL_PAGE;
cap->winfo |= HAX_CAP_RAM_PROTECTION;
cap->winfo |= HAX_CAP_DEBUG;
cap->winfo |= HAX_CAP_CPUID;
if (cpu_data->vmx_info._ept_cap) {
cap->winfo |= HAX_CAP_EPT;
}
Expand Down Expand Up @@ -565,7 +566,10 @@ int hax_module_init(void)
hax_clear_page(hax_cpu_data[cpu_id]->hstate.hfxpage);
hax_cpu_data[cpu_id]->cpu_id = cpu_id;
}
cpu_init_feature_cache();

cpuid_host_init();
cpuid_init_supported_features();

if (hax_vmx_init() < 0)
goto out_2;

Expand Down
Loading