162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Check for KVM_GET_REG_LIST regressions. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2020, Red Hat, Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * When attempting to migrate from a host with an older kernel to a host 862306a36Sopenharmony_ci * with a newer kernel we allow the newer kernel on the destination to 962306a36Sopenharmony_ci * list new registers with get-reg-list. We assume they'll be unused, at 1062306a36Sopenharmony_ci * least until the guest reboots, and so they're relatively harmless. 1162306a36Sopenharmony_ci * However, if the destination host with the newer kernel is missing 1262306a36Sopenharmony_ci * registers which the source host with the older kernel has, then that's 1362306a36Sopenharmony_ci * a regression in get-reg-list. This test checks for that regression by 1462306a36Sopenharmony_ci * checking the current list against a blessed list. We should never have 1562306a36Sopenharmony_ci * missing registers, but if new ones appear then they can probably be 1662306a36Sopenharmony_ci * added to the blessed list. A completely new blessed list can be created 1762306a36Sopenharmony_ci * by running the test with the --list command line argument. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * The blessed list should be created from the oldest possible kernel. 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci#include <stdio.h> 2262306a36Sopenharmony_ci#include <stdlib.h> 2362306a36Sopenharmony_ci#include <string.h> 2462306a36Sopenharmony_ci#include <unistd.h> 2562306a36Sopenharmony_ci#include <sys/types.h> 2662306a36Sopenharmony_ci#include <sys/wait.h> 2762306a36Sopenharmony_ci#include "kvm_util.h" 2862306a36Sopenharmony_ci#include "test_util.h" 2962306a36Sopenharmony_ci#include "processor.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic struct kvm_reg_list *reg_list; 3262306a36Sopenharmony_cistatic __u64 *blessed_reg, blessed_n; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ciextern struct vcpu_reg_list *vcpu_configs[]; 3562306a36Sopenharmony_ciextern int vcpu_configs_n; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define for_each_reg(i) \ 3862306a36Sopenharmony_ci for ((i) = 0; (i) < reg_list->n; ++(i)) 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define for_each_reg_filtered(i) \ 4162306a36Sopenharmony_ci for_each_reg(i) \ 4262306a36Sopenharmony_ci if (!filter_reg(reg_list->reg[i])) 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define for_each_missing_reg(i) \ 4562306a36Sopenharmony_ci for ((i) = 0; (i) < blessed_n; ++(i)) \ 4662306a36Sopenharmony_ci if (!find_reg(reg_list->reg, reg_list->n, blessed_reg[i])) \ 4762306a36Sopenharmony_ci if (check_supported_reg(vcpu, blessed_reg[i])) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define for_each_new_reg(i) \ 5062306a36Sopenharmony_ci for_each_reg_filtered(i) \ 5162306a36Sopenharmony_ci if (!find_reg(blessed_reg, blessed_n, reg_list->reg[i])) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define for_each_present_blessed_reg(i) \ 5462306a36Sopenharmony_ci for_each_reg(i) \ 5562306a36Sopenharmony_ci if (find_reg(blessed_reg, blessed_n, reg_list->reg[i])) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic const char *config_name(struct vcpu_reg_list *c) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct vcpu_reg_sublist *s; 6062306a36Sopenharmony_ci int len = 0; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (c->name) 6362306a36Sopenharmony_ci return c->name; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci for_each_sublist(c, s) 6662306a36Sopenharmony_ci len += strlen(s->name) + 1; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci c->name = malloc(len); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci len = 0; 7162306a36Sopenharmony_ci for_each_sublist(c, s) { 7262306a36Sopenharmony_ci if (!strcmp(s->name, "base")) 7362306a36Sopenharmony_ci continue; 7462306a36Sopenharmony_ci strcat(c->name + len, s->name); 7562306a36Sopenharmony_ci len += strlen(s->name) + 1; 7662306a36Sopenharmony_ci c->name[len - 1] = '+'; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci c->name[len - 1] = '\0'; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return c->name; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cibool __weak check_supported_reg(struct kvm_vcpu *vcpu, __u64 reg) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci return true; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cibool __weak filter_reg(__u64 reg) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci return false; 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic bool find_reg(__u64 regs[], __u64 nr_regs, __u64 reg) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci int i; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci for (i = 0; i < nr_regs; ++i) 9862306a36Sopenharmony_ci if (reg == regs[i]) 9962306a36Sopenharmony_ci return true; 10062306a36Sopenharmony_ci return false; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_civoid __weak print_reg(const char *prefix, __u64 id) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci printf("\t0x%llx,\n", id); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cibool __weak check_reject_set(int err) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci return true; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_civoid __weak finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_reg_list *c) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#ifdef __aarch64__ 11862306a36Sopenharmony_cistatic void prepare_vcpu_init(struct vcpu_reg_list *c, struct kvm_vcpu_init *init) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct vcpu_reg_sublist *s; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci for_each_sublist(c, s) 12362306a36Sopenharmony_ci if (s->capability) 12462306a36Sopenharmony_ci init->features[s->feature / 32] |= 1 << (s->feature % 32); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct kvm_vcpu_init init = { .target = -1, }; 13062306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci prepare_vcpu_init(c, &init); 13362306a36Sopenharmony_ci vcpu = __vm_vcpu_add(vm, 0); 13462306a36Sopenharmony_ci aarch64_vcpu_setup(vcpu, &init); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return vcpu; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci#else 13962306a36Sopenharmony_cistatic struct kvm_vcpu *vcpu_config_get_vcpu(struct vcpu_reg_list *c, struct kvm_vm *vm) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci return __vm_vcpu_add(vm, 0); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci#endif 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic void check_supported(struct vcpu_reg_list *c) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct vcpu_reg_sublist *s; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci for_each_sublist(c, s) { 15062306a36Sopenharmony_ci if (!s->capability) 15162306a36Sopenharmony_ci continue; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci __TEST_REQUIRE(kvm_has_cap(s->capability), 15462306a36Sopenharmony_ci "%s: %s not available, skipping tests\n", 15562306a36Sopenharmony_ci config_name(c), s->name); 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic bool print_list; 16062306a36Sopenharmony_cistatic bool print_filtered; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic void run_test(struct vcpu_reg_list *c) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci int new_regs = 0, missing_regs = 0, i, n; 16562306a36Sopenharmony_ci int failed_get = 0, failed_set = 0, failed_reject = 0; 16662306a36Sopenharmony_ci int skipped_set = 0; 16762306a36Sopenharmony_ci struct kvm_vcpu *vcpu; 16862306a36Sopenharmony_ci struct kvm_vm *vm; 16962306a36Sopenharmony_ci struct vcpu_reg_sublist *s; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci check_supported(c); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci vm = vm_create_barebones(); 17462306a36Sopenharmony_ci vcpu = vcpu_config_get_vcpu(c, vm); 17562306a36Sopenharmony_ci finalize_vcpu(vcpu, c); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci reg_list = vcpu_get_reg_list(vcpu); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (print_list || print_filtered) { 18062306a36Sopenharmony_ci putchar('\n'); 18162306a36Sopenharmony_ci for_each_reg(i) { 18262306a36Sopenharmony_ci __u64 id = reg_list->reg[i]; 18362306a36Sopenharmony_ci if ((print_list && !filter_reg(id)) || 18462306a36Sopenharmony_ci (print_filtered && filter_reg(id))) 18562306a36Sopenharmony_ci print_reg(config_name(c), id); 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci putchar('\n'); 18862306a36Sopenharmony_ci return; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci for_each_sublist(c, s) 19262306a36Sopenharmony_ci blessed_n += s->regs_n; 19362306a36Sopenharmony_ci blessed_reg = calloc(blessed_n, sizeof(__u64)); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci n = 0; 19662306a36Sopenharmony_ci for_each_sublist(c, s) { 19762306a36Sopenharmony_ci for (i = 0; i < s->regs_n; ++i) 19862306a36Sopenharmony_ci blessed_reg[n++] = s->regs[i]; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* 20262306a36Sopenharmony_ci * We only test that we can get the register and then write back the 20362306a36Sopenharmony_ci * same value. Some registers may allow other values to be written 20462306a36Sopenharmony_ci * back, but others only allow some bits to be changed, and at least 20562306a36Sopenharmony_ci * for ID registers set will fail if the value does not exactly match 20662306a36Sopenharmony_ci * what was returned by get. If registers that allow other values to 20762306a36Sopenharmony_ci * be written need to have the other values tested, then we should 20862306a36Sopenharmony_ci * create a new set of tests for those in a new independent test 20962306a36Sopenharmony_ci * executable. 21062306a36Sopenharmony_ci * 21162306a36Sopenharmony_ci * Only do the get/set tests on present, blessed list registers, 21262306a36Sopenharmony_ci * since we don't know the capabilities of any new registers. 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_ci for_each_present_blessed_reg(i) { 21562306a36Sopenharmony_ci uint8_t addr[2048 / 8]; 21662306a36Sopenharmony_ci struct kvm_one_reg reg = { 21762306a36Sopenharmony_ci .id = reg_list->reg[i], 21862306a36Sopenharmony_ci .addr = (__u64)&addr, 21962306a36Sopenharmony_ci }; 22062306a36Sopenharmony_ci bool reject_reg = false, skip_reg = false; 22162306a36Sopenharmony_ci int ret; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci ret = __vcpu_get_reg(vcpu, reg_list->reg[i], &addr); 22462306a36Sopenharmony_ci if (ret) { 22562306a36Sopenharmony_ci printf("%s: Failed to get ", config_name(c)); 22662306a36Sopenharmony_ci print_reg(config_name(c), reg.id); 22762306a36Sopenharmony_ci putchar('\n'); 22862306a36Sopenharmony_ci ++failed_get; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci for_each_sublist(c, s) { 23262306a36Sopenharmony_ci /* rejects_set registers are rejected for set operation */ 23362306a36Sopenharmony_ci if (s->rejects_set && find_reg(s->rejects_set, s->rejects_set_n, reg.id)) { 23462306a36Sopenharmony_ci reject_reg = true; 23562306a36Sopenharmony_ci ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, ®); 23662306a36Sopenharmony_ci if (ret != -1 || !check_reject_set(errno)) { 23762306a36Sopenharmony_ci printf("%s: Failed to reject (ret=%d, errno=%d) ", config_name(c), ret, errno); 23862306a36Sopenharmony_ci print_reg(config_name(c), reg.id); 23962306a36Sopenharmony_ci putchar('\n'); 24062306a36Sopenharmony_ci ++failed_reject; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* skips_set registers are skipped for set operation */ 24662306a36Sopenharmony_ci if (s->skips_set && find_reg(s->skips_set, s->skips_set_n, reg.id)) { 24762306a36Sopenharmony_ci skip_reg = true; 24862306a36Sopenharmony_ci ++skipped_set; 24962306a36Sopenharmony_ci break; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (!reject_reg && !skip_reg) { 25462306a36Sopenharmony_ci ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, ®); 25562306a36Sopenharmony_ci if (ret) { 25662306a36Sopenharmony_ci printf("%s: Failed to set ", config_name(c)); 25762306a36Sopenharmony_ci print_reg(config_name(c), reg.id); 25862306a36Sopenharmony_ci putchar('\n'); 25962306a36Sopenharmony_ci ++failed_set; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci for_each_new_reg(i) 26562306a36Sopenharmony_ci ++new_regs; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci for_each_missing_reg(i) 26862306a36Sopenharmony_ci ++missing_regs; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (new_regs || missing_regs) { 27162306a36Sopenharmony_ci n = 0; 27262306a36Sopenharmony_ci for_each_reg_filtered(i) 27362306a36Sopenharmony_ci ++n; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci printf("%s: Number blessed registers: %5lld\n", config_name(c), blessed_n); 27662306a36Sopenharmony_ci printf("%s: Number registers: %5lld (includes %lld filtered registers)\n", 27762306a36Sopenharmony_ci config_name(c), reg_list->n, reg_list->n - n); 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (new_regs) { 28162306a36Sopenharmony_ci printf("\n%s: There are %d new registers.\n" 28262306a36Sopenharmony_ci "Consider adding them to the blessed reg " 28362306a36Sopenharmony_ci "list with the following lines:\n\n", config_name(c), new_regs); 28462306a36Sopenharmony_ci for_each_new_reg(i) 28562306a36Sopenharmony_ci print_reg(config_name(c), reg_list->reg[i]); 28662306a36Sopenharmony_ci putchar('\n'); 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (missing_regs) { 29062306a36Sopenharmony_ci printf("\n%s: There are %d missing registers.\n" 29162306a36Sopenharmony_ci "The following lines are missing registers:\n\n", config_name(c), missing_regs); 29262306a36Sopenharmony_ci for_each_missing_reg(i) 29362306a36Sopenharmony_ci print_reg(config_name(c), blessed_reg[i]); 29462306a36Sopenharmony_ci putchar('\n'); 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci TEST_ASSERT(!missing_regs && !failed_get && !failed_set && !failed_reject, 29862306a36Sopenharmony_ci "%s: There are %d missing registers; %d registers failed get; " 29962306a36Sopenharmony_ci "%d registers failed set; %d registers failed reject; %d registers skipped set", 30062306a36Sopenharmony_ci config_name(c), missing_regs, failed_get, failed_set, failed_reject, skipped_set); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci pr_info("%s: PASS\n", config_name(c)); 30362306a36Sopenharmony_ci blessed_n = 0; 30462306a36Sopenharmony_ci free(blessed_reg); 30562306a36Sopenharmony_ci free(reg_list); 30662306a36Sopenharmony_ci kvm_vm_free(vm); 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic void help(void) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct vcpu_reg_list *c; 31262306a36Sopenharmony_ci int i; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci printf( 31562306a36Sopenharmony_ci "\n" 31662306a36Sopenharmony_ci "usage: get-reg-list [--config=<selection>] [--list] [--list-filtered]\n\n" 31762306a36Sopenharmony_ci " --config=<selection> Used to select a specific vcpu configuration for the test/listing\n" 31862306a36Sopenharmony_ci " '<selection>' may be\n"); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci for (i = 0; i < vcpu_configs_n; ++i) { 32162306a36Sopenharmony_ci c = vcpu_configs[i]; 32262306a36Sopenharmony_ci printf( 32362306a36Sopenharmony_ci " '%s'\n", config_name(c)); 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci printf( 32762306a36Sopenharmony_ci "\n" 32862306a36Sopenharmony_ci " --list Print the register list rather than test it (requires --config)\n" 32962306a36Sopenharmony_ci " --list-filtered Print registers that would normally be filtered out (requires --config)\n" 33062306a36Sopenharmony_ci "\n" 33162306a36Sopenharmony_ci ); 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic struct vcpu_reg_list *parse_config(const char *config) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct vcpu_reg_list *c = NULL; 33762306a36Sopenharmony_ci int i; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (config[8] != '=') 34062306a36Sopenharmony_ci help(), exit(1); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci for (i = 0; i < vcpu_configs_n; ++i) { 34362306a36Sopenharmony_ci c = vcpu_configs[i]; 34462306a36Sopenharmony_ci if (strcmp(config_name(c), &config[9]) == 0) 34562306a36Sopenharmony_ci break; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (i == vcpu_configs_n) 34962306a36Sopenharmony_ci help(), exit(1); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return c; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ciint main(int ac, char **av) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct vcpu_reg_list *c, *sel = NULL; 35762306a36Sopenharmony_ci int i, ret = 0; 35862306a36Sopenharmony_ci pid_t pid; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci for (i = 1; i < ac; ++i) { 36162306a36Sopenharmony_ci if (strncmp(av[i], "--config", 8) == 0) 36262306a36Sopenharmony_ci sel = parse_config(av[i]); 36362306a36Sopenharmony_ci else if (strcmp(av[i], "--list") == 0) 36462306a36Sopenharmony_ci print_list = true; 36562306a36Sopenharmony_ci else if (strcmp(av[i], "--list-filtered") == 0) 36662306a36Sopenharmony_ci print_filtered = true; 36762306a36Sopenharmony_ci else if (strcmp(av[i], "--help") == 0 || strcmp(av[1], "-h") == 0) 36862306a36Sopenharmony_ci help(), exit(0); 36962306a36Sopenharmony_ci else 37062306a36Sopenharmony_ci help(), exit(1); 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci if (print_list || print_filtered) { 37462306a36Sopenharmony_ci /* 37562306a36Sopenharmony_ci * We only want to print the register list of a single config. 37662306a36Sopenharmony_ci */ 37762306a36Sopenharmony_ci if (!sel) 37862306a36Sopenharmony_ci help(), exit(1); 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci for (i = 0; i < vcpu_configs_n; ++i) { 38262306a36Sopenharmony_ci c = vcpu_configs[i]; 38362306a36Sopenharmony_ci if (sel && c != sel) 38462306a36Sopenharmony_ci continue; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci pid = fork(); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (!pid) { 38962306a36Sopenharmony_ci run_test(c); 39062306a36Sopenharmony_ci exit(0); 39162306a36Sopenharmony_ci } else { 39262306a36Sopenharmony_ci int wstatus; 39362306a36Sopenharmony_ci pid_t wpid = wait(&wstatus); 39462306a36Sopenharmony_ci TEST_ASSERT(wpid == pid && WIFEXITED(wstatus), "wait: Unexpected return"); 39562306a36Sopenharmony_ci if (WEXITSTATUS(wstatus) && WEXITSTATUS(wstatus) != KSFT_SKIP) 39662306a36Sopenharmony_ci ret = KSFT_FAIL; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci return ret; 40162306a36Sopenharmony_ci} 402