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/qmp/qerror.h" 32 #include "qapi/qobject-input-visitor.h" 33 #include "qapi/visitor.h" 34 #include "qom/qom-qobject.h" 35 #include "sysemu/kvm.h" 36 #include "sysemu/tcg.h" 37 #include "cpu-qom.h" 38 #include "cpu.h" 39 40 static void riscv_cpu_add_definition(gpointer data, gpointer user_data) 41 { 42 ObjectClass *oc = data; 43 CpuDefinitionInfoList **cpu_list = user_data; 44 CpuDefinitionInfo *info = g_malloc0(sizeof(*info)); 45 const char *typename = object_class_get_name(oc); 46 ObjectClass *dyn_class; 47 48 info->name = cpu_model_from_type(typename); 49 info->q_typename = g_strdup(typename); 50 51 dyn_class = object_class_dynamic_cast(oc, TYPE_RISCV_DYNAMIC_CPU); 52 info->q_static = dyn_class == NULL; 53 54 QAPI_LIST_PREPEND(*cpu_list, info); 55 } 56 57 CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) 58 { 59 CpuDefinitionInfoList *cpu_list = NULL; 60 GSList *list = object_class_get_list(TYPE_RISCV_CPU, false); 61 62 g_slist_foreach(list, riscv_cpu_add_definition, &cpu_list); 63 g_slist_free(list); 64 65 return cpu_list; 66 } 67 68 static void riscv_check_if_cpu_available(RISCVCPU *cpu, Error **errp) 69 { 70 if (!riscv_cpu_accelerator_compatible(cpu)) { 71 g_autofree char *name = riscv_cpu_get_name(cpu); 72 const char *accel = kvm_enabled() ? "kvm" : "tcg"; 73 74 error_setg(errp, "'%s' CPU not available with %s", name, accel); 75 return; 76 } 77 } 78 79 static void riscv_obj_add_qdict_prop(Object *obj, QDict *qdict_out, 80 const char *name) 81 { 82 ObjectProperty *prop = object_property_find(obj, name); 83 84 if (prop) { 85 QObject *value; 86 87 assert(prop->get); 88 value = object_property_get_qobject(obj, name, &error_abort); 89 90 qdict_put_obj(qdict_out, name, value); 91 } 92 } 93 94 static void riscv_obj_add_multiext_props(Object *obj, QDict *qdict_out, 95 const RISCVCPUMultiExtConfig *arr) 96 { 97 for (int i = 0; arr[i].name != NULL; i++) { 98 riscv_obj_add_qdict_prop(obj, qdict_out, arr[i].name); 99 } 100 } 101 102 static void riscv_obj_add_named_feats_qdict(Object *obj, QDict *qdict_out) 103 { 104 const RISCVCPUMultiExtConfig *named_cfg; 105 RISCVCPU *cpu = RISCV_CPU(obj); 106 QObject *value; 107 bool flag_val; 108 109 for (int i = 0; riscv_cpu_named_features[i].name != NULL; i++) { 110 named_cfg = &riscv_cpu_named_features[i]; 111 flag_val = isa_ext_is_enabled(cpu, named_cfg->offset); 112 value = QOBJECT(qbool_from_bool(flag_val)); 113 114 qdict_put_obj(qdict_out, named_cfg->name, value); 115 } 116 } 117 118 static void riscv_obj_add_profiles_qdict(Object *obj, QDict *qdict_out) 119 { 120 RISCVCPUProfile *profile; 121 QObject *value; 122 123 for (int i = 0; riscv_profiles[i] != NULL; i++) { 124 profile = riscv_profiles[i]; 125 value = QOBJECT(qbool_from_bool(profile->enabled)); 126 127 qdict_put_obj(qdict_out, profile->name, value); 128 } 129 } 130 131 static void riscv_cpuobj_validate_qdict_in(Object *obj, QObject *props, 132 const QDict *qdict_in, 133 Error **errp) 134 { 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, NULL, NULL, 0, &local_err)) { 141 goto err; 142 } 143 144 for (qe = qdict_first(qdict_in); qe; qe = qdict_next(qdict_in, qe)) { 145 object_property_find_err(obj, qe->key, &local_err); 146 if (local_err) { 147 goto err; 148 } 149 150 object_property_set(obj, qe->key, visitor, &local_err); 151 if (local_err) { 152 goto err; 153 } 154 } 155 156 visit_check_struct(visitor, &local_err); 157 if (local_err) { 158 goto err; 159 } 160 161 visit_end_struct(visitor, NULL); 162 163 err: 164 error_propagate(errp, local_err); 165 visit_free(visitor); 166 } 167 168 CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, 169 CpuModelInfo *model, 170 Error **errp) 171 { 172 CpuModelExpansionInfo *expansion_info; 173 const QDict *qdict_in = NULL; 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 if (model->props) { 192 qdict_in = qobject_to(QDict, model->props); 193 if (!qdict_in) { 194 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "props", "dict"); 195 return NULL; 196 } 197 } 198 199 obj = object_new(object_class_get_name(oc)); 200 201 riscv_check_if_cpu_available(RISCV_CPU(obj), &local_err); 202 if (local_err != NULL) { 203 error_propagate(errp, local_err); 204 object_unref(obj); 205 return NULL; 206 } 207 208 if (qdict_in) { 209 riscv_cpuobj_validate_qdict_in(obj, model->props, qdict_in, 210 &local_err); 211 if (local_err) { 212 error_propagate(errp, local_err); 213 object_unref(obj); 214 return NULL; 215 } 216 } 217 218 riscv_cpu_finalize_features(RISCV_CPU(obj), &local_err); 219 if (local_err) { 220 error_propagate(errp, local_err); 221 object_unref(obj); 222 return NULL; 223 } 224 225 expansion_info = g_new0(CpuModelExpansionInfo, 1); 226 expansion_info->model = g_malloc0(sizeof(*expansion_info->model)); 227 expansion_info->model->name = g_strdup(model->name); 228 229 qdict_out = qdict_new(); 230 231 riscv_obj_add_multiext_props(obj, qdict_out, riscv_cpu_extensions); 232 riscv_obj_add_multiext_props(obj, qdict_out, riscv_cpu_experimental_exts); 233 riscv_obj_add_multiext_props(obj, qdict_out, riscv_cpu_vendor_exts); 234 riscv_obj_add_named_feats_qdict(obj, qdict_out); 235 riscv_obj_add_profiles_qdict(obj, qdict_out); 236 237 /* Add our CPU boolean options too */ 238 riscv_obj_add_qdict_prop(obj, qdict_out, "mmu"); 239 riscv_obj_add_qdict_prop(obj, qdict_out, "pmp"); 240 241 if (!qdict_size(qdict_out)) { 242 qobject_unref(qdict_out); 243 } else { 244 expansion_info->model->props = QOBJECT(qdict_out); 245 } 246 247 object_unref(obj); 248 249 return expansion_info; 250 } 251