1// SPDX-License-Identifier: GPL-2.0
2/*
3 * A test for GUEST_PRINTF
4 *
5 * Copyright 2022, Google, Inc. and/or its affiliates.
6 */
7#include <fcntl.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <sys/ioctl.h>
12
13#include "test_util.h"
14#include "kvm_util.h"
15#include "processor.h"
16
17struct guest_vals {
18 uint64_t a;
19 uint64_t b;
20 uint64_t type;
21};
22
23static struct guest_vals vals;
24
25/* GUEST_PRINTF()/GUEST_ASSERT_FMT() does not support float or double. */
26#define TYPE_LIST \
27TYPE(test_type_i64, I64, "%ld", int64_t) \
28TYPE(test_type_u64, U64u, "%lu", uint64_t) \
29TYPE(test_type_x64, U64x, "0x%lx", uint64_t) \
30TYPE(test_type_X64, U64X, "0x%lX", uint64_t) \
31TYPE(test_type_u32, U32u, "%u", uint32_t) \
32TYPE(test_type_x32, U32x, "0x%x", uint32_t) \
33TYPE(test_type_X32, U32X, "0x%X", uint32_t) \
34TYPE(test_type_int, INT, "%d", int) \
35TYPE(test_type_char, CHAR, "%c", char) \
36TYPE(test_type_str, STR, "'%s'", const char *) \
37TYPE(test_type_ptr, PTR, "%p", uintptr_t)
38
39enum args_type {
40#define TYPE(fn, ext, fmt_t, T) TYPE_##ext,
41 TYPE_LIST
42#undef TYPE
43};
44
45static void run_test(struct kvm_vcpu *vcpu, const char *expected_printf,
46 const char *expected_assert);
47
48#define BUILD_TYPE_STRINGS_AND_HELPER(fn, ext, fmt_t, T) \
49const char *PRINTF_FMT_##ext = "Got params a = " fmt_t " and b = " fmt_t; \
50const char *ASSERT_FMT_##ext = "Expected " fmt_t ", got " fmt_t " instead"; \
51static void fn(struct kvm_vcpu *vcpu, T a, T b) \
52{ \
53 char expected_printf[UCALL_BUFFER_LEN]; \
54 char expected_assert[UCALL_BUFFER_LEN]; \
55 \
56 snprintf(expected_printf, UCALL_BUFFER_LEN, PRINTF_FMT_##ext, a, b); \
57 snprintf(expected_assert, UCALL_BUFFER_LEN, ASSERT_FMT_##ext, a, b); \
58 vals = (struct guest_vals){ (uint64_t)a, (uint64_t)b, TYPE_##ext }; \
59 sync_global_to_guest(vcpu->vm, vals); \
60 run_test(vcpu, expected_printf, expected_assert); \
61}
62
63#define TYPE(fn, ext, fmt_t, T) \
64 BUILD_TYPE_STRINGS_AND_HELPER(fn, ext, fmt_t, T)
65 TYPE_LIST
66#undef TYPE
67
68static void guest_code(void)
69{
70 while (1) {
71 switch (vals.type) {
72#define TYPE(fn, ext, fmt_t, T) \
73 case TYPE_##ext: \
74 GUEST_PRINTF(PRINTF_FMT_##ext, vals.a, vals.b); \
75 __GUEST_ASSERT(vals.a == vals.b, \
76 ASSERT_FMT_##ext, vals.a, vals.b); \
77 break;
78 TYPE_LIST
79#undef TYPE
80 default:
81 GUEST_SYNC(vals.type);
82 }
83
84 GUEST_DONE();
85 }
86}
87
88/*
89 * Unfortunately this gets a little messy because 'assert_msg' doesn't
90 * just contains the matching string, it also contains additional assert
91 * info. Fortunately the part that matches should be at the very end of
92 * 'assert_msg'.
93 */
94static void ucall_abort(const char *assert_msg, const char *expected_assert_msg)
95{
96 int len_str = strlen(assert_msg);
97 int len_substr = strlen(expected_assert_msg);
98 int offset = len_str - len_substr;
99
100 TEST_ASSERT(len_substr <= len_str,
101 "Expected '%s' to be a substring of '%s'",
102 assert_msg, expected_assert_msg);
103
104 TEST_ASSERT(strcmp(&assert_msg[offset], expected_assert_msg) == 0,
105 "Unexpected mismatch. Expected: '%s', got: '%s'",
106 expected_assert_msg, &assert_msg[offset]);
107}
108
109static void run_test(struct kvm_vcpu *vcpu, const char *expected_printf,
110 const char *expected_assert)
111{
112 struct kvm_run *run = vcpu->run;
113 struct ucall uc;
114
115 while (1) {
116 vcpu_run(vcpu);
117
118 TEST_ASSERT(run->exit_reason == UCALL_EXIT_REASON,
119 "Unexpected exit reason: %u (%s),",
120 run->exit_reason, exit_reason_str(run->exit_reason));
121
122 switch (get_ucall(vcpu, &uc)) {
123 case UCALL_SYNC:
124 TEST_FAIL("Unknown 'args_type' = %lu", uc.args[1]);
125 break;
126 case UCALL_PRINTF:
127 TEST_ASSERT(strcmp(uc.buffer, expected_printf) == 0,
128 "Unexpected mismatch. Expected: '%s', got: '%s'",
129 expected_printf, uc.buffer);
130 break;
131 case UCALL_ABORT:
132 ucall_abort(assert_msg: uc.buffer, expected_assert_msg: expected_assert);
133 break;
134 case UCALL_DONE:
135 return;
136 default:
137 TEST_FAIL("Unknown ucall %lu", uc.cmd);
138 }
139 }
140}
141
142static void guest_code_limits(void)
143{
144 char test_str[UCALL_BUFFER_LEN + 10];
145
146 memset(test_str, 'a', sizeof(test_str));
147 test_str[sizeof(test_str) - 1] = 0;
148
149 GUEST_PRINTF("%s", test_str);
150}
151
152static void test_limits(void)
153{
154 struct kvm_vcpu *vcpu;
155 struct kvm_run *run;
156 struct kvm_vm *vm;
157 struct ucall uc;
158
159 vm = vm_create_with_one_vcpu(&vcpu, guest_code_limits);
160 run = vcpu->run;
161 vcpu_run(vcpu);
162
163 TEST_ASSERT(run->exit_reason == UCALL_EXIT_REASON,
164 "Unexpected exit reason: %u (%s),",
165 run->exit_reason, exit_reason_str(run->exit_reason));
166
167 TEST_ASSERT(get_ucall(vcpu, &uc) == UCALL_ABORT,
168 "Unexpected ucall command: %lu, Expected: %u (UCALL_ABORT)",
169 uc.cmd, UCALL_ABORT);
170
171 kvm_vm_free(vm);
172}
173
174int main(int argc, char *argv[])
175{
176 struct kvm_vcpu *vcpu;
177 struct kvm_vm *vm;
178
179 vm = vm_create_with_one_vcpu(&vcpu, guest_code);
180
181 test_type_i64(vcpu, -1, -1);
182 test_type_i64(vcpu, -1, 1);
183 test_type_i64(vcpu, 0x1234567890abcdef, 0x1234567890abcdef);
184 test_type_i64(vcpu, 0x1234567890abcdef, 0x1234567890abcdee);
185
186 test_type_u64(vcpu, 0x1234567890abcdef, 0x1234567890abcdef);
187 test_type_u64(vcpu, 0x1234567890abcdef, 0x1234567890abcdee);
188 test_type_x64(vcpu, 0x1234567890abcdef, 0x1234567890abcdef);
189 test_type_x64(vcpu, 0x1234567890abcdef, 0x1234567890abcdee);
190 test_type_X64(vcpu, 0x1234567890abcdef, 0x1234567890abcdef);
191 test_type_X64(vcpu, 0x1234567890abcdef, 0x1234567890abcdee);
192
193 test_type_u32(vcpu, 0x90abcdef, 0x90abcdef);
194 test_type_u32(vcpu, 0x90abcdef, 0x90abcdee);
195 test_type_x32(vcpu, 0x90abcdef, 0x90abcdef);
196 test_type_x32(vcpu, 0x90abcdef, 0x90abcdee);
197 test_type_X32(vcpu, 0x90abcdef, 0x90abcdef);
198 test_type_X32(vcpu, 0x90abcdef, 0x90abcdee);
199
200 test_type_int(vcpu, a: -1, b: -1);
201 test_type_int(vcpu, a: -1, b: 1);
202 test_type_int(vcpu, a: 1, b: 1);
203
204 test_type_char(vcpu, a: 'a', b: 'a');
205 test_type_char(vcpu, a: 'a', b: 'A');
206 test_type_char(vcpu, a: 'a', b: 'b');
207
208 test_type_str(vcpu, a: "foo", b: "foo");
209 test_type_str(vcpu, a: "foo", b: "bar");
210
211 test_type_ptr(vcpu, 0x1234567890abcdef, 0x1234567890abcdef);
212 test_type_ptr(vcpu, 0x1234567890abcdef, 0x1234567890abcdee);
213
214 kvm_vm_free(vm);
215
216 test_limits();
217
218 return 0;
219}
220

source code of linux/tools/testing/selftests/kvm/guest_print_test.c