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 | |
17 | struct guest_vals { |
18 | uint64_t a; |
19 | uint64_t b; |
20 | uint64_t type; |
21 | }; |
22 | |
23 | static struct guest_vals vals; |
24 | |
25 | /* GUEST_PRINTF()/GUEST_ASSERT_FMT() does not support float or double. */ |
26 | #define TYPE_LIST \ |
27 | TYPE(test_type_i64, I64, "%ld", int64_t) \ |
28 | TYPE(test_type_u64, U64u, "%lu", uint64_t) \ |
29 | TYPE(test_type_x64, U64x, "0x%lx", uint64_t) \ |
30 | TYPE(test_type_X64, U64X, "0x%lX", uint64_t) \ |
31 | TYPE(test_type_u32, U32u, "%u", uint32_t) \ |
32 | TYPE(test_type_x32, U32x, "0x%x", uint32_t) \ |
33 | TYPE(test_type_X32, U32X, "0x%X", uint32_t) \ |
34 | TYPE(test_type_int, INT, "%d", int) \ |
35 | TYPE(test_type_char, CHAR, "%c", char) \ |
36 | TYPE(test_type_str, STR, "'%s'", const char *) \ |
37 | TYPE(test_type_ptr, PTR, "%p", uintptr_t) |
38 | |
39 | enum args_type { |
40 | #define TYPE(fn, ext, fmt_t, T) TYPE_##ext, |
41 | TYPE_LIST |
42 | #undef TYPE |
43 | }; |
44 | |
45 | static 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) \ |
49 | const char *PRINTF_FMT_##ext = "Got params a = " fmt_t " and b = " fmt_t; \ |
50 | const char *ASSERT_FMT_##ext = "Expected " fmt_t ", got " fmt_t " instead"; \ |
51 | static 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 | |
68 | static 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 | */ |
94 | static 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 | |
109 | static 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 | |
142 | static 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 | |
152 | static 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 | |
174 | int 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 | |