]>
Commit | Line | Data |
---|---|---|
ae50a770 PX |
1 | /* |
2 | * QEMU monitor.c for ARM. | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
5 | * of this software and associated documentation files (the "Software"), to deal | |
6 | * in the Software without restriction, including without limitation the rights | |
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
8 | * copies of the Software, and to permit persons to whom the Software is | |
9 | * furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice shall be included in | |
12 | * all copies or substantial portions of the Software. | |
13 | * | |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
20 | * THE SOFTWARE. | |
21 | */ | |
112ed241 | 22 | |
ae50a770 | 23 | #include "qemu/osdep.h" |
e19afd56 | 24 | #include "hw/boards.h" |
db31e49a | 25 | #include "kvm_arm.h" |
e19afd56 AJ |
26 | #include "qapi/error.h" |
27 | #include "qapi/visitor.h" | |
28 | #include "qapi/qobject-input-visitor.h" | |
29 | #include "qapi/qapi-commands-machine-target.h" | |
b0227cdb | 30 | #include "qapi/qapi-commands-misc-target.h" |
e19afd56 AJ |
31 | #include "qapi/qmp/qerror.h" |
32 | #include "qapi/qmp/qdict.h" | |
33 | #include "qom/qom-qobject.h" | |
db31e49a PX |
34 | |
35 | static GICCapability *gic_cap_new(int version) | |
36 | { | |
37 | GICCapability *cap = g_new0(GICCapability, 1); | |
38 | cap->version = version; | |
39 | /* by default, support none */ | |
40 | cap->emulated = false; | |
41 | cap->kernel = false; | |
42 | return cap; | |
43 | } | |
44 | ||
db31e49a PX |
45 | static inline void gic_cap_kvm_probe(GICCapability *v2, GICCapability *v3) |
46 | { | |
47 | #ifdef CONFIG_KVM | |
48 | int fdarray[3]; | |
49 | ||
50 | if (!kvm_arm_create_scratch_host_vcpu(NULL, fdarray, NULL)) { | |
51 | return; | |
52 | } | |
53 | ||
54 | /* Test KVM GICv2 */ | |
55 | if (kvm_device_supported(fdarray[1], KVM_DEV_TYPE_ARM_VGIC_V2)) { | |
56 | v2->kernel = true; | |
57 | } | |
58 | ||
59 | /* Test KVM GICv3 */ | |
60 | if (kvm_device_supported(fdarray[1], KVM_DEV_TYPE_ARM_VGIC_V3)) { | |
61 | v3->kernel = true; | |
62 | } | |
63 | ||
64 | kvm_arm_destroy_scratch_host_vcpu(fdarray); | |
65 | #endif | |
66 | } | |
ae50a770 PX |
67 | |
68 | GICCapabilityList *qmp_query_gic_capabilities(Error **errp) | |
69 | { | |
db31e49a PX |
70 | GICCapabilityList *head = NULL; |
71 | GICCapability *v2 = gic_cap_new(2), *v3 = gic_cap_new(3); | |
72 | ||
73 | v2->emulated = true; | |
3b1a2225 | 74 | v3->emulated = true; |
db31e49a PX |
75 | |
76 | gic_cap_kvm_probe(v2, v3); | |
77 | ||
54aa3de7 EB |
78 | QAPI_LIST_PREPEND(head, v2); |
79 | QAPI_LIST_PREPEND(head, v3); | |
db31e49a PX |
80 | |
81 | return head; | |
ae50a770 | 82 | } |
e19afd56 | 83 | |
0df9142d AJ |
84 | QEMU_BUILD_BUG_ON(ARM_MAX_VQ > 16); |
85 | ||
e19afd56 AJ |
86 | /* |
87 | * These are cpu model features we want to advertise. The order here | |
88 | * matters as this is the order in which qmp_query_cpu_model_expansion | |
89 | * will attempt to set them. If there are dependencies between features, | |
90 | * then the order that considers those dependencies must be used. | |
91 | */ | |
92 | static const char *cpu_model_advertised_features[] = { | |
73234775 | 93 | "aarch64", "pmu", "sve", |
0df9142d AJ |
94 | "sve128", "sve256", "sve384", "sve512", |
95 | "sve640", "sve768", "sve896", "sve1024", "sve1152", "sve1280", | |
96 | "sve1408", "sve1536", "sve1664", "sve1792", "sve1920", "sve2048", | |
68970d1e | 97 | "kvm-no-adjvtime", "kvm-steal-time", |
eb94284d | 98 | "pauth", "pauth-impdef", |
e19afd56 AJ |
99 | NULL |
100 | }; | |
101 | ||
102 | CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, | |
103 | CpuModelInfo *model, | |
104 | Error **errp) | |
105 | { | |
106 | CpuModelExpansionInfo *expansion_info; | |
107 | const QDict *qdict_in = NULL; | |
108 | QDict *qdict_out; | |
109 | ObjectClass *oc; | |
110 | Object *obj; | |
111 | const char *name; | |
112 | int i; | |
113 | ||
114 | if (type != CPU_MODEL_EXPANSION_TYPE_FULL) { | |
115 | error_setg(errp, "The requested expansion type is not supported"); | |
116 | return NULL; | |
117 | } | |
118 | ||
119 | if (!kvm_enabled() && !strcmp(model->name, "host")) { | |
120 | error_setg(errp, "The CPU type '%s' requires KVM", model->name); | |
121 | return NULL; | |
122 | } | |
123 | ||
124 | oc = cpu_class_by_name(TYPE_ARM_CPU, model->name); | |
125 | if (!oc) { | |
126 | error_setg(errp, "The CPU type '%s' is not a recognized ARM CPU type", | |
127 | model->name); | |
128 | return NULL; | |
129 | } | |
130 | ||
131 | if (kvm_enabled()) { | |
e19afd56 AJ |
132 | bool supported = false; |
133 | ||
134 | if (!strcmp(model->name, "host") || !strcmp(model->name, "max")) { | |
135 | /* These are kvmarm's recommended cpu types */ | |
136 | supported = true; | |
0999a4ba LY |
137 | } else if (current_machine->cpu_type) { |
138 | const char *cpu_type = current_machine->cpu_type; | |
139 | int len = strlen(cpu_type) - strlen(ARM_CPU_TYPE_SUFFIX); | |
140 | ||
141 | if (strlen(model->name) == len && | |
142 | !strncmp(model->name, cpu_type, len)) { | |
143 | /* KVM is enabled and we're using this type, so it works. */ | |
144 | supported = true; | |
145 | } | |
e19afd56 AJ |
146 | } |
147 | if (!supported) { | |
148 | error_setg(errp, "We cannot guarantee the CPU type '%s' works " | |
149 | "with KVM on this host", model->name); | |
150 | return NULL; | |
151 | } | |
152 | } | |
153 | ||
154 | if (model->props) { | |
155 | qdict_in = qobject_to(QDict, model->props); | |
156 | if (!qdict_in) { | |
157 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "props", "dict"); | |
158 | return NULL; | |
159 | } | |
160 | } | |
161 | ||
162 | obj = object_new(object_class_get_name(oc)); | |
163 | ||
164 | if (qdict_in) { | |
165 | Visitor *visitor; | |
166 | Error *err = NULL; | |
167 | ||
168 | visitor = qobject_input_visitor_new(model->props); | |
668f62ec | 169 | if (!visit_start_struct(visitor, NULL, NULL, 0, errp)) { |
e19afd56 AJ |
170 | visit_free(visitor); |
171 | object_unref(obj); | |
e19afd56 AJ |
172 | return NULL; |
173 | } | |
174 | ||
175 | i = 0; | |
176 | while ((name = cpu_model_advertised_features[i++]) != NULL) { | |
177 | if (qdict_get(qdict_in, name)) { | |
778a2dc5 | 178 | if (!object_property_set(obj, name, visitor, &err)) { |
e19afd56 AJ |
179 | break; |
180 | } | |
181 | } | |
182 | } | |
183 | ||
184 | if (!err) { | |
185 | visit_check_struct(visitor, &err); | |
186 | } | |
0df9142d AJ |
187 | if (!err) { |
188 | arm_cpu_finalize_features(ARM_CPU(obj), &err); | |
189 | } | |
e19afd56 AJ |
190 | visit_end_struct(visitor, NULL); |
191 | visit_free(visitor); | |
192 | if (err) { | |
193 | object_unref(obj); | |
194 | error_propagate(errp, err); | |
195 | return NULL; | |
196 | } | |
0df9142d | 197 | } else { |
20ac582d | 198 | arm_cpu_finalize_features(ARM_CPU(obj), &error_abort); |
e19afd56 AJ |
199 | } |
200 | ||
201 | expansion_info = g_new0(CpuModelExpansionInfo, 1); | |
202 | expansion_info->model = g_malloc0(sizeof(*expansion_info->model)); | |
203 | expansion_info->model->name = g_strdup(model->name); | |
204 | ||
205 | qdict_out = qdict_new(); | |
206 | ||
207 | i = 0; | |
208 | while ((name = cpu_model_advertised_features[i++]) != NULL) { | |
efba1595 | 209 | ObjectProperty *prop = object_property_find(obj, name); |
e19afd56 | 210 | if (prop) { |
e19afd56 AJ |
211 | QObject *value; |
212 | ||
213 | assert(prop->get); | |
20ac582d | 214 | value = object_property_get_qobject(obj, name, &error_abort); |
e19afd56 AJ |
215 | |
216 | qdict_put_obj(qdict_out, name, value); | |
217 | } | |
218 | } | |
219 | ||
220 | if (!qdict_size(qdict_out)) { | |
221 | qobject_unref(qdict_out); | |
222 | } else { | |
223 | expansion_info->model->props = QOBJECT(qdict_out); | |
e19afd56 AJ |
224 | } |
225 | ||
226 | object_unref(obj); | |
227 | ||
228 | return expansion_info; | |
229 | } | |
3362f04d PMD |
230 | |
231 | static void arm_cpu_add_definition(gpointer data, gpointer user_data) | |
232 | { | |
233 | ObjectClass *oc = data; | |
234 | CpuDefinitionInfoList **cpu_list = user_data; | |
235 | CpuDefinitionInfo *info; | |
236 | const char *typename; | |
237 | ||
238 | typename = object_class_get_name(oc); | |
239 | info = g_malloc0(sizeof(*info)); | |
240 | info->name = g_strndup(typename, | |
241 | strlen(typename) - strlen("-" TYPE_ARM_CPU)); | |
242 | info->q_typename = g_strdup(typename); | |
243 | ||
244 | QAPI_LIST_PREPEND(*cpu_list, info); | |
245 | } | |
246 | ||
247 | CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) | |
248 | { | |
249 | CpuDefinitionInfoList *cpu_list = NULL; | |
250 | GSList *list; | |
251 | ||
252 | list = object_class_get_list(TYPE_ARM_CPU, false); | |
253 | g_slist_foreach(list, arm_cpu_add_definition, &cpu_list); | |
254 | g_slist_free(list); | |
255 | ||
256 | return cpu_list; | |
257 | } |