# Commit 53307f5c5d71b0e05b7840fcae4ad76e0fca9fa0
# Date 2023-08-03 19:14:19 +0100
# Author Roger Pau Monne <roger.pau@citrix.com>
# Committer Andrew Cooper <andrew.cooper3@citrix.com>
libxl: introduce MSR data in libxl_cpuid_policy

Add a new array field to libxl_cpuid_policy in order to store the MSR
policies.

Adding the MSR data in the libxl_cpuid_policy_list type is done so
that existing users can seamlessly pass MSR features as part of the
CPUID data, without requiring the introduction of a separate
domain_build_info field, and a new set of handlers functions.

Note that support for parsing the old JSON format is kept, as that's
required in order to restore domains or received migrations from
previous tool versions.  Differentiation between the old and the new
formats is done based on whether the contents of the 'cpuid' field is
an array or a map JSON object.

Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
Reviewed-by: Anthony PERARD <anthony.perard@citrix.com>
(cherry picked from commit 5b80cecb747b2176b9e85f6e7aa7be83416d77e1)

--- a/tools/libxl/libxl_cpuid.c
+++ b/tools/libxl/libxl_cpuid.c
@@ -16,7 +16,7 @@
 
 int libxl__cpuid_policy_is_empty(libxl_cpuid_policy_list *pl)
 {
-    return !libxl_cpuid_policy_list_length(pl);
+    return !*pl || !libxl_cpuid_policy_list_length(pl);
 }
 
 void libxl_cpuid_dispose(libxl_cpuid_policy_list *p_cpuid_list)
