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:
Keir Fraser committed 2008-04-25 13:44:45 +01:00
1 parent 9fb02748d0
commit 5f14a87ceb
17 files changed
+1001 -133

No files matched your search

+135
View File
@@ -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 }
};
+1
View File
@@ -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
+51
View File
@@ -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):
+33
View File
@@ -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):
+31 -2
View File
@@ -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):
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.