]>
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 | ||
45 | static GICCapabilityList *gic_cap_list_add(GICCapabilityList *head, | |
46 | GICCapability *cap) | |
47 | { | |
48 | GICCapabilityList *item = g_new0(GICCapabilityList, 1); | |
49 | item->value = cap; | |
50 | item->next = head; | |
51 | return item; | |
52 | } | |
53 | ||
54 | static inline void gic_cap_kvm_probe(GICCapability *v2, GICCapability *v3) | |
55 | { | |
56 | #ifdef CONFIG_KVM | |
57 | int fdarray[3]; | |
58 | ||
59 | if (!kvm_arm_create_scratch_host_vcpu(NULL, fdarray, NULL)) { | |
60 | return; | |
61 | } | |
62 | ||
63 | /* Test KVM GICv2 */ | |
64 | if (kvm_device_supported(fdarray[1], KVM_DEV_TYPE_ARM_VGIC_V2)) { | |
65 | v2->kernel = true; | |
66 | } | |
67 | ||
68 | /* Test KVM GICv3 */ | |
69 | if (kvm_device_supported(fdarray[1], KVM_DEV_TYPE_ARM_VGIC_V3)) { | |
70 | v3->kernel = true; | |
71 | } | |
72 | ||
73 | kvm_arm_destroy_scratch_host_vcpu(fdarray); | |
74 | #endif | |
75 | } | |
ae50a770 PX |
76 | |
77 | GICCapabilityList *qmp_query_gic_capabilities(Error **errp) | |
78 | { | |
db31e49a PX |
79 | GICCapabilityList *head = NULL; |
80 | GICCapability *v2 = gic_cap_new(2), *v3 = gic_cap_new(3); | |
81 | ||
82 | v2->emulated = true; | |
3b1a2225 | 83 | v3->emulated = true; |
db31e49a PX |
84 | |
85 | gic_cap_kvm_probe(v2, v3); | |
86 | ||
87 | head = gic_cap_list_add(head, v2); | |
88 | head = gic_cap_list_add(head, v3); | |
89 | ||
90 | return head; | |
ae50a770 | 91 | } |
e19afd56 | 92 | |
0df9142d AJ |
93 | QEMU_BUILD_BUG_ON(ARM_MAX_VQ > 16); |
94 | ||
e19afd56 AJ |
95 | /* |
96 | * These are cpu model features we want to advertise. The order here | |
97 | * matters as this is the order in which qmp_query_cpu_model_expansion | |
98 | * will attempt to set them. If there are dependencies between features, | |
99 | * then the order that considers those dependencies must be used. | |
100 | */ | |
101 | static const char *cpu_model_advertised_features[] = { | |
73234775 | 102 | "aarch64", "pmu", "sve", |
0df9142d AJ |
103 | "sve128", "sve256", "sve384", "sve512", |
104 | "sve640", "sve768", "sve896", "sve1024", "sve1152", "sve1280", | |
105 | "sve1408", "sve1536", "sve1664", "sve1792", "sve1920", "sve2048", | |
dea101a1 | 106 | "kvm-no-adjvtime", |
e19afd56 AJ |
107 | NULL |
108 | }; | |
109 | ||
110 | CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, | |
111 | CpuModelInfo *model, | |
112 | Error **errp) | |
113 | { | |
114 | CpuModelExpansionInfo *expansion_info; | |
115 | const QDict *qdict_in = NULL; | |
116 | QDict *qdict_out; | |
117 | ObjectClass *oc; | |
118 | Object *obj; | |
119 | const char *name; | |
120 | int i; | |
121 | ||
122 | if (type != CPU_MODEL_EXPANSION_TYPE_FULL) { | |
123 | error_setg(errp, "The requested expansion type is not supported"); | |
124 | return NULL; | |
125 | } | |
126 | ||
127 | if (!kvm_enabled() && !strcmp(model->name, "host")) { | |
128 | error_setg(errp, "The CPU type '%s' requires KVM", model->name); | |
129 | return NULL; | |
130 | } | |
131 | ||
132 | oc = cpu_class_by_name(TYPE_ARM_CPU, model->name); | |
133 | if (!oc) { | |
134 | error_setg(errp, "The CPU type '%s' is not a recognized ARM CPU type", | |
135 | model->name); | |
136 | return NULL; | |
137 | } | |
138 | ||
139 | if (kvm_enabled()) { | |
e19afd56 AJ |
140 | bool supported = false; |
141 | ||
142 | if (!strcmp(model->name, "host") || !strcmp(model->name, "max")) { | |
143 | /* These are kvmarm's recommended cpu types */ | |
144 | supported = true; | |
0999a4ba LY |
145 | } else if (current_machine->cpu_type) { |
146 | const char *cpu_type = current_machine->cpu_type; | |
147 | int len = strlen(cpu_type) - strlen(ARM_CPU_TYPE_SUFFIX); | |
148 | ||
149 | if (strlen(model->name) == len && | |
150 | !strncmp(model->name, cpu_type, len)) { | |
151 | /* KVM is enabled and we're using this type, so it works. */ | |
152 | supported = true; | |
153 | } | |
e19afd56 AJ |
154 | } |
155 | if (!supported) { | |
156 | error_setg(errp, "We cannot guarantee the CPU type '%s' works " | |
157 | "with KVM on this host", model->name); | |
158 | return NULL; | |
159 | } | |
160 | } | |
161 | ||
162 | if (model->props) { | |
163 | qdict_in = qobject_to(QDict, model->props); | |
164 | if (!qdict_in) { | |
165 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "props", "dict"); | |
166 | return NULL; | |
167 | } | |
168 | } | |
169 | ||
170 | obj = object_new(object_class_get_name(oc)); | |
171 | ||
172 | if (qdict_in) { | |
173 | Visitor *visitor; | |
174 | Error *err = NULL; | |
175 | ||
176 | visitor = qobject_input_visitor_new(model->props); | |
177 | visit_start_struct(visitor, NULL, NULL, 0, &err); | |
178 | if (err) { | |
179 | visit_free(visitor); | |
180 | object_unref(obj); | |
181 | error_propagate(errp, err); | |
182 | return NULL; | |
183 | } | |
184 | ||
185 | i = 0; | |
186 | while ((name = cpu_model_advertised_features[i++]) != NULL) { | |
187 | if (qdict_get(qdict_in, name)) { | |
188 | object_property_set(obj, visitor, name, &err); | |
189 | if (err) { | |
190 | break; | |
191 | } | |
192 | } | |
193 | } | |
194 | ||
195 | if (!err) { | |
196 | visit_check_struct(visitor, &err); | |
197 | } | |
0df9142d AJ |
198 | if (!err) { |
199 | arm_cpu_finalize_features(ARM_CPU(obj), &err); | |
200 | } | |
e19afd56 AJ |
201 | visit_end_struct(visitor, NULL); |
202 | visit_free(visitor); | |
203 | if (err) { | |
204 | object_unref(obj); | |
205 | error_propagate(errp, err); | |
206 | return NULL; | |
207 | } | |
0df9142d | 208 | } else { |
20ac582d | 209 | arm_cpu_finalize_features(ARM_CPU(obj), &error_abort); |
e19afd56 AJ |
210 | } |
211 | ||
212 | expansion_info = g_new0(CpuModelExpansionInfo, 1); | |
213 | expansion_info->model = g_malloc0(sizeof(*expansion_info->model)); | |
214 | expansion_info->model->name = g_strdup(model->name); | |
215 | ||
216 | qdict_out = qdict_new(); | |
217 | ||
218 | i = 0; | |
219 | while ((name = cpu_model_advertised_features[i++]) != NULL) { | |
220 | ObjectProperty *prop = object_property_find(obj, name, NULL); | |
221 | if (prop) { | |
e19afd56 AJ |
222 | QObject *value; |
223 | ||
224 | assert(prop->get); | |
20ac582d | 225 | value = object_property_get_qobject(obj, name, &error_abort); |
e19afd56 AJ |
226 | |
227 | qdict_put_obj(qdict_out, name, value); | |
228 | } | |
229 | } | |
230 | ||
231 | if (!qdict_size(qdict_out)) { | |
232 | qobject_unref(qdict_out); | |
233 | } else { | |
234 | expansion_info->model->props = QOBJECT(qdict_out); | |
235 | expansion_info->model->has_props = true; | |
236 | } | |
237 | ||
238 | object_unref(obj); | |
239 | ||
240 | return expansion_info; | |
241 | } |