b5ec5ce0e3
This is a reimplementation of prior versions which adds the ability to define cpu models for contemporary processors. The added models are likewise selected via -cpu <name>, and are intended to displace the existing convention of "-cpu qemu64" augmented with a series of feature flags. A primary motivation was determination of a least common denominator within a given processor class to simplify guest migration. It is still possible to modify an arbitrary model via additional feature flags however the goal here was to make doing so unnecessary in typical usage. The other consideration was providing models names reflective of current processors. Both AMD and Intel have reviewed the models in terms of balancing generality of migration vs. excessive feature downgrade relative to released silicon. This version of the patch replaces the prior hard wired definitions with a configuration file approach for new models. Existing models are thus far left as-is but may easily be transitioned to (or may be overridden by) the configuration file representation. Proposed new model definitions are provided here for current AMD and Intel processors. Each model consists of a name used to select it on the command line (-cpu <name>), and a model_id which corresponds to a least common denominator commercial instance of the processor class. A table of names/model_ids may be queried via "-cpu ?model": : x86 Opteron_G3 AMD Opteron 23xx (Gen 3 Class Opteron) x86 Opteron_G2 AMD Opteron 22xx (Gen 2 Class Opteron) x86 Opteron_G1 AMD Opteron 240 (Gen 1 Class Opteron) x86 Nehalem Intel Core i7 9xx (Nehalem Class Core i7) x86 Penryn Intel Core 2 Duo P9xxx (Penryn Class Core 2) x86 Conroe Intel Celeron_4x0 (Conroe/Merom Class Core 2) : Also added is "-cpu ?dump" which exhaustively outputs all config data for all defined models, and "-cpu ?cpuid" which enumerates all qemu recognized CPUID feature flags. The pseudo cpuid flag 'check' when added to the feature flag list will warn when feature flags (either implicit in a cpu model or explicit on the command line) would have otherwise been quietly unavailable to a guest: # qemu-system-x86_64 ... -cpu Nehalem,check warning: host cpuid 0000_0001 lacks requested flag 'sse4.2|sse4_2' [0x00100000] warning: host cpuid 0000_0001 lacks requested flag 'popcnt' [0x00800000] A similar 'enforce' pseudo flag exists which in addition to the above causes qemu to error exit if requested flags are unavailable. Configuration data for a cpu model resides in the target config file which by default will be installed as: /usr/local/etc/qemu/target-<arch>.conf The format of this file should be self explanatory given the definitions for the above six models and essentially mimics the structure of the static x86_def_t x86_defs. Encoding of cpuid flags names now allows aliases for both the configuration file and the command line which reconciles some Intel/AMD/Linux/Qemu naming differences. This patch was tested relative to qemu.git. Signed-off-by: john cooper <john.cooper@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
476 lines
12 KiB
C
476 lines
12 KiB
C
#include "qemu-common.h"
|
|
#include "qemu-option.h"
|
|
#include "qemu-config.h"
|
|
#include "sysemu.h"
|
|
#include "hw/qdev.h"
|
|
|
|
QemuOptsList qemu_drive_opts = {
|
|
.name = "drive",
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_drive_opts.head),
|
|
.desc = {
|
|
{
|
|
.name = "bus",
|
|
.type = QEMU_OPT_NUMBER,
|
|
.help = "bus number",
|
|
},{
|
|
.name = "unit",
|
|
.type = QEMU_OPT_NUMBER,
|
|
.help = "unit number (i.e. lun for scsi)",
|
|
},{
|
|
.name = "if",
|
|
.type = QEMU_OPT_STRING,
|
|
.help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)",
|
|
},{
|
|
.name = "index",
|
|
.type = QEMU_OPT_NUMBER,
|
|
},{
|
|
.name = "cyls",
|
|
.type = QEMU_OPT_NUMBER,
|
|
.help = "number of cylinders (ide disk geometry)",
|
|
},{
|
|
.name = "heads",
|
|
.type = QEMU_OPT_NUMBER,
|
|
.help = "number of heads (ide disk geometry)",
|
|
},{
|
|
.name = "secs",
|
|
.type = QEMU_OPT_NUMBER,
|
|
.help = "number of sectors (ide disk geometry)",
|
|
},{
|
|
.name = "trans",
|
|
.type = QEMU_OPT_STRING,
|
|
.help = "chs translation (auto, lba. none)",
|
|
},{
|
|
.name = "media",
|
|
.type = QEMU_OPT_STRING,
|
|
.help = "media type (disk, cdrom)",
|
|
},{
|
|
.name = "snapshot",
|
|
.type = QEMU_OPT_BOOL,
|
|
},{
|
|
.name = "file",
|
|
.type = QEMU_OPT_STRING,
|
|
.help = "disk image",
|
|
},{
|
|
.name = "cache",
|
|
.type = QEMU_OPT_STRING,
|
|
.help = "host cache usage (none, writeback, writethrough)",
|
|
},{
|
|
.name = "aio",
|
|
.type = QEMU_OPT_STRING,
|
|
.help = "host AIO implementation (threads, native)",
|
|
},{
|
|
.name = "format",
|
|
.type = QEMU_OPT_STRING,
|
|
.help = "disk format (raw, qcow2, ...)",
|
|
},{
|
|
.name = "serial",
|
|
.type = QEMU_OPT_STRING,
|
|
},{
|
|
.name = "rerror",
|
|
.type = QEMU_OPT_STRING,
|
|
},{
|
|
.name = "werror",
|
|
.type = QEMU_OPT_STRING,
|
|
},{
|
|
.name = "addr",
|
|
.type = QEMU_OPT_STRING,
|
|
.help = "pci address (virtio only)",
|
|
},{
|
|
.name = "readonly",
|
|
.type = QEMU_OPT_BOOL,
|
|
},
|
|
{ /* end if list */ }
|
|
},
|
|
};
|
|
|
|
QemuOptsList qemu_chardev_opts = {
|
|
.name = "chardev",
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_chardev_opts.head),
|
|
.desc = {
|
|
{
|
|
.name = "backend",
|
|
.type = QEMU_OPT_STRING,
|
|
},{
|
|
.name = "path",
|
|
.type = QEMU_OPT_STRING,
|
|
},{
|
|
.name = "host",
|
|
.type = QEMU_OPT_STRING,
|
|
},{
|
|
.name = "port",
|
|
.type = QEMU_OPT_STRING,
|
|
},{
|
|
.name = "localaddr",
|
|
.type = QEMU_OPT_STRING,
|
|
},{
|
|
.name = "localport",
|
|
.type = QEMU_OPT_STRING,
|
|
},{
|
|
.name = "to",
|
|
.type = QEMU_OPT_NUMBER,
|
|
},{
|
|
.name = "ipv4",
|
|
.type = QEMU_OPT_BOOL,
|
|
},{
|
|
.name = "ipv6",
|
|
.type = QEMU_OPT_BOOL,
|
|
},{
|
|
.name = "wait",
|
|
.type = QEMU_OPT_BOOL,
|
|
},{
|
|
.name = "server",
|
|
.type = QEMU_OPT_BOOL,
|
|
},{
|
|
.name = "delay",
|
|
.type = QEMU_OPT_BOOL,
|
|
},{
|
|
.name = "telnet",
|
|
.type = QEMU_OPT_BOOL,
|
|
},{
|
|
.name = "width",
|
|
.type = QEMU_OPT_NUMBER,
|
|
},{
|
|
.name = "height",
|
|
.type = QEMU_OPT_NUMBER,
|
|
},{
|
|
.name = "cols",
|
|
.type = QEMU_OPT_NUMBER,
|
|
},{
|
|
.name = "rows",
|
|
.type = QEMU_OPT_NUMBER,
|
|
},{
|
|
.name = "mux",
|
|
.type = QEMU_OPT_BOOL,
|
|
},{
|
|
.name = "signal",
|
|
.type = QEMU_OPT_BOOL,
|
|
},
|
|
{ /* end if list */ }
|
|
},
|
|
};
|
|
|
|
QemuOptsList qemu_device_opts = {
|
|
.name = "device",
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_device_opts.head),
|
|
.desc = {
|
|
/*
|
|
* no elements => accept any
|
|
* sanity checking will happen later
|
|
* when setting device properties
|
|
*/
|
|
{ /* end if list */ }
|
|
},
|
|
};
|
|
|
|
QemuOptsList qemu_netdev_opts = {
|
|
.name = "netdev",
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_netdev_opts.head),
|
|
.desc = {
|
|
/*
|
|
* no elements => accept any params
|
|
* validation will happen later
|
|
*/
|
|
{ /* end of list */ }
|
|
},
|
|
};
|
|
|
|
QemuOptsList qemu_net_opts = {
|
|
.name = "net",
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_net_opts.head),
|
|
.desc = {
|
|
/*
|
|
* no elements => accept any params
|
|
* validation will happen later
|
|
*/
|
|
{ /* end of list */ }
|
|
},
|
|
};
|
|
|
|
QemuOptsList qemu_rtc_opts = {
|
|
.name = "rtc",
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_rtc_opts.head),
|
|
.desc = {
|
|
{
|
|
.name = "base",
|
|
.type = QEMU_OPT_STRING,
|
|
},{
|
|
.name = "clock",
|
|
.type = QEMU_OPT_STRING,
|
|
#ifdef TARGET_I386
|
|
},{
|
|
.name = "driftfix",
|
|
.type = QEMU_OPT_STRING,
|
|
#endif
|
|
},
|
|
{ /* end if list */ }
|
|
},
|
|
};
|
|
|
|
QemuOptsList qemu_global_opts = {
|
|
.name = "global",
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_global_opts.head),
|
|
.desc = {
|
|
{
|
|
.name = "driver",
|
|
.type = QEMU_OPT_STRING,
|
|
},{
|
|
.name = "property",
|
|
.type = QEMU_OPT_STRING,
|
|
},{
|
|
.name = "value",
|
|
.type = QEMU_OPT_STRING,
|
|
},
|
|
{ /* end if list */ }
|
|
},
|
|
};
|
|
|
|
QemuOptsList qemu_mon_opts = {
|
|
.name = "mon",
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_mon_opts.head),
|
|
.desc = {
|
|
{
|
|
.name = "mode",
|
|
.type = QEMU_OPT_STRING,
|
|
},{
|
|
.name = "chardev",
|
|
.type = QEMU_OPT_STRING,
|
|
},{
|
|
.name = "default",
|
|
.type = QEMU_OPT_BOOL,
|
|
},
|
|
{ /* end if list */ }
|
|
},
|
|
};
|
|
|
|
QemuOptsList qemu_cpudef_opts = {
|
|
.name = "cpudef",
|
|
.head = QTAILQ_HEAD_INITIALIZER(qemu_cpudef_opts.head),
|
|
.desc = {
|
|
{
|
|
.name = "name",
|
|
.type = QEMU_OPT_STRING,
|
|
},{
|
|
.name = "level",
|
|
.type = QEMU_OPT_NUMBER,
|
|
},{
|
|
.name = "vendor",
|
|
.type = QEMU_OPT_STRING,
|
|
},{
|
|
.name = "family",
|
|
.type = QEMU_OPT_NUMBER,
|
|
},{
|
|
.name = "model",
|
|
.type = QEMU_OPT_NUMBER,
|
|
},{
|
|
.name = "stepping",
|
|
.type = QEMU_OPT_NUMBER,
|
|
},{
|
|
.name = "feature_edx", /* cpuid 0000_0001.edx */
|
|
.type = QEMU_OPT_STRING,
|
|
},{
|
|
.name = "feature_ecx", /* cpuid 0000_0001.ecx */
|
|
.type = QEMU_OPT_STRING,
|
|
},{
|
|
.name = "extfeature_edx", /* cpuid 8000_0001.edx */
|
|
.type = QEMU_OPT_STRING,
|
|
},{
|
|
.name = "extfeature_ecx", /* cpuid 8000_0001.ecx */
|
|
.type = QEMU_OPT_STRING,
|
|
},{
|
|
.name = "xlevel",
|
|
.type = QEMU_OPT_NUMBER,
|
|
},{
|
|
.name = "model_id",
|
|
.type = QEMU_OPT_STRING,
|
|
},{
|
|
.name = "vendor_override",
|
|
.type = QEMU_OPT_NUMBER,
|
|
},
|
|
{ /* end of list */ }
|
|
},
|
|
};
|
|
|
|
static QemuOptsList *lists[] = {
|
|
&qemu_drive_opts,
|
|
&qemu_chardev_opts,
|
|
&qemu_device_opts,
|
|
&qemu_netdev_opts,
|
|
&qemu_net_opts,
|
|
&qemu_rtc_opts,
|
|
&qemu_global_opts,
|
|
&qemu_mon_opts,
|
|
&qemu_cpudef_opts,
|
|
NULL,
|
|
};
|
|
|
|
static QemuOptsList *find_list(const char *group)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; lists[i] != NULL; i++) {
|
|
if (strcmp(lists[i]->name, group) == 0)
|
|
break;
|
|
}
|
|
if (lists[i] == NULL) {
|
|
qemu_error("there is no option group \"%s\"\n", group);
|
|
}
|
|
return lists[i];
|
|
}
|
|
|
|
int qemu_set_option(const char *str)
|
|
{
|
|
char group[64], id[64], arg[64];
|
|
QemuOptsList *list;
|
|
QemuOpts *opts;
|
|
int rc, offset;
|
|
|
|
rc = sscanf(str, "%63[^.].%63[^.].%63[^=]%n", group, id, arg, &offset);
|
|
if (rc < 3 || str[offset] != '=') {
|
|
qemu_error("can't parse: \"%s\"\n", str);
|
|
return -1;
|
|
}
|
|
|
|
list = find_list(group);
|
|
if (list == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
opts = qemu_opts_find(list, id);
|
|
if (!opts) {
|
|
qemu_error("there is no %s \"%s\" defined\n",
|
|
list->name, id);
|
|
return -1;
|
|
}
|
|
|
|
if (qemu_opt_set(opts, arg, str+offset+1) == -1) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int qemu_global_option(const char *str)
|
|
{
|
|
char driver[64], property[64];
|
|
QemuOpts *opts;
|
|
int rc, offset;
|
|
|
|
rc = sscanf(str, "%63[^.].%63[^=]%n", driver, property, &offset);
|
|
if (rc < 2 || str[offset] != '=') {
|
|
qemu_error("can't parse: \"%s\"\n", str);
|
|
return -1;
|
|
}
|
|
|
|
opts = qemu_opts_create(&qemu_global_opts, NULL, 0);
|
|
qemu_opt_set(opts, "driver", driver);
|
|
qemu_opt_set(opts, "property", property);
|
|
qemu_opt_set(opts, "value", str+offset+1);
|
|
return 0;
|
|
}
|
|
|
|
static int qemu_add_one_global(QemuOpts *opts, void *opaque)
|
|
{
|
|
GlobalProperty *g;
|
|
|
|
g = qemu_mallocz(sizeof(*g));
|
|
g->driver = qemu_opt_get(opts, "driver");
|
|
g->property = qemu_opt_get(opts, "property");
|
|
g->value = qemu_opt_get(opts, "value");
|
|
qdev_prop_register_global(g);
|
|
return 0;
|
|
}
|
|
|
|
void qemu_add_globals(void)
|
|
{
|
|
qemu_opts_foreach(&qemu_global_opts, qemu_add_one_global, NULL, 0);
|
|
}
|
|
|
|
struct ConfigWriteData {
|
|
QemuOptsList *list;
|
|
FILE *fp;
|
|
};
|
|
|
|
static int config_write_opt(const char *name, const char *value, void *opaque)
|
|
{
|
|
struct ConfigWriteData *data = opaque;
|
|
|
|
fprintf(data->fp, " %s = \"%s\"\n", name, value);
|
|
return 0;
|
|
}
|
|
|
|
static int config_write_opts(QemuOpts *opts, void *opaque)
|
|
{
|
|
struct ConfigWriteData *data = opaque;
|
|
const char *id = qemu_opts_id(opts);
|
|
|
|
if (id) {
|
|
fprintf(data->fp, "[%s \"%s\"]\n", data->list->name, id);
|
|
} else {
|
|
fprintf(data->fp, "[%s]\n", data->list->name);
|
|
}
|
|
qemu_opt_foreach(opts, config_write_opt, data, 0);
|
|
fprintf(data->fp, "\n");
|
|
return 0;
|
|
}
|
|
|
|
void qemu_config_write(FILE *fp)
|
|
{
|
|
struct ConfigWriteData data = { .fp = fp };
|
|
int i;
|
|
|
|
fprintf(fp, "# qemu config file\n\n");
|
|
for (i = 0; lists[i] != NULL; i++) {
|
|
data.list = lists[i];
|
|
qemu_opts_foreach(data.list, config_write_opts, &data, 0);
|
|
}
|
|
}
|
|
|
|
int qemu_config_parse(FILE *fp)
|
|
{
|
|
char line[1024], group[64], id[64], arg[64], value[1024];
|
|
QemuOptsList *list = NULL;
|
|
QemuOpts *opts = NULL;
|
|
|
|
while (fgets(line, sizeof(line), fp) != NULL) {
|
|
if (line[0] == '\n') {
|
|
/* skip empty lines */
|
|
continue;
|
|
}
|
|
if (line[0] == '#') {
|
|
/* comment */
|
|
continue;
|
|
}
|
|
if (sscanf(line, "[%63s \"%63[^\"]\"]", group, id) == 2) {
|
|
/* group with id */
|
|
list = find_list(group);
|
|
if (list == NULL)
|
|
return -1;
|
|
opts = qemu_opts_create(list, id, 1);
|
|
continue;
|
|
}
|
|
if (sscanf(line, "[%63[^]]]", group) == 1) {
|
|
/* group without id */
|
|
list = find_list(group);
|
|
if (list == NULL)
|
|
return -1;
|
|
opts = qemu_opts_create(list, NULL, 0);
|
|
continue;
|
|
}
|
|
if (sscanf(line, " %63s = \"%1023[^\"]\"", arg, value) == 2) {
|
|
/* arg = value */
|
|
if (opts == NULL) {
|
|
fprintf(stderr, "no group defined\n");
|
|
return -1;
|
|
}
|
|
if (qemu_opt_set(opts, arg, value) != 0) {
|
|
fprintf(stderr, "failed to set \"%s\" for %s\n",
|
|
arg, group);
|
|
return -1;
|
|
}
|
|
continue;
|
|
}
|
|
fprintf(stderr, "parse error: %s\n", line);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|