mirror of
https://github.com/yuzu-emu/unicorn.git
synced 2024-12-23 05:25:31 +00:00
cpu: Use CPUClass->parse_features() as convertor to global properties
Currently CPUClass->parse_features() is used to parse -cpu features string and set properties on created CPU instances. But considering that features specified by -cpu apply to every created CPU instance, it doesn't make sense to parse the same features string for every CPU created. It also makes every target that cares about parsing features string explicitly call CPUClass->parse_features() parser, which gets in a way if we consider using generic device_add for CPU hotplug as device_add has not a clue about CPU specific hooks. Turns out we can use global properties mechanism to set properties on every created CPU instance for a given type. That way it's possible to convert CPU features into a set of global properties for CPU type specified by -cpu cpu_model and common Device.device_post_init() will apply them to CPU of given type automatically regardless whether it's manually created CPU or CPU created with help of device_add. Backports commits 62a48a2a5798425997152dea3fc48708f9116c04 and f313369fdb78f849ecbbd8e5d88f01ddf38786c8 from qemu
This commit is contained in:
parent
f86355f82c
commit
87db6e033b
|
@ -226,6 +226,8 @@ struct uc_struct {
|
||||||
QTAILQ_HEAD(memory_listeners, MemoryListener) memory_listeners;
|
QTAILQ_HEAD(memory_listeners, MemoryListener) memory_listeners;
|
||||||
QTAILQ_HEAD(, AddressSpace) address_spaces;
|
QTAILQ_HEAD(, AddressSpace) address_spaces;
|
||||||
MachineState *machine_state;
|
MachineState *machine_state;
|
||||||
|
// qom/cpu.c
|
||||||
|
bool cpu_globals_initialized;
|
||||||
// qom/object.c
|
// qom/object.c
|
||||||
GHashTable *type_table;
|
GHashTable *type_table;
|
||||||
Type type_interface;
|
Type type_interface;
|
||||||
|
|
|
@ -61,6 +61,7 @@ typedef struct {
|
||||||
static int machvirt_init(struct uc_struct *uc, MachineState *machine)
|
static int machvirt_init(struct uc_struct *uc, MachineState *machine)
|
||||||
{
|
{
|
||||||
const char *cpu_model = machine->cpu_model;
|
const char *cpu_model = machine->cpu_model;
|
||||||
|
char **cpustr;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
if (!cpu_model) {
|
if (!cpu_model) {
|
||||||
|
@ -69,20 +70,31 @@ static int machvirt_init(struct uc_struct *uc, MachineState *machine)
|
||||||
cpu_model = "max";
|
cpu_model = "max";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Separate the actual CPU model name from any appended features */
|
||||||
|
cpustr = g_strsplit(cpu_model, ",", 2);
|
||||||
|
|
||||||
for (n = 0; n < smp_cpus; n++) {
|
for (n = 0; n < smp_cpus; n++) {
|
||||||
|
ObjectClass *oc = cpu_class_by_name(uc, TYPE_ARM_CPU, cpustr[0]);
|
||||||
|
char *cpuopts = g_strdup(cpustr[1]);
|
||||||
|
CPUClass *cc = CPU_CLASS(uc, oc);
|
||||||
Object *cpuobj;
|
Object *cpuobj;
|
||||||
ObjectClass *oc = cpu_class_by_name(uc, TYPE_ARM_CPU, cpu_model);
|
Error *err = NULL;
|
||||||
|
const char *typename = object_class_get_name(oc);
|
||||||
|
|
||||||
if (!oc) {
|
if (!oc) {
|
||||||
fprintf(stderr, "Unable to find CPU definition\n");
|
fprintf(stderr, "Unable to find CPU definition\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
cpuobj = object_new(uc, object_class_get_name(oc));
|
/* convert -smp CPU options specified by the user into global props */
|
||||||
|
cc->parse_features(uc, typename, cpuopts, &err);
|
||||||
|
cpuobj = object_new(uc, typename);
|
||||||
uc->cpu = CPU(cpuobj);
|
uc->cpu = CPU(cpuobj);
|
||||||
object_property_set_bool(uc, cpuobj, true, "realized", NULL);
|
object_property_set_bool(uc, cpuobj, true, "realized", NULL);
|
||||||
}
|
|
||||||
|
|
||||||
|
g_free(cpuopts);
|
||||||
|
}
|
||||||
|
g_strfreev(cpustr);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -124,7 +124,7 @@ typedef struct CPUClass {
|
||||||
/*< public >*/
|
/*< public >*/
|
||||||
|
|
||||||
ObjectClass *(*class_by_name)(struct uc_struct *uc, const char *cpu_model);
|
ObjectClass *(*class_by_name)(struct uc_struct *uc, const char *cpu_model);
|
||||||
void (*parse_features)(CPUState *cpu, char *str, Error **errp);
|
void (*parse_features)(struct uc_struct *uc, const char *typename, char *str, Error **errp);
|
||||||
|
|
||||||
void (*reset)(CPUState *cpu);
|
void (*reset)(CPUState *cpu);
|
||||||
int reset_dump_flags;
|
int reset_dump_flags;
|
||||||
|
|
|
@ -44,7 +44,7 @@ bool cpu_exists(struct uc_struct *uc, int64_t id)
|
||||||
CPUState *cpu_generic_init(struct uc_struct *uc, const char *typename, const char *cpu_model)
|
CPUState *cpu_generic_init(struct uc_struct *uc, const char *typename, const char *cpu_model)
|
||||||
{
|
{
|
||||||
char *str, *name, *featurestr;
|
char *str, *name, *featurestr;
|
||||||
CPUState *cpu;
|
CPUState *cpu = NULL;
|
||||||
ObjectClass *oc;
|
ObjectClass *oc;
|
||||||
CPUClass *cc;
|
CPUClass *cc;
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
@ -58,16 +58,18 @@ CPUState *cpu_generic_init(struct uc_struct *uc, const char *typename, const cha
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu = CPU(object_new(uc, object_class_get_name(oc)));
|
cc = CPU_CLASS(uc, oc);
|
||||||
cc = CPU_GET_CLASS(uc, cpu);
|
|
||||||
|
|
||||||
featurestr = strtok(NULL, ",");
|
featurestr = strtok(NULL, ",");
|
||||||
cc->parse_features(cpu, featurestr, &err);
|
/* TODO: all callers of cpu_generic_init() need to be converted to
|
||||||
|
* call parse_features() only once, before calling cpu_generic_init().
|
||||||
|
*/
|
||||||
|
cc->parse_features(uc, object_class_get_name(oc), featurestr, &err);
|
||||||
g_free(str);
|
g_free(str);
|
||||||
if (err != NULL) {
|
if (err != NULL) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cpu = CPU(object_new(uc, object_class_get_name(oc)));
|
||||||
object_property_set_bool(uc, OBJECT(cpu), true, "realized", &err);
|
object_property_set_bool(uc, OBJECT(cpu), true, "realized", &err);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
@ -218,25 +220,45 @@ static ObjectClass *cpu_common_class_by_name(struct uc_struct *uc, const char *c
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cpu_common_parse_features(CPUState *cpu, char *features,
|
static void cpu_common_parse_features(struct uc_struct *uc, const char *typename, char *features,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
char *featurestr; /* Single "key=value" string being parsed */
|
char *featurestr; /* Single "key=value" string being parsed */
|
||||||
char *val;
|
char *val;
|
||||||
Error *err = NULL;
|
|
||||||
|
/* TODO: all callers of ->parse_features() need to be changed to
|
||||||
|
* call it only once, so we can remove this check (or change it
|
||||||
|
* to assert(!cpu_globals_initialized).
|
||||||
|
* Current callers of ->parse_features() are:
|
||||||
|
* - machvirt_init()
|
||||||
|
* - cpu_generic_init()
|
||||||
|
* - cpu_x86_create()
|
||||||
|
*/
|
||||||
|
if (uc->cpu_globals_initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uc->cpu_globals_initialized = true;
|
||||||
|
|
||||||
featurestr = features ? strtok(features, ",") : NULL;
|
featurestr = features ? strtok(features, ",") : NULL;
|
||||||
|
|
||||||
while (featurestr) {
|
while (featurestr) {
|
||||||
val = strchr(featurestr, '=');
|
val = strchr(featurestr, '=');
|
||||||
if (val) {
|
if (val) {
|
||||||
|
// Unicorn: if'd out
|
||||||
|
#if 0
|
||||||
|
GlobalProperty *prop = g_new0(GlobalProperty, 1);
|
||||||
|
#endif
|
||||||
*val = 0;
|
*val = 0;
|
||||||
val++;
|
val++;
|
||||||
object_property_parse(cpu->uc, OBJECT(cpu), val, featurestr, &err);
|
|
||||||
if (err) {
|
// Unicorn: If'd out
|
||||||
error_propagate(errp, err);
|
#if 0
|
||||||
return;
|
prop->driver = typename;
|
||||||
}
|
prop->property = g_strdup(featurestr);
|
||||||
|
prop->value = g_strdup(val);
|
||||||
|
prop->errp = &error_fatal;
|
||||||
|
qdev_prop_register_global(prop);
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
error_setg(errp, "Expected key=value format, found %s.",
|
error_setg(errp, "Expected key=value format, found %s.",
|
||||||
featurestr);
|
featurestr);
|
||||||
|
|
|
@ -3018,16 +3018,17 @@ static inline void feat2prop(char *s)
|
||||||
|
|
||||||
/* Parse "+feature,-feature,feature=foo" CPU feature string
|
/* Parse "+feature,-feature,feature=foo" CPU feature string
|
||||||
*/
|
*/
|
||||||
static void x86_cpu_parse_featurestr(CPUState *cs, char *features,
|
static void x86_cpu_parse_featurestr(struct uc_struct *uc, const char *typename, char *features,
|
||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
X86CPU *cpu = X86_CPU(cs->uc, cs);
|
X86CPU *cpu = X86_CPU(uc, uc->cpu);
|
||||||
char *featurestr; /* Single 'key=value" string being parsed */
|
char *featurestr; /* Single 'key=value" string being parsed */
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
|
|
||||||
// Unicorn: added for consistent zeroing out
|
if (cpu->cpu_globals_initialized) {
|
||||||
memset(cpu->plus_features, 0, sizeof(cpu->plus_features));
|
return;
|
||||||
memset(cpu->minus_features, 0, sizeof(cpu->minus_features));
|
}
|
||||||
|
cpu->cpu_globals_initialized = true;
|
||||||
|
|
||||||
if (!features) {
|
if (!features) {
|
||||||
return;
|
return;
|
||||||
|
@ -3040,6 +3041,10 @@ static void x86_cpu_parse_featurestr(CPUState *cs, char *features,
|
||||||
const char *val = NULL;
|
const char *val = NULL;
|
||||||
char *eq = NULL;
|
char *eq = NULL;
|
||||||
char num[32];
|
char num[32];
|
||||||
|
// Unicorn: If'd out
|
||||||
|
#if 0
|
||||||
|
GlobalProperty *prop;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Compatibility syntax: */
|
/* Compatibility syntax: */
|
||||||
if (featurestr[0] == '+') {
|
if (featurestr[0] == '+') {
|
||||||
|
@ -3076,7 +3081,15 @@ static void x86_cpu_parse_featurestr(CPUState *cs, char *features,
|
||||||
name = "tsc-frequency";
|
name = "tsc-frequency";
|
||||||
}
|
}
|
||||||
|
|
||||||
object_property_parse(cs->uc, OBJECT(cpu), val, name, &local_err);
|
// Unicorn: if'd out
|
||||||
|
#if 0
|
||||||
|
prop = g_new0(GlobalProperty, 1);
|
||||||
|
prop->driver = typename;
|
||||||
|
prop->property = g_strdup(name);
|
||||||
|
prop->value = g_strdup(val);
|
||||||
|
prop->errp = &error_fatal;
|
||||||
|
qdev_prop_register_global(prop);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
|
@ -3164,9 +3177,11 @@ X86CPU *cpu_x86_create(struct uc_struct *uc, const char *cpu_model, Error **errp
|
||||||
{
|
{
|
||||||
X86CPU *cpu = NULL;
|
X86CPU *cpu = NULL;
|
||||||
ObjectClass *oc;
|
ObjectClass *oc;
|
||||||
|
CPUClass *cc;
|
||||||
gchar **model_pieces;
|
gchar **model_pieces;
|
||||||
char *name, *features;
|
char *name, *features;
|
||||||
Error *error = NULL;
|
Error *error = NULL;
|
||||||
|
const char *typename;
|
||||||
|
|
||||||
model_pieces = g_strsplit(cpu_model, ",", 2);
|
model_pieces = g_strsplit(cpu_model, ",", 2);
|
||||||
if (!model_pieces[0]) {
|
if (!model_pieces[0]) {
|
||||||
|
@ -3181,10 +3196,11 @@ X86CPU *cpu_x86_create(struct uc_struct *uc, const char *cpu_model, Error **errp
|
||||||
error_setg(&error, "Unable to find CPU definition: %s", name);
|
error_setg(&error, "Unable to find CPU definition: %s", name);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
cc = CPU_CLASS(uc, oc);
|
||||||
|
typename = object_class_get_name(oc);
|
||||||
|
|
||||||
cpu = X86_CPU(uc, object_new(uc, object_class_get_name(oc)));
|
cc->parse_features(uc, typename, features, &error);
|
||||||
|
cpu = X86_CPU(uc, object_new(uc, typename));
|
||||||
x86_cpu_parse_featurestr(CPU(cpu), features, &error);
|
|
||||||
if (error) {
|
if (error) {
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1211,6 +1211,7 @@ typedef struct CPUX86State {
|
||||||
|
|
||||||
// Unicorn engine
|
// Unicorn engine
|
||||||
struct uc_struct *uc;
|
struct uc_struct *uc;
|
||||||
|
bool cpu_globals_initialized;
|
||||||
} CPUX86State;
|
} CPUX86State;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue