blob: 9afad17114a3097902bf28b5527bf7766a1bb5a9 [file] [log] [blame]
Andrew Jonesbd31b752019-10-31 15:27:27 +01001/*
2 * Arm CPU feature test cases
3 *
4 * Copyright (c) 2019 Red Hat Inc.
5 * Authors:
6 * Andrew Jones <drjones@redhat.com>
7 *
8 * This work is licensed under the terms of the GNU GPL, version 2 or later.
9 * See the COPYING file in the top-level directory.
10 */
11#include "qemu/osdep.h"
12#include "libqtest.h"
13#include "qapi/qmp/qdict.h"
14#include "qapi/qmp/qjson.h"
15
16#define MACHINE "-machine virt,gic-version=max,accel=tcg "
17#define MACHINE_KVM "-machine virt,gic-version=max,accel=kvm:tcg "
18#define QUERY_HEAD "{ 'execute': 'query-cpu-model-expansion', " \
19 " 'arguments': { 'type': 'full', "
20#define QUERY_TAIL "}}"
21
22static bool kvm_enabled(QTestState *qts)
23{
24 QDict *resp, *qdict;
25 bool enabled;
26
27 resp = qtest_qmp(qts, "{ 'execute': 'query-kvm' }");
28 g_assert(qdict_haskey(resp, "return"));
29 qdict = qdict_get_qdict(resp, "return");
30 g_assert(qdict_haskey(qdict, "enabled"));
31 enabled = qdict_get_bool(qdict, "enabled");
32 qobject_unref(resp);
33
34 return enabled;
35}
36
37static QDict *do_query_no_props(QTestState *qts, const char *cpu_type)
38{
39 return qtest_qmp(qts, QUERY_HEAD "'model': { 'name': %s }"
40 QUERY_TAIL, cpu_type);
41}
42
43static QDict *do_query(QTestState *qts, const char *cpu_type,
44 const char *fmt, ...)
45{
46 QDict *resp;
47
48 if (fmt) {
49 QDict *args;
50 va_list ap;
51
52 va_start(ap, fmt);
53 args = qdict_from_vjsonf_nofail(fmt, ap);
54 va_end(ap);
55
56 resp = qtest_qmp(qts, QUERY_HEAD "'model': { 'name': %s, "
57 "'props': %p }"
58 QUERY_TAIL, cpu_type, args);
59 } else {
60 resp = do_query_no_props(qts, cpu_type);
61 }
62
63 return resp;
64}
65
66static const char *resp_get_error(QDict *resp)
67{
68 QDict *qdict;
69
70 g_assert(resp);
71
72 qdict = qdict_get_qdict(resp, "error");
73 if (qdict) {
74 return qdict_get_str(qdict, "desc");
75 }
76
77 return NULL;
78}
79
80#define assert_error(qts, cpu_type, expected_error, fmt, ...) \
81({ \
82 QDict *_resp; \
83 const char *_error; \
84 \
85 _resp = do_query(qts, cpu_type, fmt, ##__VA_ARGS__); \
86 g_assert(_resp); \
87 _error = resp_get_error(_resp); \
88 g_assert(_error); \
89 g_assert(g_str_equal(_error, expected_error)); \
90 qobject_unref(_resp); \
91})
92
93static bool resp_has_props(QDict *resp)
94{
95 QDict *qdict;
96
97 g_assert(resp);
98
99 if (!qdict_haskey(resp, "return")) {
100 return false;
101 }
102 qdict = qdict_get_qdict(resp, "return");
103
104 if (!qdict_haskey(qdict, "model")) {
105 return false;
106 }
107 qdict = qdict_get_qdict(qdict, "model");
108
109 return qdict_haskey(qdict, "props");
110}
111
112static QDict *resp_get_props(QDict *resp)
113{
114 QDict *qdict;
115
116 g_assert(resp);
117 g_assert(resp_has_props(resp));
118
119 qdict = qdict_get_qdict(resp, "return");
120 qdict = qdict_get_qdict(qdict, "model");
121 qdict = qdict_get_qdict(qdict, "props");
122
123 return qdict;
124}
125
126#define assert_has_feature(qts, cpu_type, feature) \
127({ \
128 QDict *_resp = do_query_no_props(qts, cpu_type); \
129 g_assert(_resp); \
130 g_assert(resp_has_props(_resp)); \
131 g_assert(qdict_get(resp_get_props(_resp), feature)); \
132 qobject_unref(_resp); \
133})
134
135#define assert_has_not_feature(qts, cpu_type, feature) \
136({ \
137 QDict *_resp = do_query_no_props(qts, cpu_type); \
138 g_assert(_resp); \
139 g_assert(!resp_has_props(_resp) || \
140 !qdict_get(resp_get_props(_resp), feature)); \
141 qobject_unref(_resp); \
142})
143
144static void assert_type_full(QTestState *qts)
145{
146 const char *error;
147 QDict *resp;
148
149 resp = qtest_qmp(qts, "{ 'execute': 'query-cpu-model-expansion', "
150 "'arguments': { 'type': 'static', "
151 "'model': { 'name': 'foo' }}}");
152 g_assert(resp);
153 error = resp_get_error(resp);
154 g_assert(error);
155 g_assert(g_str_equal(error,
156 "The requested expansion type is not supported"));
157 qobject_unref(resp);
158}
159
160static void assert_bad_props(QTestState *qts, const char *cpu_type)
161{
162 const char *error;
163 QDict *resp;
164
165 resp = qtest_qmp(qts, "{ 'execute': 'query-cpu-model-expansion', "
166 "'arguments': { 'type': 'full', "
167 "'model': { 'name': %s, "
168 "'props': false }}}",
169 cpu_type);
170 g_assert(resp);
171 error = resp_get_error(resp);
172 g_assert(error);
173 g_assert(g_str_equal(error,
174 "Invalid parameter type for 'props', expected: dict"));
175 qobject_unref(resp);
176}
177
178static void test_query_cpu_model_expansion(const void *data)
179{
180 QTestState *qts;
181
182 qts = qtest_init(MACHINE "-cpu max");
183
184 /* Test common query-cpu-model-expansion input validation */
185 assert_type_full(qts);
186 assert_bad_props(qts, "max");
187 assert_error(qts, "foo", "The CPU type 'foo' is not a recognized "
188 "ARM CPU type", NULL);
189 assert_error(qts, "max", "Parameter 'not-a-prop' is unexpected",
190 "{ 'not-a-prop': false }");
191 assert_error(qts, "host", "The CPU type 'host' requires KVM", NULL);
192
193 /* Test expected feature presence/absence for some cpu types */
194 assert_has_feature(qts, "max", "pmu");
195 assert_has_feature(qts, "cortex-a15", "pmu");
196 assert_has_not_feature(qts, "cortex-a15", "aarch64");
197
198 if (g_str_equal(qtest_get_arch(), "aarch64")) {
199 assert_has_feature(qts, "max", "aarch64");
200 assert_has_feature(qts, "cortex-a57", "pmu");
201 assert_has_feature(qts, "cortex-a57", "aarch64");
202
203 /* Test that features that depend on KVM generate errors without. */
204 assert_error(qts, "max",
205 "'aarch64' feature cannot be disabled "
206 "unless KVM is enabled and 32-bit EL1 "
207 "is supported",
208 "{ 'aarch64': false }");
209 }
210
211 qtest_quit(qts);
212}
213
214static void test_query_cpu_model_expansion_kvm(const void *data)
215{
216 QTestState *qts;
217
218 qts = qtest_init(MACHINE_KVM "-cpu max");
219
220 /*
221 * These tests target the 'host' CPU type, so KVM must be enabled.
222 */
223 if (!kvm_enabled(qts)) {
224 qtest_quit(qts);
225 return;
226 }
227
228 if (g_str_equal(qtest_get_arch(), "aarch64")) {
229 assert_has_feature(qts, "host", "aarch64");
230 assert_has_feature(qts, "host", "pmu");
231
232 assert_error(qts, "cortex-a15",
233 "We cannot guarantee the CPU type 'cortex-a15' works "
234 "with KVM on this host", NULL);
235 } else {
236 assert_has_not_feature(qts, "host", "aarch64");
237 assert_has_not_feature(qts, "host", "pmu");
238 }
239
240 qtest_quit(qts);
241}
242
243int main(int argc, char **argv)
244{
245 g_test_init(&argc, &argv, NULL);
246
247 qtest_add_data_func("/arm/query-cpu-model-expansion",
248 NULL, test_query_cpu_model_expansion);
249
250 /*
251 * For now we only run KVM specific tests with AArch64 QEMU in
252 * order avoid attempting to run an AArch32 QEMU with KVM on
253 * AArch64 hosts. That won't work and isn't easy to detect.
254 */
255 if (g_str_equal(qtest_get_arch(), "aarch64")) {
256 qtest_add_data_func("/arm/kvm/query-cpu-model-expansion",
257 NULL, test_query_cpu_model_expansion_kvm);
258 }
259
260 return g_test_run();
261}