1 /*
2 * QEMU CPU QMP commands for RISC-V
3 *
4 * Copyright (c) 2023 Ventana Micro Systems Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24
25 #include "qemu/osdep.h"
26
27 #include "qapi/error.h"
28 #include "qapi/qapi-commands-machine-target.h"
29 #include "qapi/qmp/qbool.h"
30 #include "qapi/qmp/qdict.h"
31 #include "qapi/qobject-input-visitor.h"
32 #include "qapi/visitor.h"
33 #include "qom/qom-qobject.h"
34 #include "sysemu/kvm.h"
35 #include "sysemu/tcg.h"
36 #include "cpu-qom.h"
37 #include "cpu.h"
38
riscv_cpu_add_definition(gpointer data,gpointer user_data)39 static void riscv_cpu_add_definition(gpointer data, gpointer user_data)
40 {
41 ObjectClass *oc = data;
42 CpuDefinitionInfoList **cpu_list = user_data;
43 CpuDefinitionInfo *info = g_malloc0(sizeof(*info));
44 const char *typename = object_class_get_name(oc);
45 ObjectClass *dyn_class;
46
47 info->name = cpu_model_from_type(typename);
48 info->q_typename = g_strdup(typename);
49
50 dyn_class = object_class_dynamic_cast(oc, TYPE_RISCV_DYNAMIC_CPU);
51 info->q_static = dyn_class == NULL;
52
53 QAPI_LIST_PREPEND(*cpu_list, info);
54 }
55
qmp_query_cpu_definitions(Error ** errp)56 CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
57 {
58 CpuDefinitionInfoList *cpu_list = NULL;
59 GSList *list = object_class_get_list(TYPE_RISCV_CPU, false);
60
61 g_slist_foreach(list, riscv_cpu_add_definition, &cpu_list);
62 g_slist_free(list);
63
64 return cpu_list;
65 }
66
riscv_check_if_cpu_available(RISCVCPU * cpu,Error ** errp)67 static void riscv_check_if_cpu_available(RISCVCPU *cpu, Error **errp)
68 {
69 if (!riscv_cpu_accelerator_compatible(cpu)) {
70 g_autofree char *name = riscv_cpu_get_name(cpu);
71 const char *accel = kvm_enabled() ? "kvm" : "tcg";
72
73 error_setg(errp, "'%s' CPU not available with %s", name, accel);
74 return;
75 }
76 }
77
riscv_obj_add_qdict_prop(Object * obj,QDict * qdict_out,const char * name)78 static void riscv_obj_add_qdict_prop(Object *obj, QDict *qdict_out,
79 const char *name)
80 {
81 ObjectProperty *prop = object_property_find(obj, name);
82
83 if (prop) {
84 QObject *value;
85
86 assert(prop->get);
87 value = object_property_get_qobject(obj, name, &error_abort);
88
89 qdict_put_obj(qdict_out, name, value);
90 }
91 }
92
riscv_obj_add_multiext_props(Object * obj,QDict * qdict_out,const RISCVCPUMultiExtConfig * arr)93 static void riscv_obj_add_multiext_props(Object *obj, QDict *qdict_out,
94 const RISCVCPUMultiExtConfig *arr)
95 {
96 for (int i = 0; arr[i].name != NULL; i++) {
97 riscv_obj_add_qdict_prop(obj, qdict_out, arr[i].name);
98 }
99 }
100
riscv_obj_add_named_feats_qdict(Object * obj,QDict * qdict_out)101 static void riscv_obj_add_named_feats_qdict(Object *obj, QDict *qdict_out)
102 {
103 const RISCVCPUMultiExtConfig *named_cfg;
104 RISCVCPU *cpu = RISCV_CPU(obj);
105 QObject *value;
106 bool flag_val;
107
108 for (int i = 0; riscv_cpu_named_features[i].name != NULL; i++) {
109 named_cfg = &riscv_cpu_named_features[i];
110 flag_val = isa_ext_is_enabled(cpu, named_cfg->offset);
111 value = QOBJECT(qbool_from_bool(flag_val));
112
113 qdict_put_obj(qdict_out, named_cfg->name, value);
114 }
115 }
116
riscv_obj_add_profiles_qdict(Object * obj,QDict * qdict_out)117 static void riscv_obj_add_profiles_qdict(Object *obj, QDict *qdict_out)
118 {
119 RISCVCPUProfile *profile;
120 QObject *value;
121
122 for (int i = 0; riscv_profiles[i] != NULL; i++) {
123 profile = riscv_profiles[i];
124 value = QOBJECT(qbool_from_bool(profile->enabled));
125
126 qdict_put_obj(qdict_out, profile->name, value);
127 }
128 }
129
riscv_cpuobj_validate_qdict_in(Object * obj,QObject * props,const char * props_arg_name,Error ** errp)130 static void riscv_cpuobj_validate_qdict_in(Object *obj, QObject *props,
131 const char *props_arg_name,
132 Error **errp)
133 {
134 const QDict *qdict_in;
135 const QDictEntry *qe;
136 Visitor *visitor;
137 Error *local_err = NULL;
138
139 visitor = qobject_input_visitor_new(props);
140 if (!visit_start_struct(visitor, props_arg_name, NULL, 0, &local_err)) {
141 goto err;
142 }
143
144 qdict_in = qobject_to(QDict, props);
145 for (qe = qdict_first(qdict_in); qe; qe = qdict_next(qdict_in, qe)) {
146 object_property_find_err(obj, qe->key, &local_err);
147 if (local_err) {
148 goto err;
149 }
150
151 object_property_set(obj, qe->key, visitor, &local_err);
152 if (local_err) {
153 goto err;
154 }
155 }
156
157 visit_check_struct(visitor, &local_err);
158 if (local_err) {
159 goto err;
160 }
161
162 visit_end_struct(visitor, NULL);
163
164 err:
165 error_propagate(errp, local_err);
166 visit_free(visitor);
167 }
168
qmp_query_cpu_model_expansion(CpuModelExpansionType type,CpuModelInfo * model,Error ** errp)169 CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type,
170 CpuModelInfo *model,
171 Error **errp)
172 {
173 CpuModelExpansionInfo *expansion_info;
174 QDict *qdict_out;
175 ObjectClass *oc;
176 Object *obj;
177 Error *local_err = NULL;
178
179 if (type != CPU_MODEL_EXPANSION_TYPE_FULL) {
180 error_setg(errp, "The requested expansion type is not supported");
181 return NULL;
182 }
183
184 oc = cpu_class_by_name(TYPE_RISCV_CPU, model->name);
185 if (!oc) {
186 error_setg(errp, "The CPU type '%s' is not a known RISC-V CPU type",
187 model->name);
188 return NULL;
189 }
190
191 obj = object_new(object_class_get_name(oc));
192
193 riscv_check_if_cpu_available(RISCV_CPU(obj), &local_err);
194 if (local_err != NULL) {
195 error_propagate(errp, local_err);
196 object_unref(obj);
197 return NULL;
198 }
199
200 if (model->props) {
201 riscv_cpuobj_validate_qdict_in(obj, model->props, "model.props",
202 &local_err);
203 if (local_err) {
204 error_propagate(errp, local_err);
205 object_unref(obj);
206 return NULL;
207 }
208 }
209
210 riscv_cpu_finalize_features(RISCV_CPU(obj), &local_err);
211 if (local_err) {
212 error_propagate(errp, local_err);
213 object_unref(obj);
214 return NULL;
215 }
216
217 expansion_info = g_new0(CpuModelExpansionInfo, 1);
218 expansion_info->model = g_malloc0(sizeof(*expansion_info->model));
219 expansion_info->model->name = g_strdup(model->name);
220
221 qdict_out = qdict_new();
222
223 riscv_obj_add_multiext_props(obj, qdict_out, riscv_cpu_extensions);
224 riscv_obj_add_multiext_props(obj, qdict_out, riscv_cpu_experimental_exts);
225 riscv_obj_add_multiext_props(obj, qdict_out, riscv_cpu_vendor_exts);
226 riscv_obj_add_named_feats_qdict(obj, qdict_out);
227 riscv_obj_add_profiles_qdict(obj, qdict_out);
228
229 /* Add our CPU boolean options too */
230 riscv_obj_add_qdict_prop(obj, qdict_out, "mmu");
231 riscv_obj_add_qdict_prop(obj, qdict_out, "pmp");
232
233 if (!qdict_size(qdict_out)) {
234 qobject_unref(qdict_out);
235 } else {
236 expansion_info->model->props = QOBJECT(qdict_out);
237 }
238
239 object_unref(obj);
240
241 return expansion_info;
242 }
243