@@ -3,7 +3,7 @@ XEN_ROOT=$(TOPLEVEL)/../..
include $(TOPLEVEL)/common.make
CFLAGS += -I../mmap $(CFLAGS_libxenctrl) $(CFLAGS_libxenguest)
-CFLAGS += $(APPEND_CFLAGS)
+CFLAGS += $(APPEND_CFLAGS) -Wno-declaration-after-statement
OCAMLINCLUDE += -I ../mmap
OBJS = xenctrl
@@ -286,6 +286,43 @@ external version_capabilities: handle -> string =
type featureset_index = Featureset_raw | Featureset_host | Featureset_pv | Featureset_hvm
external get_cpu_featureset : handle -> featureset_index -> int64 array = "stub_xc_get_cpu_featureset"
+(* order must match the order in Val_cpuid_leaf *)
+type xen_cpuid_leaf = {
+ leaf: int64;
+ subleaf: int64;
+ a: int64;
+ b: int64;
+ c: int64;
+ d: int64;
+}
+
+(* order must match the order in Val_msr_entry *)
+type xen_msr_entry = {
+ idx: int64;
+ flags: int64;
+ value: int64; (* val is a keyword, using 'value' *)
+}
+
+type xen_cpu_policy = {
+ leaves: xen_cpuid_leaf array;
+ msrs: xen_msr_entry array;
+}
+
+(* must match XEN_SYSCTL_cpu_policy* order in xen/include/public/sysctl.h *)
+type xen_cpu_policy_index = Cpu_policy_raw | Cpu_policy_host | Cpu_policy_pv_max | Cpu_policy_hvm_max | Cpu_policy_pv_default | Cpu_policy_hvm_default
+
+let string_of_xen_cpu_policy_index = function
+ | Cpu_policy_raw -> "Raw"
+ | Cpu_policy_host -> "Host"
+ | Cpu_policy_pv_max -> "PV Max"
+ | Cpu_policy_hvm_max -> "HVM Max"
+ | Cpu_policy_pv_default -> "PV default"
+ | Cpu_policy_hvm_default -> "HVM default"
+
+external get_system_cpu_policy: handle -> xen_cpu_policy_index -> xen_cpu_policy = "stub_xc_get_system_cpu_policy"
+
+external cpu_policy_to_featureset: handle -> xen_cpu_policy -> int64 array = "stub_xc_policy_to_featureset"
+
external watchdog : handle -> int -> int32 -> int
= "stub_xc_watchdog"
@@ -223,6 +223,77 @@ external version_capabilities : handle -> string
type featureset_index = Featureset_raw | Featureset_host | Featureset_pv | Featureset_hvm
external get_cpu_featureset : handle -> featureset_index -> int64 array = "stub_xc_get_cpu_featureset"
+(** CPUID takes a leaf (EAX) and optional subleaf (ECX) as input and
+ returns feature information bitset in 4 registers (EAX, EBX, ECX, EDX).
+ This record captures one such invocation of CPUID.
+
+ CPU manuals contain tables explaining the available leaves/subleaves and feature bits:
+
+ https://software.intel.com/content/www/us/en/develop/articles/intel-sdm.html
+ Intel® 64 and IA-32 architectures software developer's manual volume 2A: Instruction set reference
+ Chapter 3.2, Table 3-8
+
+ https://developer.amd.com/resources/developer-guides-manuals/
+ AMD64 Architecture Programmer’s Manual Volume 3: General Purpose and System Instructions
+ Appendix D Instruction Subsets and CPUID Feature Flags
+ *)
+type xen_cpuid_leaf = {
+ leaf: int64; (** initial EAX value *)
+ subleaf: int64; (** initial ECX value *)
+ a: int64; (** EAX result *)
+ b: int64; (** EBX result *)
+ c: int64; (** ECX result *)
+ d: int64; (** EDX result *)
+}
+
+(** CPU Model Specific Registers control various aspects of CPU behaviour.
+
+ RDMSR takes ECX as input and returns its result in EDX:EAX.
+ This record captures one invocation of RDMSR.
+
+ CPU manuals document the available MSRs and feature bits
+
+ https://software.intel.com/content/www/us/en/develop/articles/intel-sdm.html
+ Intel® 64 and IA-32 architectures software developer's manual volume 4: Model-specific registers
+ Chapter 2, "Model-Specific Registers (MSRs)"
+
+ https://developer.amd.com/resources/developer-guides-manuals/
+ AMD64 Architecture Programmer’s Manual Volume 2: System Programming
+ Appendix A "MSR Cross-Reference"
+ *)
+type xen_msr_entry = {
+ idx: int64; (** MSR register - ECX input *)
+ flags: int64; (** reserved, must be zero *)
+ value: int64; (** EDX:EAX output *)
+}
+
+(** Xen CPU policy contains the CPUID features and MSRs visible in a domain.
+ The order of leaves and MSRs is not important, but entries cannot be duplicated.
+ *)
+type xen_cpu_policy = {
+ leaves: xen_cpuid_leaf array; (** Array of CPUID leaves/ *)
+ msrs: xen_msr_entry array; (** Array of MSRs *)
+}
+
+(** Xen CPU policy to query or set *)
+type xen_cpu_policy_index =
+ | Cpu_policy_raw (** as seen on boot *)
+ | Cpu_policy_host (** features implemented by the host *)
+ | Cpu_policy_pv_max (** maximum PV features that we can accept in a migration: either implemented natively or emulated *)
+ | Cpu_policy_hvm_max (** maximum HVM features that we can accept in a migration: either implemented natively or emulated *)
+ | Cpu_policy_pv_default (** default PV features for newly booted VMs *)
+ | Cpu_policy_hvm_default (** default HVM features for newly booted VMs *)
+
+(** [string_of_xen_cpu_policy_index policy_index] is the name of the [policy_index] policy *)
+val string_of_xen_cpu_policy_index : xen_cpu_policy_index -> string
+
+(** [get_system_cpu_policy xenctrlhandle policy_index] retrieves the [policy_index] policy from the running hypervisor *)
+external get_system_cpu_policy: handle -> xen_cpu_policy_index -> xen_cpu_policy = "stub_xc_get_system_cpu_policy"
+
+(** [cpu_policy_to_featureset xenctrlhandle policy] converts [policy] to a featureset for backwards compatibility
+ (e.g. accepting incoming migrations in xenopsd from a non-policy-aware xenopsd) *)
+external cpu_policy_to_featureset: handle -> xen_cpu_policy -> int64 array = "stub_xc_policy_to_featureset"
+
external pages_to_kib : int64 -> int64 = "stub_pages_to_kib"
val pages_to_mib : int64 -> int64
external watchdog : handle -> int -> int32 -> int
@@ -34,6 +34,9 @@
#include <xenctrl.h>
#include <xen-tools/libs.h>
+#include <xen/lib/x86/cpuid.h>
+#include <xen/lib/x86/msr.h>
+
#include "mmap_stubs.h"
#define PAGE_SHIFT 12
@@ -1216,6 +1219,198 @@ CAMLprim value stub_xc_watchdog(value xch, value domid, value timeout)
CAMLreturn(Val_int(ret));
}
+static CAMLprim value Val_cpuid_leaf(const xen_cpuid_leaf_t *leaf)
+{
+ CAMLparam0();
+ CAMLlocal1(result);
+ result = caml_alloc_tuple(6);
+ Store_field(result, 0, caml_copy_int64(leaf->leaf));
+ Store_field(result, 1, caml_copy_int64(leaf->subleaf));
+ Store_field(result, 2, caml_copy_int64(leaf->a));
+ Store_field(result, 3, caml_copy_int64(leaf->b));
+ Store_field(result, 4, caml_copy_int64(leaf->c));
+ Store_field(result, 5, caml_copy_int64(leaf->d));
+
+ CAMLreturn(result);
+}
+
+static CAMLprim void cpuid_leaf_of_val(xen_cpuid_leaf_t *leaf, value v)
+{
+ CAMLparam1(v);
+ leaf->leaf = Int64_val(Field(v, 0));
+ leaf->subleaf = Int64_val(Field(v, 1));
+ leaf->a = Int64_val(Field(v, 2));
+ leaf->b = Int64_val(Field(v, 3));
+ leaf->c = Int64_val(Field(v, 4));
+ leaf->d = Int64_val(Field(v, 5));
+
+ CAMLreturn0;
+}
+
+static CAMLprim value Val_msr_entry(const xen_msr_entry_t *msr)
+{
+ CAMLparam0();
+ CAMLlocal1(result);
+ result = caml_alloc_tuple(3);
+ Store_field(result, 0, caml_copy_int64(msr->idx));
+ Store_field(result, 1, caml_copy_int64(msr->flags));
+ Store_field(result, 2, caml_copy_int64(msr->val));
+ CAMLreturn(result);
+}
+
+#if 0
+static CAMLprim void msr_entry_of_val(xen_msr_entry_t *msr, value v)
+{
+ CAMLparam1(v);
+ msr->idx = Int64_val(Field(v, 0));
+ msr->flags = Int64_val(Field(v, 1));
+ msr->val = Int64_val(Field(v, 2));
+ CAMLreturn0;
+}
+#endif
+
+static CAMLprim value Val_leaves(const xen_cpuid_leaf_t *leaves, uint32_t nr_leaves)
+{
+ CAMLparam0();
+ CAMLlocal1(result);
+ uint32_t i;
+
+ result = caml_alloc(nr_leaves, 0);
+ for (i=0;i<nr_leaves;i++)
+ Store_field(result, i, Val_cpuid_leaf(&leaves[i]));
+
+ CAMLreturn(result);
+}
+
+static CAMLprim value Val_msrs(const xen_msr_entry_t *msrs, uint32_t nr_msrs)
+{
+ CAMLparam0();
+ CAMLlocal1(result);
+
+ result = caml_alloc(nr_msrs, 0);
+ for (unsigned i=0;i<nr_msrs;i++)
+ Store_field(result, i, Val_msr_entry(&msrs[i]));
+ CAMLreturn(result);
+}
+
+static CAMLprim value Val_policy(const xen_cpuid_leaf_t *leaves, uint32_t nr_leaves, const xen_msr_entry_t *msrs, uint32_t nr_msrs)
+{
+ CAMLparam0();
+ CAMLlocal1(result);
+
+ result = caml_alloc_tuple(2);
+ Store_field(result, 0, Val_leaves(leaves, nr_leaves));
+ Store_field(result, 1, Val_msrs(msrs, nr_msrs));
+ CAMLreturn(result);
+}
+
+static void cpuid_policy_of_val(struct cpuid_policy *p, value policy)
+{
+ CAMLparam1(policy);
+ CAMLlocal1(cpu_policy);
+ uint32_t i;
+
+ cpu_policy = Field(policy, 0);
+
+ uint32_t nr_leaves = caml_array_length(cpu_policy);
+ xen_cpuid_leaf_t leaves[nr_leaves];
+ for (i=0;i<nr_leaves;i++)
+ cpuid_leaf_of_val(&leaves[i], Field(cpu_policy, i));
+
+
+ uint32_t err_leaf=0, err_subleaf=0;
+ int rc = x86_cpuid_copy_from_buffer(p, leaves, nr_leaves, &err_leaf, &err_subleaf);
+ if (rc)
+ caml_failwith("Failed to deserialize CPU policy"); /* TODO: err_leaf/err_subleaf */
+
+ CAMLreturn0;
+}
+
+#if 0
+static void msr_policy_of_val(struct msr_policy *p, value policy)
+{
+ CAMLparam1(policy);
+ CAMLlocal1(msr_policy);
+ uint32_t i;
+
+ msr_policy = Field(policy, 1);
+
+ uint32_t nr_msrs = caml_array_length(msr_policy);
+ xen_msr_entry_t msrs[nr_msrs];
+ for (i=0;i<nr_msrs;i++)
+ msr_entry_of_val(&msrs[i], Field(msr_policy, i));
+
+ uint32_t err_msr = 0;
+ int rc = x86_msr_copy_from_buffer(p, msrs, nr_msrs, &err_msr);
+ if (rc)
+ caml_failwith("Failed to deserialize CPU policy"); /* TODO: err_msr */
+
+ CAMLreturn0;
+}
+#endif
+
+CAMLprim value stub_xc_get_system_cpu_policy(value xch, value policy_kind)
+{
+ CAMLparam2(xch, policy_kind);
+ CAMLlocal1(result);
+
+ uint32_t max_leaves = 0, max_msrs = 0;
+
+ if (xc_cpu_policy_get_size(_H(xch), &max_leaves, &max_msrs))
+ failwith_xc(_H(xch));
+
+ xen_cpuid_leaf_t leaves[max_leaves];
+ xen_msr_entry_t msrs[max_msrs];
+ memset(leaves, 0, sizeof(leaves));
+ memset(msrs, 0, sizeof(msrs));
+
+ /* It'd be simpler if we could avoid this allocation here,
+ but the type is private */
+ xc_cpu_policy_t *policy = xc_cpu_policy_init();
+ if (!policy)
+ caml_raise_out_of_memory();
+
+ int rc;
+ rc = xc_cpu_policy_get_system(_H(xch), Int_val(policy_kind), policy) ||
+ xc_cpu_policy_serialise(_H(xch), policy, leaves, &max_leaves, msrs, &max_msrs);
+ xc_cpu_policy_destroy(policy);
+ if (rc)
+ failwith_xc(_H(xch));
+
+ result = Val_policy(leaves, max_leaves, msrs, max_msrs);
+ CAMLreturn(result);
+}
+
+CAMLprim value stub_xc_policy_to_featureset(value xch, value policy)
+{
+ CAMLparam2(xch, policy);
+ CAMLlocal1(result);
+ struct cpuid_policy p;
+
+ memset(&p, 0, sizeof(p));
+ cpuid_policy_of_val(&p, policy);
+
+ uint32_t fs_len;
+ int rc = xc_get_cpu_featureset(_H(xch), 0, &fs_len, NULL);
+ if (rc)
+ failwith_xc(_H(xch));
+ /* xenctrl stub is statically linked, xenctrl is dynamically loaded,
+ * the 2 featureset lengths could be different, but fs_len should be the greater one.
+ * */
+ if (fs_len < FEATURESET_NR_ENTRIES)
+ caml_invalid_argument("cpuid_policy_to_featureset");
+
+ uint32_t featureset[fs_len];
+ memset(featureset, 0, sizeof(featureset));
+ cpuid_policy_to_featureset(&p, featureset);
+
+ result = caml_alloc(fs_len, 0);
+ for(unsigned i=0; i<fs_len; i++)
+ Store_field(result, i, caml_copy_int64(featureset[i]));
+
+ CAMLreturn(result);
+}
+
/*
* Local variables:
* indent-tabs-mode: t
Introduces following functions in Xenctrl and associated types: get_system_cpu_policy cpu_policy_to_featureset, string_of_xen_cpu_policy_index These are wrappers around the existing C functions in xenctrl.h, that will be used by xenopsd initially. -Wno-declaration-after-statement is disabled to allow mixing declarations and code to simplify writing the stubs by using variable length arrays on the stack instead of allocating/freeing memory (which would require additional error-handling logic). Signed-off-by: Edwin Török <edvin.torok@citrix.com> --- tools/ocaml/libs/xc/Makefile | 2 +- tools/ocaml/libs/xc/xenctrl.ml | 37 ++++++ tools/ocaml/libs/xc/xenctrl.mli | 71 ++++++++++ tools/ocaml/libs/xc/xenctrl_stubs.c | 195 ++++++++++++++++++++++++++++ 4 files changed, 304 insertions(+), 1 deletion(-)