1ee1563f4SThomas Huth // SPDX-License-Identifier: GPL-2.0-only
2ee1563f4SThomas Huth /*
3ee1563f4SThomas Huth * Test for s390x KVM_CAP_SYNC_REGS
4ee1563f4SThomas Huth *
5ee1563f4SThomas Huth * Based on the same test for x86:
6ee1563f4SThomas Huth * Copyright (C) 2018, Google LLC.
7ee1563f4SThomas Huth *
8ee1563f4SThomas Huth * Adaptions for s390x:
9ee1563f4SThomas Huth * Copyright (C) 2019, Red Hat, Inc.
10ee1563f4SThomas Huth *
11ee1563f4SThomas Huth * Test expected behavior of the KVM_CAP_SYNC_REGS functionality.
12ee1563f4SThomas Huth */
13ee1563f4SThomas Huth
14ee1563f4SThomas Huth #define _GNU_SOURCE /* for program_invocation_short_name */
15ee1563f4SThomas Huth #include <fcntl.h>
16ee1563f4SThomas Huth #include <stdio.h>
17ee1563f4SThomas Huth #include <stdlib.h>
18ee1563f4SThomas Huth #include <string.h>
19ee1563f4SThomas Huth #include <sys/ioctl.h>
20ee1563f4SThomas Huth
21ee1563f4SThomas Huth #include "test_util.h"
22ee1563f4SThomas Huth #include "kvm_util.h"
23efaa83a3SCollin Walling #include "diag318_test_handler.h"
2417e48d8aSThomas Huth #include "kselftest.h"
25ee1563f4SThomas Huth
guest_code(void)26ee1563f4SThomas Huth static void guest_code(void)
27ee1563f4SThomas Huth {
28efec8d21SChristian Borntraeger /*
29efec8d21SChristian Borntraeger * We embed diag 501 here instead of doing a ucall to avoid that
30efec8d21SChristian Borntraeger * the compiler has messed with r11 at the time of the ucall.
31efec8d21SChristian Borntraeger */
32efec8d21SChristian Borntraeger asm volatile (
33efec8d21SChristian Borntraeger "0: diag 0,0,0x501\n"
34efec8d21SChristian Borntraeger " ahi 11,1\n"
35efec8d21SChristian Borntraeger " j 0b\n"
36efec8d21SChristian Borntraeger );
37ee1563f4SThomas Huth }
38ee1563f4SThomas Huth
39ee1563f4SThomas Huth #define REG_COMPARE(reg) \
40ee1563f4SThomas Huth TEST_ASSERT(left->reg == right->reg, \
41ee1563f4SThomas Huth "Register " #reg \
42ee1563f4SThomas Huth " values did not match: 0x%llx, 0x%llx\n", \
43ee1563f4SThomas Huth left->reg, right->reg)
44ee1563f4SThomas Huth
456a46fcf9SChristian Borntraeger #define REG_COMPARE32(reg) \
466a46fcf9SChristian Borntraeger TEST_ASSERT(left->reg == right->reg, \
476a46fcf9SChristian Borntraeger "Register " #reg \
486a46fcf9SChristian Borntraeger " values did not match: 0x%x, 0x%x\n", \
496a46fcf9SChristian Borntraeger left->reg, right->reg)
506a46fcf9SChristian Borntraeger
516a46fcf9SChristian Borntraeger
compare_regs(struct kvm_regs * left,struct kvm_sync_regs * right)52ee1563f4SThomas Huth static void compare_regs(struct kvm_regs *left, struct kvm_sync_regs *right)
53ee1563f4SThomas Huth {
54ee1563f4SThomas Huth int i;
55ee1563f4SThomas Huth
56ee1563f4SThomas Huth for (i = 0; i < 16; i++)
57ee1563f4SThomas Huth REG_COMPARE(gprs[i]);
58ee1563f4SThomas Huth }
59ee1563f4SThomas Huth
compare_sregs(struct kvm_sregs * left,struct kvm_sync_regs * right)60ee1563f4SThomas Huth static void compare_sregs(struct kvm_sregs *left, struct kvm_sync_regs *right)
61ee1563f4SThomas Huth {
62ee1563f4SThomas Huth int i;
63ee1563f4SThomas Huth
64ee1563f4SThomas Huth for (i = 0; i < 16; i++)
656a46fcf9SChristian Borntraeger REG_COMPARE32(acrs[i]);
66ee1563f4SThomas Huth
67ee1563f4SThomas Huth for (i = 0; i < 16; i++)
68ee1563f4SThomas Huth REG_COMPARE(crs[i]);
69ee1563f4SThomas Huth }
70ee1563f4SThomas Huth
71ee1563f4SThomas Huth #undef REG_COMPARE
72ee1563f4SThomas Huth
73efaa83a3SCollin Walling #define TEST_SYNC_FIELDS (KVM_SYNC_GPRS|KVM_SYNC_ACRS|KVM_SYNC_CRS|KVM_SYNC_DIAG318)
74ee1563f4SThomas Huth #define INVALID_SYNC_FIELD 0x80000000
75ee1563f4SThomas Huth
test_read_invalid(struct kvm_vcpu * vcpu)76e5b77cdeSSean Christopherson void test_read_invalid(struct kvm_vcpu *vcpu)
77ee1563f4SThomas Huth {
78e5b77cdeSSean Christopherson struct kvm_run *run = vcpu->run;
7917e48d8aSThomas Huth int rv;
80ee1563f4SThomas Huth
8181cb736cSThomas Huth /* Request reading invalid register set from VCPU. */
8281cb736cSThomas Huth run->kvm_valid_regs = INVALID_SYNC_FIELD;
83768e9a61SSean Christopherson rv = _vcpu_run(vcpu);
8481cb736cSThomas Huth TEST_ASSERT(rv < 0 && errno == EINVAL,
8581cb736cSThomas Huth "Invalid kvm_valid_regs did not cause expected KVM_RUN error: %d\n",
8681cb736cSThomas Huth rv);
87e5b77cdeSSean Christopherson run->kvm_valid_regs = 0;
8881cb736cSThomas Huth
8981cb736cSThomas Huth run->kvm_valid_regs = INVALID_SYNC_FIELD | TEST_SYNC_FIELDS;
90768e9a61SSean Christopherson rv = _vcpu_run(vcpu);
9181cb736cSThomas Huth TEST_ASSERT(rv < 0 && errno == EINVAL,
9281cb736cSThomas Huth "Invalid kvm_valid_regs did not cause expected KVM_RUN error: %d\n",
9381cb736cSThomas Huth rv);
94e5b77cdeSSean Christopherson run->kvm_valid_regs = 0;
9517e48d8aSThomas Huth }
9617e48d8aSThomas Huth
test_set_invalid(struct kvm_vcpu * vcpu)97e5b77cdeSSean Christopherson void test_set_invalid(struct kvm_vcpu *vcpu)
9817e48d8aSThomas Huth {
99e5b77cdeSSean Christopherson struct kvm_run *run = vcpu->run;
10017e48d8aSThomas Huth int rv;
10181cb736cSThomas Huth
10281cb736cSThomas Huth /* Request setting invalid register set into VCPU. */
10381cb736cSThomas Huth run->kvm_dirty_regs = INVALID_SYNC_FIELD;
104768e9a61SSean Christopherson rv = _vcpu_run(vcpu);
10581cb736cSThomas Huth TEST_ASSERT(rv < 0 && errno == EINVAL,
10681cb736cSThomas Huth "Invalid kvm_dirty_regs did not cause expected KVM_RUN error: %d\n",
10781cb736cSThomas Huth rv);
108e5b77cdeSSean Christopherson run->kvm_dirty_regs = 0;
10981cb736cSThomas Huth
11081cb736cSThomas Huth run->kvm_dirty_regs = INVALID_SYNC_FIELD | TEST_SYNC_FIELDS;
111768e9a61SSean Christopherson rv = _vcpu_run(vcpu);
11281cb736cSThomas Huth TEST_ASSERT(rv < 0 && errno == EINVAL,
11381cb736cSThomas Huth "Invalid kvm_dirty_regs did not cause expected KVM_RUN error: %d\n",
11481cb736cSThomas Huth rv);
115e5b77cdeSSean Christopherson run->kvm_dirty_regs = 0;
11617e48d8aSThomas Huth }
11717e48d8aSThomas Huth
test_req_and_verify_all_valid_regs(struct kvm_vcpu * vcpu)118e5b77cdeSSean Christopherson void test_req_and_verify_all_valid_regs(struct kvm_vcpu *vcpu)
11917e48d8aSThomas Huth {
120e5b77cdeSSean Christopherson struct kvm_run *run = vcpu->run;
12117e48d8aSThomas Huth struct kvm_sregs sregs;
12217e48d8aSThomas Huth struct kvm_regs regs;
12317e48d8aSThomas Huth int rv;
12481cb736cSThomas Huth
125ee1563f4SThomas Huth /* Request and verify all valid register sets. */
126ee1563f4SThomas Huth run->kvm_valid_regs = TEST_SYNC_FIELDS;
127768e9a61SSean Christopherson rv = _vcpu_run(vcpu);
128ee1563f4SThomas Huth TEST_ASSERT(rv == 0, "vcpu_run failed: %d\n", rv);
129*c96f57b0SVipin Sharma TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
130ee1563f4SThomas Huth TEST_ASSERT(run->s390_sieic.icptcode == 4 &&
131ee1563f4SThomas Huth (run->s390_sieic.ipa >> 8) == 0x83 &&
132ee1563f4SThomas Huth (run->s390_sieic.ipb >> 16) == 0x501,
133ee1563f4SThomas Huth "Unexpected interception code: ic=%u, ipa=0x%x, ipb=0x%x\n",
134ee1563f4SThomas Huth run->s390_sieic.icptcode, run->s390_sieic.ipa,
135ee1563f4SThomas Huth run->s390_sieic.ipb);
136ee1563f4SThomas Huth
137768e9a61SSean Christopherson vcpu_regs_get(vcpu, ®s);
138ee1563f4SThomas Huth compare_regs(®s, &run->s.regs);
139ee1563f4SThomas Huth
140768e9a61SSean Christopherson vcpu_sregs_get(vcpu, &sregs);
141ee1563f4SThomas Huth compare_sregs(&sregs, &run->s.regs);
14217e48d8aSThomas Huth }
14317e48d8aSThomas Huth
test_set_and_verify_various_reg_values(struct kvm_vcpu * vcpu)144e5b77cdeSSean Christopherson void test_set_and_verify_various_reg_values(struct kvm_vcpu *vcpu)
14517e48d8aSThomas Huth {
146e5b77cdeSSean Christopherson struct kvm_run *run = vcpu->run;
14717e48d8aSThomas Huth struct kvm_sregs sregs;
14817e48d8aSThomas Huth struct kvm_regs regs;
14917e48d8aSThomas Huth int rv;
150ee1563f4SThomas Huth
151ee1563f4SThomas Huth /* Set and verify various register values */
152ee1563f4SThomas Huth run->s.regs.gprs[11] = 0xBAD1DEA;
153ee1563f4SThomas Huth run->s.regs.acrs[0] = 1 << 11;
154ee1563f4SThomas Huth
155ee1563f4SThomas Huth run->kvm_valid_regs = TEST_SYNC_FIELDS;
156ee1563f4SThomas Huth run->kvm_dirty_regs = KVM_SYNC_GPRS | KVM_SYNC_ACRS;
157efaa83a3SCollin Walling
158efaa83a3SCollin Walling if (get_diag318_info() > 0) {
159efaa83a3SCollin Walling run->s.regs.diag318 = get_diag318_info();
160efaa83a3SCollin Walling run->kvm_dirty_regs |= KVM_SYNC_DIAG318;
161efaa83a3SCollin Walling }
162efaa83a3SCollin Walling
163768e9a61SSean Christopherson rv = _vcpu_run(vcpu);
164ee1563f4SThomas Huth TEST_ASSERT(rv == 0, "vcpu_run failed: %d\n", rv);
165*c96f57b0SVipin Sharma TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
166ee1563f4SThomas Huth TEST_ASSERT(run->s.regs.gprs[11] == 0xBAD1DEA + 1,
167ee1563f4SThomas Huth "r11 sync regs value incorrect 0x%llx.",
168ee1563f4SThomas Huth run->s.regs.gprs[11]);
169ee1563f4SThomas Huth TEST_ASSERT(run->s.regs.acrs[0] == 1 << 11,
1706a46fcf9SChristian Borntraeger "acr0 sync regs value incorrect 0x%x.",
171ee1563f4SThomas Huth run->s.regs.acrs[0]);
172efaa83a3SCollin Walling TEST_ASSERT(run->s.regs.diag318 == get_diag318_info(),
173efaa83a3SCollin Walling "diag318 sync regs value incorrect 0x%llx.",
174efaa83a3SCollin Walling run->s.regs.diag318);
175ee1563f4SThomas Huth
176768e9a61SSean Christopherson vcpu_regs_get(vcpu, ®s);
177ee1563f4SThomas Huth compare_regs(®s, &run->s.regs);
178ee1563f4SThomas Huth
179768e9a61SSean Christopherson vcpu_sregs_get(vcpu, &sregs);
180ee1563f4SThomas Huth compare_sregs(&sregs, &run->s.regs);
18117e48d8aSThomas Huth }
18217e48d8aSThomas Huth
test_clear_kvm_dirty_regs_bits(struct kvm_vcpu * vcpu)183e5b77cdeSSean Christopherson void test_clear_kvm_dirty_regs_bits(struct kvm_vcpu *vcpu)
18417e48d8aSThomas Huth {
185e5b77cdeSSean Christopherson struct kvm_run *run = vcpu->run;
18617e48d8aSThomas Huth int rv;
187ee1563f4SThomas Huth
188ee1563f4SThomas Huth /* Clear kvm_dirty_regs bits, verify new s.regs values are
189ee1563f4SThomas Huth * overwritten with existing guest values.
190ee1563f4SThomas Huth */
191ee1563f4SThomas Huth run->kvm_valid_regs = TEST_SYNC_FIELDS;
192ee1563f4SThomas Huth run->kvm_dirty_regs = 0;
193ee1563f4SThomas Huth run->s.regs.gprs[11] = 0xDEADBEEF;
194efaa83a3SCollin Walling run->s.regs.diag318 = 0x4B1D;
195768e9a61SSean Christopherson rv = _vcpu_run(vcpu);
196ee1563f4SThomas Huth TEST_ASSERT(rv == 0, "vcpu_run failed: %d\n", rv);
197*c96f57b0SVipin Sharma TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_S390_SIEIC);
198ee1563f4SThomas Huth TEST_ASSERT(run->s.regs.gprs[11] != 0xDEADBEEF,
199ee1563f4SThomas Huth "r11 sync regs value incorrect 0x%llx.",
200ee1563f4SThomas Huth run->s.regs.gprs[11]);
201efaa83a3SCollin Walling TEST_ASSERT(run->s.regs.diag318 != 0x4B1D,
202efaa83a3SCollin Walling "diag318 sync regs value incorrect 0x%llx.",
203efaa83a3SCollin Walling run->s.regs.diag318);
20417e48d8aSThomas Huth }
20517e48d8aSThomas Huth
20617e48d8aSThomas Huth struct testdef {
20717e48d8aSThomas Huth const char *name;
208e5b77cdeSSean Christopherson void (*test)(struct kvm_vcpu *vcpu);
20917e48d8aSThomas Huth } testlist[] = {
21017e48d8aSThomas Huth { "read invalid", test_read_invalid },
21117e48d8aSThomas Huth { "set invalid", test_set_invalid },
21217e48d8aSThomas Huth { "request+verify all valid regs", test_req_and_verify_all_valid_regs },
21317e48d8aSThomas Huth { "set+verify various regs", test_set_and_verify_various_reg_values },
21417e48d8aSThomas Huth { "clear kvm_dirty_regs bits", test_clear_kvm_dirty_regs_bits },
21517e48d8aSThomas Huth };
21617e48d8aSThomas Huth
main(int argc,char * argv[])21717e48d8aSThomas Huth int main(int argc, char *argv[])
21817e48d8aSThomas Huth {
219e5b77cdeSSean Christopherson struct kvm_vcpu *vcpu;
220e5b77cdeSSean Christopherson struct kvm_vm *vm;
22117e48d8aSThomas Huth int idx;
22217e48d8aSThomas Huth
2239393cb13SSean Christopherson TEST_REQUIRE(kvm_has_cap(KVM_CAP_SYNC_REGS));
2247ed397d1SSean Christopherson
22517e48d8aSThomas Huth ksft_print_header();
22617e48d8aSThomas Huth
22717e48d8aSThomas Huth ksft_set_plan(ARRAY_SIZE(testlist));
22817e48d8aSThomas Huth
22917e48d8aSThomas Huth /* Create VM */
230e5b77cdeSSean Christopherson vm = vm_create_with_one_vcpu(&vcpu, guest_code);
23117e48d8aSThomas Huth
23217e48d8aSThomas Huth for (idx = 0; idx < ARRAY_SIZE(testlist); idx++) {
233e5b77cdeSSean Christopherson testlist[idx].test(vcpu);
23417e48d8aSThomas Huth ksft_test_result_pass("%s\n", testlist[idx].name);
23517e48d8aSThomas Huth }
236ee1563f4SThomas Huth
237ee1563f4SThomas Huth kvm_vm_free(vm);
238ee1563f4SThomas Huth
23917e48d8aSThomas Huth ksft_finished(); /* Print results and exit() accordingly */
240ee1563f4SThomas Huth }
241