~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/kvm/get-reg-list.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0
  2 /*
  3  * Check for KVM_GET_REG_LIST regressions.
  4  *
  5  * Copyright (C) 2020, Red Hat, Inc.
  6  *
  7  * When attempting to migrate from a host with an older kernel to a host
  8  * with a newer kernel we allow the newer kernel on the destination to
  9  * list new registers with get-reg-list. We assume they'll be unused, at
 10  * least until the guest reboots, and so they're relatively harmless.
 11  * However, if the destination host with the newer kernel is missing
 12  * registers which the source host with the older kernel has, then that's
 13  * a regression in get-reg-list. This test checks for that regression by
 14  * checking the current list against a blessed list. We should never have
 15  * missing registers, but if new ones appear then they can probably be
 16  * added to the blessed list. A completely new blessed list can be created
 17  * by running the test with the --list command line argument.
 18  *
 19  * The blessed list should be created from the oldest possible kernel.
 20  */
 21 #include <stdio.h>
 22 #include <stdlib.h>
 23 #include <string.h>
 24 #include <unistd.h>
 25 #include <sys/types.h>
 26 #include <sys/wait.h>
 27 #include "kvm_util.h"
 28 #include "test_util.h"
 29 #include "processor.h"
 30 
 31 static struct kvm_reg_list *reg_list;
 32 static __u64 *blessed_reg, blessed_n;
 33 
 34 extern struct vcpu_reg_list *vcpu_configs[];
 35 extern int vcpu_configs_n;
 36 
 37 #define for_each_reg(i)                                                         \
 38         for ((i) = 0; (i) < reg_list->n; ++(i))
 39 
 40 #define for_each_reg_filtered(i)                                                \
 41         for_each_reg(i)                                                         \
 42                 if (!filter_reg(reg_list->reg[i]))
 43 
 44 #define for_each_missing_reg(i)                                                 \
 45         for ((i) = 0; (i) < blessed_n; ++(i))                                   \
 46                 if (!find_reg(reg_list->reg, reg_list->n, blessed_reg[i]))      \
 47                         if (check_supported_reg(vcpu, blessed_reg[i]))
 48 
 49 #define for_each_new_reg(i)                                                     \
 50         for_each_reg_filtered(i)                                                \
 51                 if (!find_reg(blessed_reg, blessed_n, reg_list->reg[i]))
 52 
 53 #define for_each_present_blessed_reg(i)                                         \
 54         for_each_reg(i)                                                         \
 55                 if (find_reg(blessed_reg, blessed_n, reg_list->reg[i]))
 56 
 57 static const char *config_name(struct vcpu_reg_list *c)
 58 {
 59         struct vcpu_reg_sublist *s;
 60         int len = 0;
 61 
 62         if (c->name)
 63                 return c->name;
 64 
 65         for_each_sublist(c, s)
 66                 len += strlen(s->name) + 1;
 67 
 68         c->name = malloc(len);
 69 
 70         len = 0;
 71         for_each_sublist(c, s) {
 72                 if (!strcmp(s->name, "base"))
 73                         continue;
 74                 if (len)
 75                         c->name[len++] = '+';
 76                 strcpy(c->name + len, s->name);
 77                 len += strlen(s->name);
 78         }
 79         c->name[len] = '\0';
 80 
 81         return c->name;
 82 }
 83 
 84 bool __weak check_supported_reg(struct kvm_vcpu *vcpu, __u64 reg)
 85 {
 86         return true;
 87 }
 88 
 89 bool __weak filter_reg(__u64 reg)
 90 {
 91         return false;
 92 }
 93 
 94 static bool find_reg(__u64 regs[], __u64 nr_regs, __u64 reg)
 95 {
 96         int i;
 97 
 98         for (i = 0; i < nr_regs; ++i)
 99                 if (reg == regs[i])
100                         return true;
101         return false;
102 }
103 
104 void __weak print_reg(const char *prefix, __u64 id)
105 {
106         printf("\t0x%llx,\n", id);
107 }
108 
109 bool __weak check_reject_set(int err)
110 {
111         return true;
112 }
113 
114 void __weak finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_reg_list *c)
115 {
116 }
117 
118 #ifdef __aarch64__
119 static void prepare_vcpu_init(struct vcpu_reg_list *c, struct kvm_vcpu_init *init)
120 {
121         struct vcpu_reg_sublist *s;
122 
123         for_each_sublist(c, s)
124                 if (s->capability)
125                         init->features[s->feature / 32] |= 1 << (s->feature % 32);
126 }
127 
128 static struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm)
129 {
130         struct kvm_vcpu_init init = { .target = -1, };
131         struct kvm_vcpu *vcpu;
132 
133         prepare_vcpu_init(c, &init);
134         vcpu = __vm_vcpu_add(vm, 0);
135         aarch64_vcpu_setup(vcpu, &init);
136 
137         return vcpu;
138 }
139 #else
140 static struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm)
141 {
142         return __vm_vcpu_add(vm, 0);
143 }
144 #endif
145 
146 static void check_supported(struct vcpu_reg_list *c)
147 {
148         struct vcpu_reg_sublist *s;
149 
150         for_each_sublist(c, s) {
151                 if (!s->capability)
152                         continue;
153 
154                 __TEST_REQUIRE(kvm_has_cap(s->capability),
155                                "%s: %s not available, skipping tests",
156                                config_name(c), s->name);
157         }
158 }
159 
160 static bool print_list;
161 static bool print_filtered;
162 
163 static void run_test(struct vcpu_reg_list *c)
164 {
165         int new_regs = 0, missing_regs = 0, i, n;
166         int failed_get = 0, failed_set = 0, failed_reject = 0;
167         int skipped_set = 0;
168         struct kvm_vcpu *vcpu;
169         struct kvm_vm *vm;
170         struct vcpu_reg_sublist *s;
171 
172         check_supported(c);
173 
174         vm = vm_create_barebones();
175         vcpu = vcpu_config_get_vcpu(c, vm);
176         finalize_vcpu(vcpu, c);
177 
178         reg_list = vcpu_get_reg_list(vcpu);
179 
180         if (print_list || print_filtered) {
181                 putchar('\n');
182                 for_each_reg(i) {
183                         __u64 id = reg_list->reg[i];
184                         if ((print_list && !filter_reg(id)) ||
185                             (print_filtered && filter_reg(id)))
186                                 print_reg(config_name(c), id);
187                 }
188                 putchar('\n');
189                 return;
190         }
191 
192         for_each_sublist(c, s)
193                 blessed_n += s->regs_n;
194         blessed_reg = calloc(blessed_n, sizeof(__u64));
195 
196         n = 0;
197         for_each_sublist(c, s) {
198                 for (i = 0; i < s->regs_n; ++i)
199                         blessed_reg[n++] = s->regs[i];
200         }
201 
202         /*
203          * We only test that we can get the register and then write back the
204          * same value. Some registers may allow other values to be written
205          * back, but others only allow some bits to be changed, and at least
206          * for ID registers set will fail if the value does not exactly match
207          * what was returned by get. If registers that allow other values to
208          * be written need to have the other values tested, then we should
209          * create a new set of tests for those in a new independent test
210          * executable.
211          *
212          * Only do the get/set tests on present, blessed list registers,
213          * since we don't know the capabilities of any new registers.
214          */
215         for_each_present_blessed_reg(i) {
216                 uint8_t addr[2048 / 8];
217                 struct kvm_one_reg reg = {
218                         .id = reg_list->reg[i],
219                         .addr = (__u64)&addr,
220                 };
221                 bool reject_reg = false, skip_reg = false;
222                 int ret;
223 
224                 ret = __vcpu_get_reg(vcpu, reg_list->reg[i], &addr);
225                 if (ret) {
226                         printf("%s: Failed to get ", config_name(c));
227                         print_reg(config_name(c), reg.id);
228                         putchar('\n');
229                         ++failed_get;
230                 }
231 
232                 for_each_sublist(c, s) {
233                         /* rejects_set registers are rejected for set operation */
234                         if (s->rejects_set && find_reg(s->rejects_set, s->rejects_set_n, reg.id)) {
235                                 reject_reg = true;
236                                 ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, &reg);
237                                 if (ret != -1 || !check_reject_set(errno)) {
238                                         printf("%s: Failed to reject (ret=%d, errno=%d) ", config_name(c), ret, errno);
239                                         print_reg(config_name(c), reg.id);
240                                         putchar('\n');
241                                         ++failed_reject;
242                                 }
243                                 break;
244                         }
245 
246                         /* skips_set registers are skipped for set operation */
247                         if (s->skips_set && find_reg(s->skips_set, s->skips_set_n, reg.id)) {
248                                 skip_reg = true;
249                                 ++skipped_set;
250                                 break;
251                         }
252                 }
253 
254                 if (!reject_reg && !skip_reg) {
255                         ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, &reg);
256                         if (ret) {
257                                 printf("%s: Failed to set ", config_name(c));
258                                 print_reg(config_name(c), reg.id);
259                                 putchar('\n');
260                                 ++failed_set;
261                         }
262                 }
263         }
264 
265         for_each_new_reg(i)
266                 ++new_regs;
267 
268         for_each_missing_reg(i)
269                 ++missing_regs;
270 
271         if (new_regs || missing_regs) {
272                 n = 0;
273                 for_each_reg_filtered(i)
274                         ++n;
275 
276                 printf("%s: Number blessed registers: %5lld\n", config_name(c), blessed_n);
277                 printf("%s: Number registers:         %5lld (includes %lld filtered registers)\n",
278                        config_name(c), reg_list->n, reg_list->n - n);
279         }
280 
281         if (new_regs) {
282                 printf("\n%s: There are %d new registers.\n"
283                        "Consider adding them to the blessed reg "
284                        "list with the following lines:\n\n", config_name(c), new_regs);
285                 for_each_new_reg(i)
286                         print_reg(config_name(c), reg_list->reg[i]);
287                 putchar('\n');
288         }
289 
290         if (missing_regs) {
291                 printf("\n%s: There are %d missing registers.\n"
292                        "The following lines are missing registers:\n\n", config_name(c), missing_regs);
293                 for_each_missing_reg(i)
294                         print_reg(config_name(c), blessed_reg[i]);
295                 putchar('\n');
296         }
297 
298         TEST_ASSERT(!missing_regs && !failed_get && !failed_set && !failed_reject,
299                     "%s: There are %d missing registers; %d registers failed get; "
300                     "%d registers failed set; %d registers failed reject; %d registers skipped set",
301                     config_name(c), missing_regs, failed_get, failed_set, failed_reject, skipped_set);
302 
303         pr_info("%s: PASS\n", config_name(c));
304         blessed_n = 0;
305         free(blessed_reg);
306         free(reg_list);
307         kvm_vm_free(vm);
308 }
309 
310 static void help(void)
311 {
312         struct vcpu_reg_list *c;
313         int i;
314 
315         printf(
316         "\n"
317         "usage: get-reg-list [--config=<selection>] [--list] [--list-filtered]\n\n"
318         " --config=<selection>        Used to select a specific vcpu configuration for the test/listing\n"
319         "                             '<selection>' may be\n");
320 
321         for (i = 0; i < vcpu_configs_n; ++i) {
322                 c = vcpu_configs[i];
323                 printf(
324         "                               '%s'\n", config_name(c));
325         }
326 
327         printf(
328         "\n"
329         " --list                      Print the register list rather than test it (requires --config)\n"
330         " --list-filtered             Print registers that would normally be filtered out (requires --config)\n"
331         "\n"
332         );
333 }
334 
335 static struct vcpu_reg_list *parse_config(const char *config)
336 {
337         struct vcpu_reg_list *c = NULL;
338         int i;
339 
340         if (config[8] != '=')
341                 help(), exit(1);
342 
343         for (i = 0; i < vcpu_configs_n; ++i) {
344                 c = vcpu_configs[i];
345                 if (strcmp(config_name(c), &config[9]) == 0)
346                         break;
347         }
348 
349         if (i == vcpu_configs_n)
350                 help(), exit(1);
351 
352         return c;
353 }
354 
355 int main(int ac, char **av)
356 {
357         struct vcpu_reg_list *c, *sel = NULL;
358         int i, ret = 0;
359         pid_t pid;
360 
361         for (i = 1; i < ac; ++i) {
362                 if (strncmp(av[i], "--config", 8) == 0)
363                         sel = parse_config(av[i]);
364                 else if (strcmp(av[i], "--list") == 0)
365                         print_list = true;
366                 else if (strcmp(av[i], "--list-filtered") == 0)
367                         print_filtered = true;
368                 else if (strcmp(av[i], "--help") == 0 || strcmp(av[1], "-h") == 0)
369                         help(), exit(0);
370                 else
371                         help(), exit(1);
372         }
373 
374         if (print_list || print_filtered) {
375                 /*
376                  * We only want to print the register list of a single config.
377                  */
378                 if (!sel)
379                         help(), exit(1);
380         }
381 
382         for (i = 0; i < vcpu_configs_n; ++i) {
383                 c = vcpu_configs[i];
384                 if (sel && c != sel)
385                         continue;
386 
387                 pid = fork();
388 
389                 if (!pid) {
390                         run_test(c);
391                         exit(0);
392                 } else {
393                         int wstatus;
394                         pid_t wpid = wait(&wstatus);
395                         TEST_ASSERT(wpid == pid && WIFEXITED(wstatus), "wait: Unexpected return");
396                         if (WEXITSTATUS(wstatus) && WEXITSTATUS(wstatus) != KSFT_SKIP)
397                                 ret = KSFT_FAIL;
398                 }
399         }
400 
401         return ret;
402 }
403 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php