mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 18:21:39 +01:00
x86, hvm: Guest CPUID configuration.
CPUID's replies are now pre-calculated in libxc and given to the hypervisor via a new domctl. There are two parts to this feature: - We can overwrite cpuid's replies. - We can check that a vm is compatible with a host's processor. Signed-off-by: Jean Guyader <jean.guyader@eu.citrix.com> Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
This commit is contained in:
1 parent
9fb02748d0
commit
5f14a87ceb
17 files changed
+1001
-133
No files matched your search
@@ -611,6 +611,110 @@ static PyObject *pyxc_set_os_type(XcObject *self,
|
||||
}
|
||||
#endif /* __ia64__ */
|
||||
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
static void pyxc_dom_extract_cpuid(PyObject *config,
|
||||
char **regs)
|
||||
{
|
||||
const char *regs_extract[4] = { "eax", "ebx", "ecx", "edx" };
|
||||
PyObject *obj;
|
||||
int i;
|
||||
|
||||
memset(regs, 0, 4*sizeof(*regs));
|
||||
|
||||
if ( !PyDict_Check(config) )
|
||||
return;
|
||||
|
||||
for ( i = 0; i < 4; i++ )
|
||||
if ( (obj = PyDict_GetItemString(config, regs_extract[i])) != NULL )
|
||||
regs[i] = PyString_AS_STRING(obj);
|
||||
}
|
||||
|
||||
static PyObject *pyxc_create_cpuid_dict(char **regs)
|
||||
{
|
||||
const char *regs_extract[4] = { "eax", "ebx", "ecx", "edx" };
|
||||
PyObject *dict;
|
||||
int i;
|
||||
|
||||
dict = PyDict_New();
|
||||
for ( i = 0; i < 4; i++ )
|
||||
{
|
||||
if ( regs[i] == NULL )
|
||||
continue;
|
||||
PyDict_SetItemString(dict, regs_extract[i],
|
||||
PyString_FromString(regs[i]));
|
||||
free(regs[i]);
|
||||
regs[i] = NULL;
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
|
||||
static PyObject *pyxc_dom_check_cpuid(XcObject *self,
|
||||
PyObject *args)
|
||||
{
|
||||
PyObject *sub_input, *config;
|
||||
unsigned int input[2];
|
||||
char *regs[4], *regs_transform[4];
|
||||
|
||||
if ( !PyArg_ParseTuple(args, "iOO", &input[0], &sub_input, &config) )
|
||||
return NULL;
|
||||
|
||||
pyxc_dom_extract_cpuid(config, regs);
|
||||
|
||||
input[1] = XEN_CPUID_INPUT_UNUSED;
|
||||
if ( PyLong_Check(sub_input) )
|
||||
input[1] = PyLong_AsUnsignedLong(sub_input);
|
||||
|
||||
if ( xc_cpuid_check(self->xc_handle, input,
|
||||
(const char **)regs, regs_transform) )
|
||||
return pyxc_error_to_exception();
|
||||
|
||||
return pyxc_create_cpuid_dict(regs_transform);
|
||||
}
|
||||
|
||||
static PyObject *pyxc_dom_set_policy_cpuid(XcObject *self,
|
||||
PyObject *args)
|
||||
{
|
||||
domid_t domid;
|
||||
|
||||
if ( !PyArg_ParseTuple(args, "i", &domid) )
|
||||
return NULL;
|
||||
|
||||
if ( xc_cpuid_apply_policy(self->xc_handle, domid) )
|
||||
return pyxc_error_to_exception();
|
||||
|
||||
Py_INCREF(zero);
|
||||
return zero;
|
||||
}
|
||||
|
||||
|
||||
static PyObject *pyxc_dom_set_cpuid(XcObject *self,
|
||||
PyObject *args)
|
||||
{
|
||||
domid_t domid;
|
||||
PyObject *sub_input, *config;
|
||||
unsigned int input[2];
|
||||
char *regs[4], *regs_transform[4];
|
||||
|
||||
if ( !PyArg_ParseTuple(args, "iiOO", &domid,
|
||||
&input[0], &sub_input, &config) )
|
||||
return NULL;
|
||||
|
||||
pyxc_dom_extract_cpuid(config, regs);
|
||||
|
||||
input[1] = XEN_CPUID_INPUT_UNUSED;
|
||||
if ( PyLong_Check(sub_input) )
|
||||
input[1] = PyLong_AsUnsignedLong(sub_input);
|
||||
|
||||
if ( xc_cpuid_set(self->xc_handle, domid, input, (const char **)regs,
|
||||
regs_transform) )
|
||||
return pyxc_error_to_exception();
|
||||
|
||||
return pyxc_create_cpuid_dict(regs_transform);
|
||||
}
|
||||
|
||||
#endif /* __i386__ || __x86_64__ */
|
||||
|
||||
static PyObject *pyxc_hvm_build(XcObject *self,
|
||||
PyObject *args,
|
||||
PyObject *kwds)
|
||||
@@ -1635,6 +1739,37 @@ static PyMethodDef pyxc_methods[] = {
|
||||
" log [int]: Specifies the area's size.\n"
|
||||
"Returns: [int] 0 on success; -1 on error.\n" },
|
||||
#endif /* __powerpc */
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
{ "domain_check_cpuid",
|
||||
(PyCFunction)pyxc_dom_check_cpuid,
|
||||
METH_VARARGS, "\n"
|
||||
"Apply checks to host CPUID.\n"
|
||||
" input [long]: Input for cpuid instruction (eax)\n"
|
||||
" sub_input [long]: Second input (optional, may be None) for cpuid "
|
||||
" instruction (ecx)\n"
|
||||
" config [dict]: Dictionary of register\n"
|
||||
" config [dict]: Dictionary of register, use for checking\n\n"
|
||||
"Returns: [int] 0 on success; exception on error.\n" },
|
||||
|
||||
{ "domain_set_cpuid",
|
||||
(PyCFunction)pyxc_dom_set_cpuid,
|
||||
METH_VARARGS, "\n"
|
||||
"Set cpuid response for an input and a domain.\n"
|
||||
" dom [int]: Identifier of domain.\n"
|
||||
" input [long]: Input for cpuid instruction (eax)\n"
|
||||
" sub_input [long]: Second input (optional, may be None) for cpuid "
|
||||
" instruction (ecx)\n"
|
||||
" config [dict]: Dictionary of register\n\n"
|
||||
"Returns: [int] 0 on success; exception on error.\n" },
|
||||
|
||||
{ "domain_set_policy_cpuid",
|
||||
(PyCFunction)pyxc_dom_set_policy_cpuid,
|
||||
METH_VARARGS, "\n"
|
||||
"Set the default cpuid policy for a domain.\n"
|
||||
" dom [int]: Identifier of domain.\n\n"
|
||||
"Returns: [int] 0 on success; exception on error.\n" },
|
||||
#endif
|
||||
|
||||
{ NULL, NULL, 0, NULL }
|
||||
};
|
||||
|
||||
@@ -309,6 +309,7 @@ def restore(xd, fd, dominfo = None, paused = False, relocating = False):
|
||||
else:
|
||||
break
|
||||
os.close(qemu_fd)
|
||||
restore_image.setCpuid()
|
||||
|
||||
|
||||
os.read(fd, 1) # Wait for source to close connection
|
||||
|
||||
@@ -203,6 +203,8 @@ XENAPI_CFG_TYPES = {
|
||||
'target': int,
|
||||
'security_label': str,
|
||||
'pci': str,
|
||||
'cpuid' : dict,
|
||||
'cpuid_check' : dict,
|
||||
}
|
||||
|
||||
# List of legacy configuration keys that have no equivalent in the
|
||||
@@ -497,6 +499,32 @@ class XendConfig(dict):
|
||||
if 'handle' in dominfo:
|
||||
self['uuid'] = uuid.toString(dominfo['handle'])
|
||||
|
||||
def parse_cpuid(self, cfg, field):
|
||||
def int2bin(n, count=32):
|
||||
return "".join([str((n >> y) & 1) for y in range(count-1, -1, -1)])
|
||||
|
||||
for input, regs in cfg[field].iteritems():
|
||||
if not regs is dict:
|
||||
cfg[field][input] = dict(regs)
|
||||
|
||||
cpuid = {}
|
||||
for input in cfg[field]:
|
||||
inputs = input.split(',')
|
||||
if inputs[0][0:2] == '0x':
|
||||
inputs[0] = str(int(inputs[0], 16))
|
||||
if len(inputs) == 2:
|
||||
if inputs[1][0:2] == '0x':
|
||||
inputs[1] = str(int(inputs[1], 16))
|
||||
new_input = ','.join(inputs)
|
||||
cpuid[new_input] = {} # new input
|
||||
for reg in cfg[field][input]:
|
||||
val = cfg[field][input][reg]
|
||||
if val[0:2] == '0x':
|
||||
cpuid[new_input][reg] = int2bin(int(val, 16))
|
||||
else:
|
||||
cpuid[new_input][reg] = val
|
||||
cfg[field] = cpuid
|
||||
|
||||
def _parse_sxp(self, sxp_cfg):
|
||||
""" Populate this XendConfig using the parsed SXP.
|
||||
|
||||
@@ -653,6 +681,12 @@ class XendConfig(dict):
|
||||
except ValueError, e:
|
||||
raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
|
||||
|
||||
# Parse cpuid
|
||||
if 'cpuid' in cfg:
|
||||
self.parse_cpuid(cfg, 'cpuid')
|
||||
if 'cpuid_check' in cfg:
|
||||
self.parse_cpuid(cfg, 'cpuid_check')
|
||||
|
||||
import xen.util.xsm.xsm as security
|
||||
if security.on():
|
||||
from xen.util.acmpolicy import ACM_LABEL_UNLABELED
|
||||
@@ -901,6 +935,16 @@ class XendConfig(dict):
|
||||
int(self['vcpus_params'].get('weight', 256))
|
||||
self['vcpus_params']['cap'] = int(self['vcpus_params'].get('cap', 0))
|
||||
|
||||
def cpuid_to_sxp(self, sxpr, field):
|
||||
regs_list = []
|
||||
for input, regs in self[field].iteritems():
|
||||
reg_list = []
|
||||
for reg, val in regs.iteritems():
|
||||
reg_list.append([reg, val])
|
||||
regs_list.append([input, reg_list])
|
||||
sxpr.append([field, regs_list])
|
||||
|
||||
|
||||
def to_sxp(self, domain = None, ignore_devices = False, ignore = [],
|
||||
legacy_only = True):
|
||||
""" Get SXP representation of this config object.
|
||||
@@ -1012,6 +1056,13 @@ class XendConfig(dict):
|
||||
txn.abort()
|
||||
raise
|
||||
|
||||
if 'cpuid' in self:
|
||||
self.cpuid_to_sxp(sxpr, 'cpuid')
|
||||
if 'cpuid_check' in self:
|
||||
self.cpuid_to_sxp(sxpr, 'cpuid_check')
|
||||
|
||||
log.debug(sxpr)
|
||||
|
||||
return sxpr
|
||||
|
||||
def _blkdev_name_to_number(self, dev):
|
||||
|
||||
@@ -551,6 +551,38 @@ class HVMImageHandler(ImageHandler):
|
||||
self.acpi = int(vmConfig['platform'].get('acpi', 0))
|
||||
self.guest_os_type = vmConfig['platform'].get('guest_os_type')
|
||||
|
||||
self.vmConfig = vmConfig
|
||||
|
||||
def setCpuid(self):
|
||||
xc.domain_set_policy_cpuid(self.vm.getDomid())
|
||||
|
||||
if 'cpuid' in self.vmConfig:
|
||||
cpuid = self.vmConfig['cpuid']
|
||||
transformed = {}
|
||||
for sinput, regs in cpuid.iteritems():
|
||||
inputs = sinput.split(',')
|
||||
input = long(inputs[0])
|
||||
sub_input = None
|
||||
if len(inputs) == 2:
|
||||
sub_input = long(inputs[1])
|
||||
t = xc.domain_set_cpuid(self.vm.getDomid(),
|
||||
input, sub_input, regs)
|
||||
transformed[sinput] = t
|
||||
self.vmConfig['cpuid'] = transformed
|
||||
|
||||
if 'cpuid_check' in self.vmConfig:
|
||||
cpuid_check = self.vmConfig['cpuid_check']
|
||||
transformed = {}
|
||||
for sinput, regs_check in cpuid_check.iteritems():
|
||||
inputs = sinput.split(',')
|
||||
input = long(inputs[0])
|
||||
sub_input = None
|
||||
if len(inputs) == 2:
|
||||
sub_input = long(inputs[1])
|
||||
t = xc.domain_check_cpuid(input, sub_input, regs_check)
|
||||
transformed[sinput] = t
|
||||
self.vmConfig['cpuid_check'] = transformed
|
||||
|
||||
# Return a list of cmd line args to the device models based on the
|
||||
# xm config file
|
||||
def parseDeviceModelArgs(self, vmConfig):
|
||||
@@ -718,6 +750,7 @@ class X86_HVM_ImageHandler(HVMImageHandler):
|
||||
|
||||
def buildDomain(self):
|
||||
xc.hvm_set_param(self.vm.getDomid(), HVM_PARAM_PAE_ENABLED, self.pae)
|
||||
self.setCpuid()
|
||||
return HVMImageHandler.buildDomain(self)
|
||||
|
||||
def getRequiredAvailableMemory(self, mem_kb):
|
||||
|
||||
@@ -549,6 +549,14 @@ gopts.var('hap', val='HAP',
|
||||
use="""Hap status (0=hap is disabled;
|
||||
1=hap is enabled.""")
|
||||
|
||||
gopts.var('cpuid', val="IN[,SIN]:eax=EAX,ebx=EBX,exc=ECX,edx=EDX",
|
||||
fn=append_value, default=[],
|
||||
use="""Cpuid description.""")
|
||||
|
||||
gopts.var('cpuid_check', val="IN[,SIN]:eax=EAX,ebx=EBX,exc=ECX,edx=EDX",
|
||||
fn=append_value, default=[],
|
||||
use="""Cpuid check description.""")
|
||||
|
||||
def err(msg):
|
||||
"""Print an error to stderr and exit.
|
||||
"""
|
||||
@@ -755,7 +763,7 @@ def configure_hvm(config_image, vals):
|
||||
'vnc', 'vncdisplay', 'vncunused', 'vncconsole', 'vnclisten',
|
||||
'sdl', 'display', 'xauthority', 'rtc_timeoffset', 'monitor',
|
||||
'acpi', 'apic', 'usb', 'usbdevice', 'keymap', 'pci', 'hpet',
|
||||
'guest_os_type', 'hap', 'opengl']
|
||||
'guest_os_type', 'hap', 'opengl', 'cpuid', 'cpuid_check']
|
||||
|
||||
for a in args:
|
||||
if a in vals.__dict__ and vals.__dict__[a] is not None:
|
||||
@@ -779,7 +787,8 @@ def make_config(vals):
|
||||
map(add_conf, ['name', 'memory', 'maxmem', 'shadow_memory',
|
||||
'restart', 'on_poweroff',
|
||||
'on_reboot', 'on_crash', 'vcpus', 'vcpu_avail', 'features',
|
||||
'on_xend_start', 'on_xend_stop', 'target'])
|
||||
'on_xend_start', 'on_xend_stop', 'target', 'cpuid',
|
||||
'cpuid_check'])
|
||||
|
||||
if vals.uuid is not None:
|
||||
config.append(['uuid', vals.uuid])
|
||||
@@ -843,6 +852,24 @@ def preprocess_disk(vals):
|
||||
disk.append(d)
|
||||
vals.disk = disk
|
||||
|
||||
def preprocess_cpuid(vals, attr_name):
|
||||
if not vals.cpuid: return
|
||||
cpuid = {}
|
||||
for cpuid_input in getattr(vals, attr_name):
|
||||
cpuid_match = re.match(r"(?P<input>(0x)?[0-9A-Fa-f]+):(?P<regs>.*)", cpuid_input)
|
||||
if cpuid_match != None:
|
||||
res_cpuid = cpuid_match.groupdict()
|
||||
input = res_cpuid['input']
|
||||
regs = res_cpuid['regs'].split(',')
|
||||
cpuid[input]= {} # New input
|
||||
for reg in regs:
|
||||
reg_match = re.match(r"(?P<reg>eax|ebx|ecx|edx)=(?P<val>.*)", reg)
|
||||
if reg_match == None:
|
||||
err("cpuid's syntax is (eax|ebx|ecx|edx)=value")
|
||||
res = reg_match.groupdict()
|
||||
cpuid[input][res['reg']] = res['val'] # new register
|
||||
setattr(vals, attr_name, cpuid)
|
||||
|
||||
def preprocess_pci(vals):
|
||||
if not vals.pci: return
|
||||
pci = []
|
||||
@@ -1047,6 +1074,8 @@ def preprocess(vals):
|
||||
preprocess_vnc(vals)
|
||||
preprocess_vtpm(vals)
|
||||
preprocess_access_control(vals)
|
||||
preprocess_cpuid(vals, 'cpuid')
|
||||
preprocess_cpuid(vals, 'cpuid_check')
|
||||
|
||||
|
||||
def comma_sep_kv_to_dict(c):
|
||||
|
||||