mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com> Reviewed-by: Jan Beulich <jbeulich@suse.com>
835 lines
26 KiB
C
835 lines
26 KiB
C
/*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published
|
|
* by the Free Software Foundation; version 2.1 only. with the special
|
|
* exception on linking described in file LICENSE.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*/
|
|
|
|
#include "libxl_osdeps.h" /* must come before any other headers */
|
|
|
|
#include "libxl_internal.h"
|
|
|
|
#include <xen/lib/x86/cpu-policy.h>
|
|
|
|
int libxl__cpuid_policy_is_empty(libxl_cpuid_policy_list *pl)
|
|
{
|
|
return !*pl || (!libxl_cpuid_policy_list_length(pl) && !(*pl)->msr);
|
|
}
|
|
|
|
void libxl_cpuid_dispose(libxl_cpuid_policy_list *pl)
|
|
{
|
|
libxl_cpuid_policy_list policy = *pl;
|
|
|
|
if (policy == NULL)
|
|
return;
|
|
|
|
if (policy->cpuid) {
|
|
unsigned int i, j;
|
|
struct xc_xend_cpuid *cpuid_list = policy->cpuid;
|
|
|
|
for (i = 0; cpuid_list[i].input[0] != XEN_CPUID_INPUT_UNUSED; i++) {
|
|
for (j = 0; j < 4; j++) {
|
|
if (cpuid_list[i].policy[j] != NULL) {
|
|
free(cpuid_list[i].policy[j]);
|
|
}
|
|
}
|
|
}
|
|
free(policy->cpuid);
|
|
}
|
|
|
|
free(policy->msr);
|
|
|
|
free(policy);
|
|
*pl = NULL;
|
|
return;
|
|
}
|
|
|
|
#define CPUID_REG_INV 0
|
|
#define CPUID_REG_EAX 1
|
|
#define CPUID_REG_EBX 2
|
|
#define CPUID_REG_ECX 3
|
|
#define CPUID_REG_EDX 4
|
|
|
|
/* mapping CPUID features to names
|
|
* holds a "name" for each feature, specified by the "leaf" number (and an
|
|
* optional "subleaf" in ECX), the "reg"ister (EAX-EDX) used and a number of
|
|
* bits starting with "bit" and being "length" bits long.
|
|
* Used for the static structure describing all features.
|
|
*/
|
|
struct cpuid_flags {
|
|
const char *name;
|
|
uint32_t leaf;
|
|
uint32_t subleaf;
|
|
int reg;
|
|
int bit;
|
|
int length;
|
|
};
|
|
|
|
/* go through the dynamic array finding the entry for a specified leaf.
|
|
* if no entry exists, allocate one and return that.
|
|
*/
|
|
static struct xc_xend_cpuid *cpuid_find_match(libxl_cpuid_policy_list *pl,
|
|
uint32_t leaf, uint32_t subleaf)
|
|
{
|
|
libxl_cpuid_policy_list policy = *pl;
|
|
struct xc_xend_cpuid **list;
|
|
int i = 0;
|
|
|
|
if (policy == NULL)
|
|
policy = *pl = calloc(1, sizeof(*policy));
|
|
|
|
list = &policy->cpuid;
|
|
if (*list != NULL) {
|
|
for (i = 0; (*list)[i].input[0] != XEN_CPUID_INPUT_UNUSED; i++) {
|
|
if ((*list)[i].input[0] == leaf && (*list)[i].input[1] == subleaf)
|
|
return *list + i;
|
|
}
|
|
}
|
|
*list = realloc(*list, sizeof((*list)[0]) * (i + 2));
|
|
(*list)[i].input[0] = leaf;
|
|
(*list)[i].input[1] = subleaf;
|
|
memset((*list)[i].policy, 0, 4 * sizeof(char*));
|
|
(*list)[i + 1].input[0] = XEN_CPUID_INPUT_UNUSED;
|
|
return *list + i;
|
|
}
|
|
|
|
static int cpuid_add(libxl_cpuid_policy_list *policy,
|
|
const struct cpuid_flags *flag, const char *val)
|
|
{
|
|
struct xc_xend_cpuid *entry = cpuid_find_match(policy, flag->leaf,
|
|
flag->subleaf);
|
|
unsigned long num;
|
|
char flags[33], *resstr, *endptr;
|
|
unsigned int i;
|
|
|
|
resstr = entry->policy[flag->reg - 1];
|
|
num = strtoul(val, &endptr, 0);
|
|
flags[flag->length] = 0;
|
|
if (endptr != val) {
|
|
/* if this was a valid number, write the binary form into the string */
|
|
for (i = 0; i < flag->length; i++) {
|
|
flags[flag->length - 1 - i] = "01"[(num >> i) & 1];
|
|
}
|
|
} else {
|
|
switch(val[0]) {
|
|
case 'x': case 'k': case 's':
|
|
memset(flags, val[0], flag->length);
|
|
break;
|
|
default:
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
if (resstr == NULL) {
|
|
resstr = strdup("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
|
|
}
|
|
|
|
/* the family and model entry is potentially split up across
|
|
* two fields in Fn0000_0001_EAX, so handle them here separately.
|
|
*/
|
|
if (!strcmp(flag->name, "family")) {
|
|
if (num < 16) {
|
|
memcpy(resstr + (32 - 4) - flag->bit, flags + 4, 4);
|
|
memcpy(resstr + (32 - 8) - 20, "00000000", 8);
|
|
} else {
|
|
num -= 15;
|
|
memcpy(resstr + (32 - 4) - flag->bit, "1111", 4);
|
|
for (i = 0; i < 7; i++) {
|
|
flags[7 - i] = "01"[num & 1];
|
|
num >>= 1;
|
|
}
|
|
memcpy(resstr + (32 - 8) - 20, flags, 8);
|
|
}
|
|
} else if (!strcmp(flag->name, "model")) {
|
|
memcpy(resstr + (32 - 4) - 16, flags, 4);
|
|
memcpy(resstr + (32 - 4) - flag->bit, flags + 4, 4);
|
|
} else {
|
|
memcpy(resstr + (32 - flag->length) - flag->bit, flags,
|
|
flag->length);
|
|
}
|
|
entry->policy[flag->reg - 1] = resstr;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct xc_msr *msr_find_match(libxl_cpuid_policy_list *pl, uint32_t idx)
|
|
{
|
|
unsigned int i = 0;
|
|
libxl_cpuid_policy_list policy = *pl;
|
|
|
|
if (policy == NULL)
|
|
policy = *pl = calloc(1, sizeof(*policy));
|
|
|
|
if (policy->msr != NULL) {
|
|
for (i = 0; policy->msr[i].index != XC_MSR_INPUT_UNUSED; i++) {
|
|
if (policy->msr[i].index == idx) {
|
|
return &policy->msr[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
policy->msr = realloc(policy->msr, sizeof(struct xc_msr) * (i + 2));
|
|
policy->msr[i].index = idx;
|
|
memset(policy->msr[i].policy, 'x', ARRAY_SIZE(policy->msr[0].policy) - 1);
|
|
policy->msr[i].policy[ARRAY_SIZE(policy->msr[0].policy) - 1] = '\0';
|
|
policy->msr[i + 1].index = XC_MSR_INPUT_UNUSED;
|
|
|
|
return &policy->msr[i];
|
|
}
|
|
|
|
static int msr_add(libxl_cpuid_policy_list *policy, uint32_t idx,
|
|
unsigned int bit, const char *val)
|
|
{
|
|
struct xc_msr *entry = msr_find_match(policy, idx);
|
|
|
|
/* Only allow options taking a character for MSRs, no values allowed. */
|
|
if (strlen(val) != 1)
|
|
return 3;
|
|
|
|
switch (val[0]) {
|
|
case '0':
|
|
case '1':
|
|
case 'x':
|
|
case 'k':
|
|
entry->policy[63 - bit] = val[0];
|
|
break;
|
|
|
|
case 's':
|
|
/* Translate s -> k as xc_msr doesn't support the deprecated 's'. */
|
|
entry->policy[63 - bit] = 'k';
|
|
break;
|
|
|
|
default:
|
|
return 3;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct feature_name {
|
|
const char *name;
|
|
unsigned int bit;
|
|
};
|
|
|
|
static int search_feature(const void *a, const void *b)
|
|
{
|
|
const char *key = a;
|
|
const char *feat = ((const struct feature_name *)b)->name;
|
|
|
|
return strcmp(key, feat);
|
|
}
|
|
|
|
/* parse a single key=value pair and translate it into the libxc
|
|
* used interface using 32-characters strings for each register.
|
|
* Will overwrite earlier entries and thus can be called multiple
|
|
* times.
|
|
*/
|
|
int libxl_cpuid_parse_config(libxl_cpuid_policy_list *policy, const char* str)
|
|
{
|
|
#define NA XEN_CPUID_INPUT_UNUSED
|
|
static const struct cpuid_flags cpuid_flags[] = {
|
|
{"maxleaf", 0x00000000, NA, CPUID_REG_EAX, 0, 32},
|
|
/* the following two entries are subject to tweaking later in the code */
|
|
{"stepping", 0x00000001, NA, CPUID_REG_EAX, 0, 4},
|
|
{"model", 0x00000001, NA, CPUID_REG_EAX, 4, 8},
|
|
{"family", 0x00000001, NA, CPUID_REG_EAX, 8, 8},
|
|
|
|
{"brandid", 0x00000001, NA, CPUID_REG_EBX, 0, 8},
|
|
{"clflush", 0x00000001, NA, CPUID_REG_EBX, 8, 8},
|
|
{"proccount", 0x00000001, NA, CPUID_REG_EBX, 16, 8},
|
|
{"localapicid", 0x00000001, NA, CPUID_REG_EBX, 24, 8},
|
|
|
|
{"est", 0x00000001, NA, CPUID_REG_ECX, 7, 1},
|
|
{"cntxid", 0x00000001, NA, CPUID_REG_ECX, 10, 1},
|
|
{"cmpxchg16", 0x00000001, NA, CPUID_REG_ECX, 13, 1},
|
|
/* Linux uses sse4_{1,2}. Keep sse4.{1,2} for compatibility */
|
|
{"sse4_1", 0x00000001, NA, CPUID_REG_ECX, 19, 1},
|
|
{"sse4.1", 0x00000001, NA, CPUID_REG_ECX, 19, 1},
|
|
{"sse4_2", 0x00000001, NA, CPUID_REG_ECX, 20, 1},
|
|
{"sse4.2", 0x00000001, NA, CPUID_REG_ECX, 20, 1},
|
|
{"aes", 0x00000001, NA, CPUID_REG_ECX, 25, 1},
|
|
|
|
{"cmpxchg8", 0x00000001, NA, CPUID_REG_EDX, 8, 1},
|
|
{"sysenter", 0x00000001, NA, CPUID_REG_EDX, 11, 1},
|
|
{"psn", 0x00000001, NA, CPUID_REG_EDX, 18, 1},
|
|
{"clfsh", 0x00000001, NA, CPUID_REG_EDX, 19, 1},
|
|
{"tm", 0x00000001, NA, CPUID_REG_EDX, 29, 1},
|
|
{"ia64", 0x00000001, NA, CPUID_REG_EDX, 30, 1},
|
|
|
|
{"arat", 0x00000006, NA, CPUID_REG_EAX, 2, 1},
|
|
|
|
{"tsc_adjust", 0x00000007, 0, CPUID_REG_EBX, 1, 1},
|
|
{"cmt", 0x00000007, 0, CPUID_REG_EBX, 12, 1},
|
|
|
|
{"lahfsahf", 0x80000001, NA, CPUID_REG_ECX, 0, 1},
|
|
{"cmplegacy", 0x80000001, NA, CPUID_REG_ECX, 1, 1},
|
|
{"altmovcr8", 0x80000001, NA, CPUID_REG_ECX, 4, 1},
|
|
{"nodeid", 0x80000001, NA, CPUID_REG_ECX, 19, 1},
|
|
{"perfctr_core", 0x80000001, NA, CPUID_REG_ECX, 23, 1},
|
|
{"perfctr_nb", 0x80000001, NA, CPUID_REG_ECX, 24, 1},
|
|
|
|
{"procpkg", 0x00000004, 0, CPUID_REG_EAX, 26, 6},
|
|
|
|
{"invtsc", 0x80000007, NA, CPUID_REG_EDX, 8, 1},
|
|
|
|
{"ppin", 0x80000008, NA, CPUID_REG_EBX, 23, 1},
|
|
|
|
{"nc", 0x80000008, NA, CPUID_REG_ECX, 0, 8},
|
|
{"apicidsize", 0x80000008, NA, CPUID_REG_ECX, 12, 4},
|
|
|
|
{"svm_npt", 0x8000000a, NA, CPUID_REG_EDX, 0, 1},
|
|
{"svm_lbrv", 0x8000000a, NA, CPUID_REG_EDX, 1, 1},
|
|
{"svm_nrips", 0x8000000a, NA, CPUID_REG_EDX, 3, 1},
|
|
{"svm_tscrate", 0x8000000a, NA, CPUID_REG_EDX, 4, 1},
|
|
{"svm_vmcbclean",0x8000000a, NA, CPUID_REG_EDX, 5, 1},
|
|
{"svm_decode", 0x8000000a, NA, CPUID_REG_EDX, 7, 1},
|
|
{"svm_pausefilt",0x8000000a, NA, CPUID_REG_EDX, 10, 1},
|
|
|
|
{"lfence+", 0x80000021, NA, CPUID_REG_EAX, 2, 1},
|
|
|
|
{"maxhvleaf", 0x40000000, NA, CPUID_REG_EAX, 0, 8},
|
|
|
|
{NULL, 0, NA, CPUID_REG_INV, 0, 0}
|
|
};
|
|
static const struct feature_name features[] = INIT_FEATURE_NAME_TO_VAL;
|
|
/*
|
|
* NB: if we switch to using a cpu_policy derived object instead of a
|
|
* libxl_cpuid_policy_list we could get rid of the featureset -> cpuid leaf
|
|
* conversion table and use a featureset directly as we have conversions
|
|
* to/from featureset and cpu_policy.
|
|
*/
|
|
static const struct {
|
|
enum { FEAT_CPUID, FEAT_MSR } type;
|
|
union {
|
|
struct {
|
|
uint32_t leaf, subleaf;
|
|
unsigned int reg;
|
|
} cpuid;
|
|
struct {
|
|
uint32_t index;
|
|
unsigned int reg;
|
|
} msr;
|
|
} u;
|
|
} feature_to_policy[] = {
|
|
#define CPUID_ENTRY(l, s, r) \
|
|
{ .type = FEAT_CPUID, \
|
|
.u = { .cpuid.leaf = l, .cpuid.subleaf = s, .cpuid.reg = r } \
|
|
}
|
|
#define MSR_ENTRY(i, r) \
|
|
{ .type = FEAT_MSR, \
|
|
.u = { .msr.index = i, .msr.reg = r } \
|
|
}
|
|
CPUID_ENTRY(0x00000001, NA, CPUID_REG_EDX),
|
|
CPUID_ENTRY(0x00000001, NA, CPUID_REG_ECX),
|
|
CPUID_ENTRY(0x80000001, NA, CPUID_REG_EDX),
|
|
CPUID_ENTRY(0x80000001, NA, CPUID_REG_ECX),
|
|
CPUID_ENTRY(0x0000000D, 1, CPUID_REG_EAX),
|
|
CPUID_ENTRY(0x00000007, 0, CPUID_REG_EBX),
|
|
CPUID_ENTRY(0x00000007, 0, CPUID_REG_ECX),
|
|
CPUID_ENTRY(0x80000007, NA, CPUID_REG_EDX),
|
|
CPUID_ENTRY(0x80000008, NA, CPUID_REG_EBX),
|
|
CPUID_ENTRY(0x00000007, 0, CPUID_REG_EDX),
|
|
CPUID_ENTRY(0x00000007, 1, CPUID_REG_EAX),
|
|
CPUID_ENTRY(0x80000021, NA, CPUID_REG_EAX),
|
|
CPUID_ENTRY(0x00000007, 1, CPUID_REG_EBX),
|
|
CPUID_ENTRY(0x00000007, 2, CPUID_REG_EDX),
|
|
CPUID_ENTRY(0x00000007, 1, CPUID_REG_ECX),
|
|
CPUID_ENTRY(0x00000007, 1, CPUID_REG_EDX),
|
|
MSR_ENTRY(0x10a, CPUID_REG_EAX),
|
|
MSR_ENTRY(0x10a, CPUID_REG_EDX),
|
|
CPUID_ENTRY(0x80000021, NA, CPUID_REG_ECX),
|
|
#undef MSR_ENTRY
|
|
#undef CPUID_ENTRY
|
|
};
|
|
#undef NA
|
|
const char *sep, *val;
|
|
char *name;
|
|
const struct cpuid_flags *flag;
|
|
const struct feature_name *feat;
|
|
|
|
BUILD_BUG_ON(ARRAY_SIZE(feature_to_policy) != FEATURESET_NR_ENTRIES);
|
|
|
|
sep = strchr(str, '=');
|
|
if (sep == NULL) {
|
|
return 1;
|
|
} else {
|
|
val = sep + 1;
|
|
}
|
|
for (flag = cpuid_flags; flag->name != NULL; flag++) {
|
|
if(!strncmp(str, flag->name, sep - str) && flag->name[sep - str] == 0)
|
|
return cpuid_add(policy, flag, val);
|
|
}
|
|
|
|
/* Provide a NUL terminated feature name to the search helper. */
|
|
name = strndup(str, sep - str);
|
|
if (name == NULL)
|
|
return ERROR_NOMEM;
|
|
|
|
feat = bsearch(name, features, ARRAY_SIZE(features), sizeof(features[0]),
|
|
search_feature);
|
|
free(name);
|
|
|
|
if (feat == NULL)
|
|
return 2;
|
|
|
|
switch (feature_to_policy[feat->bit / 32].type) {
|
|
case FEAT_CPUID:
|
|
{
|
|
struct cpuid_flags f;
|
|
|
|
f.name = feat->name;
|
|
f.leaf = feature_to_policy[feat->bit / 32].u.cpuid.leaf;
|
|
f.subleaf = feature_to_policy[feat->bit / 32].u.cpuid.subleaf;
|
|
f.reg = feature_to_policy[feat->bit / 32].u.cpuid.reg;
|
|
f.bit = feat->bit % 32;
|
|
f.length = 1;
|
|
|
|
return cpuid_add(policy, &f, val);
|
|
}
|
|
|
|
case FEAT_MSR:
|
|
{
|
|
unsigned int bit = feat->bit % 32;
|
|
|
|
if (feature_to_policy[feat->bit / 32].u.msr.reg == CPUID_REG_EDX)
|
|
bit += 32;
|
|
|
|
return msr_add(policy, feature_to_policy[feat->bit / 32].u.msr.index,
|
|
bit, val);
|
|
}
|
|
}
|
|
|
|
return 2;
|
|
}
|
|
|
|
/* parse a single list item from the legacy Python xend syntax, where
|
|
* the strings for each register were directly exposed to the user.
|
|
* Used for maintaining compatibility with older config files
|
|
*/
|
|
int libxl_cpuid_parse_config_xend(libxl_cpuid_policy_list *policy,
|
|
const char* str)
|
|
{
|
|
char *endptr;
|
|
unsigned long value;
|
|
uint32_t leaf, subleaf = XEN_CPUID_INPUT_UNUSED;
|
|
struct xc_xend_cpuid *entry;
|
|
|
|
/* parse the leaf number */
|
|
value = strtoul(str, &endptr, 0);
|
|
if (str == endptr) {
|
|
return 1;
|
|
}
|
|
leaf = value;
|
|
/* check for an optional subleaf number */
|
|
if (*endptr == ',') {
|
|
str = endptr + 1;
|
|
value = strtoul(str, &endptr, 0);
|
|
if (str == endptr) {
|
|
return 2;
|
|
}
|
|
subleaf = value;
|
|
}
|
|
if (*endptr != ':') {
|
|
return 3;
|
|
}
|
|
str = endptr + 1;
|
|
entry = cpuid_find_match(policy, leaf, subleaf);
|
|
for (str = endptr + 1; *str != 0;) {
|
|
if (str[0] != 'e' || str[2] != 'x') {
|
|
return 4;
|
|
}
|
|
value = str[1] - 'a';
|
|
endptr = strchr(str, '=');
|
|
if (value > 3 || endptr == NULL) {
|
|
return 4;
|
|
}
|
|
str = endptr + 1;
|
|
endptr = strchr(str, ',');
|
|
if (endptr == NULL) {
|
|
endptr = strchr(str, 0);
|
|
}
|
|
if (endptr - str != 32) {
|
|
return 5;
|
|
}
|
|
entry->policy[value] = calloc(32 + 1, 1);
|
|
strncpy(entry->policy[value], str, 32);
|
|
entry->policy[value][32] = 0;
|
|
if (*endptr == 0) {
|
|
break;
|
|
}
|
|
for (str = endptr + 1; *str == ' ' || *str == '\n'; str++);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int libxl__cpuid_legacy(libxl_ctx *ctx, uint32_t domid, bool restore,
|
|
libxl_domain_build_info *info)
|
|
{
|
|
GC_INIT(ctx);
|
|
bool pae = true;
|
|
bool itsc;
|
|
int r;
|
|
|
|
/*
|
|
* Gross hack. Using libxl_defbool_val() here causes libvirt to crash in
|
|
* __vfscanf_internal(), which is probably collateral damage from a side
|
|
* effect of the assert().
|
|
*
|
|
* Unblock things for now by opencoding without the assert.
|
|
*/
|
|
bool nested_virt = info->nested_hvm.val > 0;
|
|
|
|
/*
|
|
* For PV guests, PAE is Xen-controlled (it is the 'p' that differentiates
|
|
* the xen-3.0-x86_32 and xen-3.0-x86_32p ABIs). It is mandatory as Xen
|
|
* is 64bit only these days.
|
|
*
|
|
* For PVH guests, there is no top-level PAE control in the domain config,
|
|
* so is treated as always available.
|
|
*
|
|
* HVM guests get a top-level choice of whether PAE is available.
|
|
*/
|
|
if (info->type == LIBXL_DOMAIN_TYPE_HVM)
|
|
pae = libxl_defbool_val(info->u.hvm.pae);
|
|
|
|
/*
|
|
* Advertising Invariant TSC to a guest means that the TSC frequency won't
|
|
* change at any point in the future.
|
|
*
|
|
* We do not have enough information about potential migration
|
|
* destinations to know whether advertising ITSC is safe, but if the guest
|
|
* isn't going to migrate, then the current hardware is all that matters.
|
|
*
|
|
* Alternatively, an internal property of vTSC is that the values read are
|
|
* invariant. Advertise ITSC when we know the domain will have emulated
|
|
* TSC everywhere it goes.
|
|
*/
|
|
itsc = (libxl_defbool_val(info->disable_migrate) ||
|
|
info->tsc_mode == LIBXL_TSC_MODE_ALWAYS_EMULATE);
|
|
|
|
r = xc_cpuid_apply_policy(ctx->xch, domid, restore, NULL, 0,
|
|
pae, itsc, nested_virt,
|
|
info->cpuid ? info->cpuid->cpuid : NULL,
|
|
info->cpuid ? info->cpuid->msr : NULL);
|
|
if (r)
|
|
LOGEVD(ERROR, -r, domid, "Failed to apply CPUID policy");
|
|
|
|
GC_FREE;
|
|
return r ? ERROR_FAIL : 0;
|
|
}
|
|
|
|
static const char *input_names[2] = { "leaf", "subleaf" };
|
|
static const char *policy_names[4] = { "eax", "ebx", "ecx", "edx" };
|
|
/*
|
|
* Aiming for:
|
|
* { '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,
|
|
libxl_cpuid_policy_list *pl)
|
|
{
|
|
libxl_cpuid_policy_list policy = *pl;
|
|
struct xc_xend_cpuid *cpuid;
|
|
const struct xc_msr *msr;
|
|
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 (policy == NULL || policy->cpuid == NULL) goto empty;
|
|
cpuid = policy->cpuid;
|
|
|
|
for (i = 0; cpuid[i].input[0] != XEN_CPUID_INPUT_UNUSED; i++) {
|
|
s = yajl_gen_map_open(hand);
|
|
if (s != yajl_gen_status_ok) goto out;
|
|
|
|
for (j = 0; j < 2; j++) {
|
|
if (cpuid[i].input[j] != XEN_CPUID_INPUT_UNUSED) {
|
|
s = libxl__yajl_gen_asciiz(hand, input_names[j]);
|
|
if (s != yajl_gen_status_ok) goto out;
|
|
s = yajl_gen_integer(hand, cpuid[i].input[j]);
|
|
if (s != yajl_gen_status_ok) goto out;
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < 4; j++) {
|
|
if (cpuid[i].policy[j] != NULL) {
|
|
s = libxl__yajl_gen_asciiz(hand, policy_names[j]);
|
|
if (s != yajl_gen_status_ok) goto out;
|
|
s = yajl_gen_string(hand,
|
|
(const unsigned char *)cpuid[i].policy[j], 32);
|
|
if (s != yajl_gen_status_ok) goto out;
|
|
}
|
|
}
|
|
s = yajl_gen_map_close(hand);
|
|
if (s != yajl_gen_status_ok) goto out;
|
|
}
|
|
|
|
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 (!policy || !policy->msr) goto done;
|
|
msr = policy->msr;
|
|
|
|
for (i = 0; msr[i].index != XC_MSR_INPUT_UNUSED; i++) {
|
|
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, msr[i].index);
|
|
if (s != yajl_gen_status_ok) goto out;
|
|
s = libxl__yajl_gen_asciiz(hand, "policy");
|
|
if (s != yajl_gen_status_ok) goto out;
|
|
s = yajl_gen_string(hand,
|
|
(const unsigned char *)msr[i].policy, 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;
|
|
}
|
|
|
|
int libxl__cpuid_policy_list_parse_json(libxl__gc *gc,
|
|
const libxl__json_object *o,
|
|
libxl_cpuid_policy_list *p)
|
|
{
|
|
int i, size;
|
|
struct xc_xend_cpuid *l;
|
|
struct xc_msr *msr;
|
|
const libxl__json_object *co;
|
|
flexarray_t *array;
|
|
bool cpuid_only = false;
|
|
|
|
/*
|
|
* 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;
|
|
cpuid_only = true;
|
|
goto parse_cpuid;
|
|
}
|
|
|
|
if (!libxl__json_object_is_map(o))
|
|
return ERROR_FAIL;
|
|
|
|
co = libxl__json_map_get("cpuid", o, JSON_ARRAY);
|
|
if (!libxl__json_object_is_array(co))
|
|
return ERROR_FAIL;
|
|
|
|
parse_cpuid:
|
|
*p = libxl__calloc(NOGC, 1, sizeof(**p));
|
|
|
|
array = libxl__json_object_get_array(co);
|
|
if (!array->count)
|
|
goto cpuid_empty;
|
|
|
|
size = array->count;
|
|
/* need one extra slot as sentinel */
|
|
l = (*p)->cpuid = libxl__calloc(NOGC, size + 1,
|
|
sizeof(struct xc_xend_cpuid));
|
|
|
|
l[size].input[0] = XEN_CPUID_INPUT_UNUSED;
|
|
l[size].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;
|
|
|
|
if (!libxl__json_object_is_map(t))
|
|
return ERROR_FAIL;
|
|
|
|
for (j = 0; j < ARRAY_SIZE(l[0].input); j++) {
|
|
const libxl__json_object *r;
|
|
|
|
r = libxl__json_map_get(input_names[j], t, JSON_INTEGER);
|
|
if (!r)
|
|
l[i].input[j] = XEN_CPUID_INPUT_UNUSED;
|
|
else
|
|
l[i].input[j] = libxl__json_object_get_integer(r);
|
|
}
|
|
|
|
for (j = 0; j < ARRAY_SIZE(l[0].policy); j++) {
|
|
const libxl__json_object *r;
|
|
|
|
r = libxl__json_map_get(policy_names[j], t, JSON_STRING);
|
|
if (!r)
|
|
l[i].policy[j] = NULL;
|
|
else
|
|
l[i].policy[j] =
|
|
libxl__strdup(NOGC, libxl__json_object_get_string(r));
|
|
}
|
|
}
|
|
|
|
cpuid_empty:
|
|
if (cpuid_only)
|
|
return 0;
|
|
|
|
co = libxl__json_map_get("msr", o, JSON_ARRAY);
|
|
if (!libxl__json_object_is_array(co))
|
|
return ERROR_FAIL;
|
|
|
|
array = libxl__json_object_get_array(co);
|
|
if (!array->count)
|
|
return 0;
|
|
size = array->count;
|
|
/* need one extra slot as sentinel */
|
|
msr = (*p)->msr = libxl__calloc(NOGC, size + 1, sizeof(struct xc_msr));
|
|
|
|
msr[size].index = XC_MSR_INPUT_UNUSED;
|
|
|
|
for (i = 0; i < size; i++) {
|
|
const libxl__json_object *t, *r;
|
|
|
|
if (flexarray_get(array, i, (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;
|
|
msr[i].index = 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[i].policy) - 1)
|
|
return ERROR_FAIL;
|
|
strcpy(msr[i].policy, libxl__json_object_get_string(r));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int libxl_cpuid_policy_list_length(const libxl_cpuid_policy_list *pl)
|
|
{
|
|
int i = 0;
|
|
const struct xc_xend_cpuid *l;
|
|
|
|
if (*pl == NULL)
|
|
return 0;
|
|
|
|
l = (*pl)->cpuid;
|
|
if (l) {
|
|
while (l[i].input[0] != XEN_CPUID_INPUT_UNUSED)
|
|
i++;
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
void libxl_cpuid_policy_list_copy(libxl_ctx *ctx,
|
|
libxl_cpuid_policy_list *pdst,
|
|
const libxl_cpuid_policy_list *psrc)
|
|
{
|
|
struct xc_xend_cpuid **dst;
|
|
struct xc_xend_cpuid *const *src;
|
|
GC_INIT(ctx);
|
|
int i, j, len;
|
|
|
|
if (*psrc == NULL) {
|
|
*pdst = NULL;
|
|
goto out;
|
|
}
|
|
|
|
*pdst = libxl__calloc(NOGC, 1, sizeof(**pdst));
|
|
|
|
if (!(*psrc)->cpuid)
|
|
goto copy_msr;
|
|
|
|
dst = &(*pdst)->cpuid;
|
|
src = &(*psrc)->cpuid;
|
|
len = libxl_cpuid_policy_list_length(psrc);
|
|
/* one extra slot for sentinel */
|
|
*dst = libxl__calloc(NOGC, len + 1, sizeof(struct xc_xend_cpuid));
|
|
(*dst)[len].input[0] = XEN_CPUID_INPUT_UNUSED;
|
|
(*dst)[len].input[1] = XEN_CPUID_INPUT_UNUSED;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
for (j = 0; j < 2; j++)
|
|
(*dst)[i].input[j] = (*src)[i].input[j];
|
|
for (j = 0; j < 4; j++)
|
|
if ((*src)[i].policy[j])
|
|
(*dst)[i].policy[j] =
|
|
libxl__strdup(NOGC, (*src)[i].policy[j]);
|
|
else
|
|
(*dst)[i].policy[j] = NULL;
|
|
}
|
|
|
|
copy_msr:
|
|
if ((*psrc)->msr) {
|
|
const struct xc_msr *msr = (*psrc)->msr;
|
|
|
|
for (i = 0; msr[i].index != XC_MSR_INPUT_UNUSED; i++)
|
|
;
|
|
len = i;
|
|
(*pdst)->msr = libxl__calloc(NOGC, len + 1, sizeof(struct xc_msr));
|
|
(*pdst)->msr[len].index = XC_MSR_INPUT_UNUSED;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
(*pdst)->msr[i].index = msr[i].index;
|
|
strcpy((*pdst)->msr[i].policy, msr[i].policy);
|
|
}
|
|
}
|
|
|
|
out:
|
|
GC_FREE;
|
|
}
|
|
|
|
/*
|
|
* Local variables:
|
|
* mode: C
|
|
* c-basic-offset: 4
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*/
|