| /* |
| * Copyright 2018 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include <errno.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "crosvm.h" |
| |
| typedef int (*crosvm_function)(struct crosvm*, uint32_t, |
| struct kvm_cpuid_entry2*, uint32_t*); |
| typedef int (*vcpu_function)(struct crosvm_vcpu*, uint32_t, |
| struct kvm_cpuid_entry2*, uint32_t*); |
| |
| // Members of union should only differ by the pointer type of 1st arg. |
| union cpuid_function { |
| crosvm_function crosvm; |
| vcpu_function vcpu; |
| }; |
| |
| int test_cpuid(void* crosvm, union cpuid_function funct, const char* name) { |
| struct kvm_cpuid_entry2 cpuids[100]; |
| int n_entries = 0; |
| int ret = funct.crosvm(crosvm, 1, cpuids, &n_entries); |
| if (ret >= 0) { |
| fprintf(stderr, |
| "expected %s to fail with E2BIG\n", name); |
| return ret; |
| } |
| |
| ret = funct.crosvm(crosvm, 100, cpuids, &n_entries); |
| if (ret < 0) { |
| if (ret != -EINVAL) { |
| fprintf(stderr, "unexpected failure of %s: %d\n", name, ret); |
| } else { |
| fprintf(stderr, |
| "Query of %s failed with EINVAL (may be expected)\n", |
| name, ret); |
| } |
| return ret; |
| } |
| |
| if (n_entries <= 1) { |
| fprintf(stderr, |
| "unexpected number of cpuid entries from %s: %d\n", |
| name, n_entries); |
| return 1; |
| } |
| return 0; |
| } |
| |
| int main(int argc, char** argv) { |
| struct crosvm* crosvm = NULL; |
| int ret = crosvm_connect(&crosvm); |
| if (ret) { |
| fprintf(stderr, "failed to connect to crosvm: %d\n", ret); |
| return 1; |
| } |
| |
| struct crosvm_vcpu* vcpu = NULL; |
| ret = crosvm_get_vcpu(crosvm, 0, &vcpu); |
| if (ret) { |
| fprintf(stderr, "failed to get vcpu #0: %d\n", ret); |
| return 1; |
| } |
| |
| union cpuid_function funct; |
| funct.crosvm = crosvm_get_supported_cpuid; |
| if (test_cpuid(crosvm, funct, "crosvm_get_supported_cpuid")) { |
| return 1; |
| } |
| funct.crosvm = crosvm_get_emulated_cpuid; |
| if (test_cpuid(crosvm, funct, "crosvm_get_emulated_cpuid")) { |
| return 1; |
| } |
| |
| ret = crosvm_start(crosvm); |
| if (ret) { |
| fprintf(stderr, "failed to start vm: %d\n", ret); |
| return 1; |
| } |
| |
| struct crosvm_vcpu_event evt = {0}; |
| ret = crosvm_vcpu_wait(vcpu, &evt); |
| if (ret) { |
| fprintf(stderr, "failed to wait for vm start: %d\n", ret); |
| return 1; |
| } |
| if (evt.kind != CROSVM_VCPU_EVENT_KIND_INIT) { |
| fprintf(stderr, "Got unexpected exit type: %d\n", evt.kind); |
| return 1; |
| } |
| |
| funct.vcpu = crosvm_get_hyperv_cpuid; |
| ret = test_cpuid(vcpu, funct, "crosvm_get_hyperv_cpuid"); |
| // Older kernels don't support and return EINVAL, so allow this for now. |
| if (ret && ret != -EINVAL) { |
| fprintf(stderr, "Ignoring failure of crosvm_get_hyperv_cpuid\n"); |
| return 1; |
| } |
| return 0; |
| } |