@@ -466,16 +466,22 @@ static const char *input_names[2] = { "l
 static const char *policy_names[4] = { "eax", "ebx", "ecx", "edx" };
 /*
  * Aiming for:
- * [
- *     { 'leaf':    'val-eax',
- *       'subleaf': 'val-ecx',
- *       'eax':     'filter',
- *       'ebx':     'filter',
- *       'ecx':     'filter',
- *       'edx':     'filter' },
- *     { 'leaf':    'val-eax', ..., 'eax': 'filter', ... },
- *     ... etc ...
- * ]
+ * {   'cpuid': [
+ *              { 'leaf':    'val-eax',
+ *                'subleaf': 'val-ecx',
+ *                'eax':     'filter',
+ *                'ebx':     'filter',
+ *                'ecx':     'filter',
+ *                'edx':     'filter' },
+ *              { 'leaf':    'val-eax', ..., 'eax': 'filter', ... },
+ *              ... etc ...
+ *     ],
+ *     'msr': [
+ *            { 'index': 'val-index',
+ *              'policy': 'filter', },
+ *              ... etc ...
+ *     ],
+ * }
  */
 
 yajl_gen_status libxl_cpuid_policy_list_gen_json(yajl_gen hand,
@@ -485,12 +491,21 @@ yajl_gen_status libxl_cpuid_policy_list_
     yajl_gen_status s;
     int i, j;
 
+    s = yajl_gen_map_open(hand);
+    if (s != yajl_gen_status_ok) goto out;
+
+    s = libxl__yajl_gen_asciiz(hand, "cpuid");
+    if (s != yajl_gen_status_ok) goto out;
+
     s = yajl_gen_array_open(hand);
     if (s != yajl_gen_status_ok) goto out;
 
     if (cpuid == NULL) goto empty;
 
     for (i = 0; cpuid[i].input[0] != XEN_CPUID_INPUT_UNUSED; i++) {
+        if ( cpuid[i].input[0] == XC_X86_MSR_LEAF )
+            continue;
+
         s = yajl_gen_map_open(hand);
         if (s != yajl_gen_status_ok) goto out;
 
@@ -518,6 +533,44 @@ yajl_gen_status libxl_cpuid_policy_list_
 
 empty:
     s = yajl_gen_array_close(hand);
+    if (s != yajl_gen_status_ok) goto out;
+
+    s = libxl__yajl_gen_asciiz(hand, "msr");
+    if (s != yajl_gen_status_ok) goto out;
+
+    s = yajl_gen_array_open(hand);
+    if (s != yajl_gen_status_ok) goto out;
+
+    if (cpuid == NULL) goto done;
+
+    for (i = 0; cpuid[i].input[0] != XEN_CPUID_INPUT_UNUSED; i++) {
+        char msr[32 * 2 + 1];
+
+        if ( cpuid[i].input[0] != XC_X86_MSR_LEAF )
+            continue;
+
+        s = yajl_gen_map_open(hand);
+        if (s != yajl_gen_status_ok) goto out;
+
+        s = libxl__yajl_gen_asciiz(hand, "index");
+        if (s != yajl_gen_status_ok) goto out;
+        s = yajl_gen_integer(hand, cpuid[i].input[1]);
+        if (s != yajl_gen_status_ok) goto out;
+        s = libxl__yajl_gen_asciiz(hand, "policy");
+        if (s != yajl_gen_status_ok) goto out;
+        snprintf(msr, ARRAY_SIZE(msr), "%.32s%.32s",
+                 cpuid[i].policy[3], cpuid[i].policy[0]);
+        s = yajl_gen_string(hand, (const unsigned char *)msr, 64);
+        if (s != yajl_gen_status_ok) goto out;
+
+        s = yajl_gen_map_close(hand);
+        if (s != yajl_gen_status_ok) goto out;
+    }
+
+done:
+    s = yajl_gen_array_close(hand);
+    if (s != yajl_gen_status_ok) goto out;
+    s = yajl_gen_map_close(hand);
 out:
     return s;
 }
@@ -526,27 +579,48 @@ int libxl__cpuid_policy_list_parse_json(
                                         const libxl__json_object *o,
                                         libxl_cpuid_policy_list *p)
 {
-    int i, size;
+    int i = 0, j, size, msize = 0;
     libxl_cpuid_policy_list l;
+    const libxl__json_object *co, *mo = NULL;
     flexarray_t *array;
 
-    if (!libxl__json_object_is_array(o))
+    /*
+     * Old JSON field was an array with just the CPUID data.  With the addition
+     * of MSRs the object is now a map with two array fields.
+     *
+     * Use the object format to detect whether the passed data contains just
+     * CPUID leafs and thus is an array, or does also contain MSRs and is a
+     * map.
+     */
+    if (libxl__json_object_is_array(o)) {
+        co = o;
+        goto parse_cpuid;
+    }
+
+    if (!libxl__json_object_is_map(o))
         return ERROR_FAIL;
 
-    array = libxl__json_object_get_array(o);
-    if (!array->count)
-        return 0;
+    co = libxl__json_map_get("cpuid", o, JSON_ARRAY);
+    if (!libxl__json_object_is_array(co))
+        return ERROR_FAIL;
 
+    mo = libxl__json_map_get("msr", o, JSON_ARRAY);
+    if (!libxl__json_object_is_array(mo))
+        return ERROR_FAIL;
+    msize = libxl__json_object_get_array(mo)->count;
+
+parse_cpuid:
+    array = libxl__json_object_get_array(co);
     size = array->count;
+
     /* need one extra slot as sentinel */
-    l = *p = libxl__calloc(NOGC, size + 1, sizeof(libxl_cpuid_policy));
+    l = *p = libxl__calloc(NOGC, size + msize + 1, sizeof(**p));
 
-    l[size].input[0] = XEN_CPUID_INPUT_UNUSED;
-    l[size].input[1] = XEN_CPUID_INPUT_UNUSED;
+    l[size + msize].input[0] = XEN_CPUID_INPUT_UNUSED;
+    l[size + msize].input[1] = XEN_CPUID_INPUT_UNUSED;
 
     for (i = 0; i < size; i++) {
         const libxl__json_object *t;
-        int j;
 
         if (flexarray_get(array, i, (void**)&t) != 0)
             return ERROR_FAIL;
@@ -576,6 +650,38 @@ int libxl__cpuid_policy_list_parse_json(
         }
     }
 
+    if (!mo)
+        return 0;
+
+    array = libxl__json_object_get_array(mo);
+
+    for (j = 0; j < msize; i++, j++) {
+        const libxl__json_object *t, *r;
+        char msr[32 * 2 + 1];
+
+        if (flexarray_get(array, j, (void**)&t) != 0)
+            return ERROR_FAIL;
+
+        if (!libxl__json_object_is_map(t))
+            return ERROR_FAIL;
+
+        r = libxl__json_map_get("index", t, JSON_INTEGER);
+        if (!r) return ERROR_FAIL;
+        l[i].input[0] = XC_X86_MSR_LEAF;
+        l[i].input[1] = libxl__json_object_get_integer(r);
+        r = libxl__json_map_get("policy", t, JSON_STRING);
+        if (!r) return ERROR_FAIL;
+        if (strlen(libxl__json_object_get_string(r)) !=
+            ARRAY_SIZE(msr) - 1)
+            return ERROR_FAIL;
+        strcpy(msr, libxl__json_object_get_string(r));
+        l[i].policy[0] = libxl__strdup(NOGC, msr + 32);
+        l[i].policy[1] = NULL;
+        l[i].policy[2] = NULL;
+        msr[32] = 0;
+        l[i].policy[3] = libxl__strdup(NOGC, msr);
+    }
+
     return 0;
 }
 
--- a/tools/libxl/libxl_types.idl
+++ b/tools/libxl/libxl_types.idl
@@ -19,7 +19,7 @@ libxl_mac = Builtin("mac", json_parse_ty
 libxl_bitmap = Builtin("bitmap", json_parse_type="JSON_ARRAY", dispose_fn="libxl_bitmap_dispose", passby=PASS_BY_REFERENCE,
                        check_default_fn="libxl_bitmap_is_empty", copy_fn="libxl_bitmap_copy_alloc")
 libxl_cpuid_policy_list = Builtin("cpuid_policy_list", dispose_fn="libxl_cpuid_dispose", passby=PASS_BY_REFERENCE,
-                                  json_parse_type="JSON_ARRAY", check_default_fn="libxl__cpuid_policy_is_empty",
+                                  json_parse_type="JSON_ANY", check_default_fn="libxl__cpuid_policy_is_empty",
                                   copy_fn="libxl_cpuid_policy_list_copy")
 
 libxl_string_list = Builtin("string_list", dispose_fn="libxl_string_list_dispose", passby=PASS_BY_REFERENCE,
