]>
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 AJ |
92 | |
93 | /* | |
94 | * These are cpu model features we want to advertise. The order here | |
95 | * matters as this is the order in which qmp_query_cpu_model_expansion | |
96 | * will attempt to set them. If there are dependencies between features, | |
97 | * then the order that considers those dependencies must be used. | |
98 | */ | |
99 | static const char *cpu_model_advertised_features[] = { | |
73234775 | 100 | "aarch64", "pmu", "sve", |
e19afd56 AJ |
101 | NULL |
102 | }; | |
103 | ||
104 | CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, | |
105 | CpuModelInfo *model, | |
106 | Error **errp) | |
107 | { | |
108 | CpuModelExpansionInfo *expansion_info; | |
109 | const QDict *qdict_in = NULL; | |
110 | QDict *qdict_out; | |
111 | ObjectClass *oc; | |
112 | Object *obj; | |
113 | const char *name; | |
114 | int i; | |
115 | ||
116 | if (type != CPU_MODEL_EXPANSION_TYPE_FULL) { | |
117 | error_setg(errp, "The requested expansion type is not supported"); | |
118 | return NULL; | |
119 | } | |
120 | ||
121 | if (!kvm_enabled() && !strcmp(model->name, "host")) { | |
122 | error_setg(errp, "The CPU type '%s' requires KVM", model->name); | |
123 | return NULL; | |
124 | } | |
125 | ||
126 | oc = cpu_class_by_name(TYPE_ARM_CPU, model->name); | |
127 | if (!oc) { | |
128 | error_setg(errp, "The CPU type '%s' is not a recognized ARM CPU type", | |
129 | model->name); | |
130 | return NULL; | |
131 | } | |
132 | ||
133 | if (kvm_enabled()) { | |
134 | const char *cpu_type = current_machine->cpu_type; | |
135 | int len = strlen(cpu_type) - strlen(ARM_CPU_TYPE_SUFFIX); | |
136 | bool supported = false; | |
137 | ||
138 | if (!strcmp(model->name, "host") || !strcmp(model->name, "max")) { | |
139 | /* These are kvmarm's recommended cpu types */ | |
140 | supported = true; | |
141 | } else 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 | } | |
146 | if (!supported) { | |
147 | error_setg(errp, "We cannot guarantee the CPU type '%s' works " | |
148 | "with KVM on this host", model->name); | |
149 | return NULL; | |
150 | } | |
151 | } | |
152 | ||
153 | if (model->props) { | |
154 | qdict_in = qobject_to(QDict, model->props); | |
155 | if (!qdict_in) { | |
156 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "props", "dict"); | |
157 | return NULL; | |
158 | } | |
159 | } | |
160 | ||
161 | obj = object_new(object_class_get_name(oc)); | |
162 | ||
163 | if (qdict_in) { | |
164 | Visitor *visitor; | |
165 | Error *err = NULL; | |
166 | ||
167 | visitor = qobject_input_visitor_new(model->props); | |
168 | visit_start_struct(visitor, NULL, NULL, 0, &err); | |
169 | if (err) { | |
170 | visit_free(visitor); | |
171 | object_unref(obj); | |
172 | error_propagate(errp, err); | |
173 | return NULL; | |
174 | } | |
175 | ||
176 | i = 0; | |
177 | while ((name = cpu_model_advertised_features[i++]) != NULL) { | |
178 | if (qdict_get(qdict_in, name)) { | |
179 | object_property_set(obj, visitor, name, &err); | |
180 | if (err) { | |
181 | break; | |
182 | } | |
183 | } | |
184 | } | |
185 | ||
186 | if (!err) { | |
187 | visit_check_struct(visitor, &err); | |
188 | } | |
189 | visit_end_struct(visitor, NULL); | |
190 | visit_free(visitor); | |
191 | if (err) { | |
192 | object_unref(obj); | |
193 | error_propagate(errp, err); | |
194 | return NULL; | |
195 | } | |
196 | } | |
197 | ||
198 | expansion_info = g_new0(CpuModelExpansionInfo, 1); | |
199 | expansion_info->model = g_malloc0(sizeof(*expansion_info->model)); | |
200 | expansion_info->model->name = g_strdup(model->name); | |
201 | ||
202 | qdict_out = qdict_new(); | |
203 | ||
204 | i = 0; | |
205 | while ((name = cpu_model_advertised_features[i++]) != NULL) { | |
206 | ObjectProperty *prop = object_property_find(obj, name, NULL); | |
207 | if (prop) { | |
208 | Error *err = NULL; | |
209 | QObject *value; | |
210 | ||
211 | assert(prop->get); | |
212 | value = object_property_get_qobject(obj, name, &err); | |
213 | assert(!err); | |
214 | ||
215 | qdict_put_obj(qdict_out, name, value); | |
216 | } | |
217 | } | |
218 | ||
219 | if (!qdict_size(qdict_out)) { | |
220 | qobject_unref(qdict_out); | |
221 | } else { | |
222 | expansion_info->model->props = QOBJECT(qdict_out); | |
223 | expansion_info->model->has_props = true; | |
224 | } | |
225 | ||
226 | object_unref(obj); | |
227 | ||
228 | return expansion_info; | |
229 | } |