/* * 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" void libxl_cpuid_dispose(libxl_cpuid_policy_list *p_cpuid_list) { int i, j; libxl_cpuid_policy_list cpuid_list = *p_cpuid_list; if (cpuid_list == NULL) return; 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]); } 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 { 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 libxl_cpuid_policy_list cpuid_find_match(libxl_cpuid_policy_list *list, uint32_t leaf, uint32_t subleaf) { int i = 0; 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; } /* 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 *cpuid, const char* str) { #define NA XEN_CPUID_INPUT_UNUSED 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 */ {"family", 0x00000001, NA, CPUID_REG_EAX, 8, 8}, {"model", 0x00000001, NA, CPUID_REG_EAX, 4, 8}, {"stepping", 0x00000001, NA, CPUID_REG_EAX, 0, 4}, {"localapicid", 0x00000001, NA, CPUID_REG_EBX, 24, 8}, {"proccount", 0x00000001, NA, CPUID_REG_EBX, 16, 8}, {"clflush", 0x00000001, NA, CPUID_REG_EBX, 8, 8}, {"brandid", 0x00000001, NA, CPUID_REG_EBX, 0, 8}, {"hypervisor", 0x00000001, NA, CPUID_REG_ECX, 31, 1}, {"f16c", 0x00000001, NA, CPUID_REG_ECX, 29, 1}, {"avx", 0x00000001, NA, CPUID_REG_ECX, 28, 1}, {"osxsave", 0x00000001, NA, CPUID_REG_ECX, 27, 1}, {"xsave", 0x00000001, NA, CPUID_REG_ECX, 26, 1}, {"aes", 0x00000001, NA, CPUID_REG_ECX, 25, 1}, {"popcnt", 0x00000001, NA, CPUID_REG_ECX, 23, 1}, {"movbe", 0x00000001, NA, CPUID_REG_ECX, 22, 1}, {"x2apic", 0x00000001, NA, CPUID_REG_ECX, 21, 1}, /* Linux uses sse4_{1,2}. Keep sse4.{1,2} for compatibility */ {"sse4.2", 0x00000001, NA, CPUID_REG_ECX, 20, 1}, {"sse4_2", 0x00000001, NA, CPUID_REG_ECX, 20, 1}, {"sse4.1", 0x00000001, NA, CPUID_REG_ECX, 19, 1}, {"sse4_1", 0x00000001, NA, CPUID_REG_ECX, 19, 1}, {"dca", 0x00000001, NA, CPUID_REG_ECX, 18, 1}, {"pdcm", 0x00000001, NA, CPUID_REG_ECX, 15, 1}, {"xtpr", 0x00000001, NA, CPUID_REG_ECX, 14, 1}, {"cmpxchg16", 0x00000001, NA, CPUID_REG_ECX, 13, 1}, {"cntxid", 0x00000001, NA, CPUID_REG_ECX, 10, 1}, {"ssse3", 0x00000001, NA, CPUID_REG_ECX, 9, 1}, {"tm2", 0x00000001, NA, CPUID_REG_ECX, 8, 1}, {"est", 0x00000001, NA, CPUID_REG_ECX, 7, 1}, {"smx", 0x00000001, NA, CPUID_REG_ECX, 6, 1}, {"vmx", 0x00000001, NA, CPUID_REG_ECX, 5, 1}, {"dscpl", 0x00000001, NA, CPUID_REG_ECX, 4, 1}, {"monitor", 0x00000001, NA, CPUID_REG_ECX, 3, 1}, {"dtes64", 0x00000001, NA, CPUID_REG_ECX, 2, 1}, {"pclmulqdq", 0x00000001, NA, CPUID_REG_ECX, 1, 1}, {"sse3", 0x00000001, NA, CPUID_REG_ECX, 0, 1}, {"pbe", 0x00000001, NA, CPUID_REG_EDX, 31, 1}, {"ia64", 0x00000001, NA, CPUID_REG_EDX, 30, 1}, {"tm", 0x00000001, NA, CPUID_REG_EDX, 29, 1}, {"htt", 0x00000001, NA, CPUID_REG_EDX, 28, 1}, {"ss", 0x00000001, NA, CPUID_REG_EDX, 27, 1}, {"sse2", 0x00000001, NA, CPUID_REG_EDX, 26, 1}, {"sse", 0x00000001, NA, CPUID_REG_EDX, 25, 1}, {"fxsr", 0x00000001, NA, CPUID_REG_EDX, 24, 1}, {"mmx", 0x00000001, NA, CPUID_REG_EDX, 23, 1}, {"acpi", 0x00000001, NA, CPUID_REG_EDX, 22, 1}, {"ds", 0x00000001, NA, CPUID_REG_EDX, 21, 1}, {"clfsh", 0x00000001, NA, CPUID_REG_EDX, 19, 1}, {"psn", 0x00000001, NA, CPUID_REG_EDX, 18, 1}, {"pse36", 0x00000001, NA, CPUID_REG_EDX, 17, 1}, {"pat", 0x00000001, NA, CPUID_REG_EDX, 16, 1}, {"cmov", 0x00000001, NA, CPUID_REG_EDX, 15, 1}, {"mca", 0x00000001, NA, CPUID_REG_EDX, 14, 1}, {"pge", 0x00000001, NA, CPUID_REG_EDX, 13, 1}, {"mtrr", 0x00000001, NA, CPUID_REG_EDX, 12, 1}, {"sysenter", 0x00000001, NA, CPUID_REG_EDX, 11, 1}, {"apic", 0x00000001, NA, CPUID_REG_EDX, 9, 1}, {"cmpxchg8", 0x00000001, NA, CPUID_REG_EDX, 8, 1}, {"mce", 0x00000001, NA, CPUID_REG_EDX, 7, 1}, {"pae", 0x00000001, NA, CPUID_REG_EDX, 6, 1}, {"msr", 0x00000001, NA, CPUID_REG_EDX, 5, 1}, {"tsc", 0x00000001, NA, CPUID_REG_EDX, 4, 1}, {"pse", 0x00000001, NA, CPUID_REG_EDX, 3, 1}, {"de", 0x00000001, NA, CPUID_REG_EDX, 2, 1}, {"vme", 0x00000001, NA, CPUID_REG_EDX, 1, 1}, {"fpu", 0x00000001, NA, CPUID_REG_EDX, 0, 1}, {"topoext", 0x80000001, NA, CPUID_REG_ECX, 22, 1}, {"tbm", 0x80000001, NA, CPUID_REG_ECX, 21, 1}, {"nodeid", 0x80000001, NA, CPUID_REG_ECX, 19, 1}, {"fma4", 0x80000001, NA, CPUID_REG_ECX, 16, 1}, {"lwp", 0x80000001, NA, CPUID_REG_ECX, 15, 1}, {"wdt", 0x80000001, NA, CPUID_REG_ECX, 13, 1}, {"skinit", 0x80000001, NA, CPUID_REG_ECX, 12, 1}, {"xop", 0x80000001, NA, CPUID_REG_ECX, 11, 1}, {"ibs", 0x80000001, NA, CPUID_REG_ECX, 10, 1}, {"osvw", 0x80000001, NA, CPUID_REG_ECX, 10, 1}, {"3dnowprefetch",0x80000001, NA, CPUID_REG_ECX, 8, 1}, {"misalignsse", 0x80000001, NA, CPUID_REG_ECX, 7, 1}, {"sse4a", 0x80000001, NA, CPUID_REG_ECX, 6, 1}, {"abm", 0x80000001, NA, CPUID_REG_ECX, 5, 1}, {"altmovcr8", 0x80000001, NA, CPUID_REG_ECX, 4, 1}, {"extapic", 0x80000001, NA, CPUID_REG_ECX, 3, 1}, {"svm", 0x80000001, NA, CPUID_REG_ECX, 2, 1}, {"cmplegacy", 0x80000001, NA, CPUID_REG_ECX, 1, 1}, {"lahfsahf", 0x80000001, NA, CPUID_REG_ECX, 0, 1}, {"3dnow", 0x80000001, NA, CPUID_REG_EDX, 31, 1}, {"3dnowext", 0x80000001, NA, CPUID_REG_EDX, 30, 1}, {"lm", 0x80000001, NA, CPUID_REG_EDX, 29, 1}, {"rdtscp", 0x80000001, NA, CPUID_REG_EDX, 27, 1}, {"page1gb", 0x80000001, NA, CPUID_REG_EDX, 26, 1}, {"ffxsr", 0x80000001, NA, CPUID_REG_EDX, 25, 1}, {"mmxext", 0x80000001, NA, CPUID_REG_EDX, 22, 1}, {"nx", 0x80000001, NA, CPUID_REG_EDX, 20, 1}, {"syscall", 0x80000001, NA, CPUID_REG_EDX, 11, 1}, {"procpkg", 0x00000004, 0, CPUID_REG_EAX, 26, 6}, {"apicidsize", 0x80000008, NA, CPUID_REG_ECX, 12, 4}, {"nc", 0x80000008, NA, CPUID_REG_ECX, 0, 8}, {"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}, {NULL, 0, NA, CPUID_REG_INV, 0, 0} }; #undef NA char *sep, *val, *endptr; int i; struct cpuid_flags *flag; struct libxl__cpuid_policy *entry; unsigned long num; char flags[33], *resstr; 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) break; } if (flag->name == NULL) { return 2; } entry = cpuid_find_match(cpuid, flag->leaf, flag->subleaf); resstr = entry->policy[flag->reg - 1]; if (resstr == NULL) { resstr = strdup("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); } num = strtoull(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 & (1 << i))]; } } else { switch(val[0]) { case 'x': case 'k': case 's': memset(flags, val[0], flag->length); break; default: return 3; } } /* the family and model entry is potentially split up across * two fields in Fn0000_0001_EAX, so handle them here separately. */ if (!strncmp(str, "family", sep - str)) { 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 (!strncmp(str, "model", sep - str)) { 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; } /* 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 *cpuid, const char* str) { char *endptr; unsigned long value; uint32_t leaf, subleaf = XEN_CPUID_INPUT_UNUSED; struct libxl__cpuid_policy *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(cpuid, 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 < 0 || 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; } void libxl_cpuid_apply_policy(libxl_ctx *ctx, uint32_t domid) { xc_cpuid_apply_policy(ctx->xch, domid); } void libxl_cpuid_set(libxl_ctx *ctx, uint32_t domid, libxl_cpuid_policy_list cpuid) { int i; char *cpuid_res[4]; for (i = 0; cpuid[i].input[0] != XEN_CPUID_INPUT_UNUSED; i++) xc_cpuid_set(ctx->xch, domid, cpuid[i].input, (const char**)(cpuid[i].policy), cpuid_res); } yajl_gen_status libxl_cpuid_policy_list_gen_json(yajl_gen hand, libxl_cpuid_policy_list *pcpuid) { libxl_cpuid_policy_list cpuid = *pcpuid; yajl_gen_status s; const char *input_names[2] = { "leaf", "subleaf" }; const char *policy_names[4] = { "eax", "ebx", "ecx", "edx" }; int i, j; /* * Aiming for: * [ * { 'leaf': 'val-eax', * 'subleaf': 'val-ecx', * 'eax': 'filter', * 'ebx': 'filter', * 'ecx': 'filter', * 'edx': 'filter' }, * { 'leaf': 'val-eax', ..., 'eax': 'filter', ... }, * ... etc ... * ] */ 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++) { 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); out: return s; } /* * Local variables: * mode: C * c-basic-offset: 4 * indent-tabs-mode: nil * End: */