1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * xen_vmcall_test
4  *
5  * Copyright © 2020 Amazon.com, Inc. or its affiliates.
6  *
7  * Userspace hypercall testing
8  */
9 
10 #include "test_util.h"
11 #include "kvm_util.h"
12 #include "processor.h"
13 
14 #define VCPU_ID		5
15 
16 #define HCALL_REGION_GPA	0xc0000000ULL
17 #define HCALL_REGION_SLOT	10
18 
19 static struct kvm_vm *vm;
20 
21 #define INPUTVALUE 17
22 #define ARGVALUE(x) (0xdeadbeef5a5a0000UL + x)
23 #define RETVALUE 0xcafef00dfbfbffffUL
24 
25 #define XEN_HYPERCALL_MSR	0x40000200
26 #define HV_GUEST_OS_ID_MSR	0x40000000
27 #define HV_HYPERCALL_MSR	0x40000001
28 
29 #define HVCALL_SIGNAL_EVENT		0x005d
30 #define HV_STATUS_INVALID_ALIGNMENT	4
31 
32 static void guest_code(void)
33 {
34 	unsigned long rax = INPUTVALUE;
35 	unsigned long rdi = ARGVALUE(1);
36 	unsigned long rsi = ARGVALUE(2);
37 	unsigned long rdx = ARGVALUE(3);
38 	unsigned long rcx;
39 	register unsigned long r10 __asm__("r10") = ARGVALUE(4);
40 	register unsigned long r8 __asm__("r8") = ARGVALUE(5);
41 	register unsigned long r9 __asm__("r9") = ARGVALUE(6);
42 
43 	/* First a direct invocation of 'vmcall' */
44 	__asm__ __volatile__("vmcall" :
45 			     "=a"(rax) :
46 			     "a"(rax), "D"(rdi), "S"(rsi), "d"(rdx),
47 			     "r"(r10), "r"(r8), "r"(r9));
48 	GUEST_ASSERT(rax == RETVALUE);
49 
50 	/* Fill in the Xen hypercall page */
51 	__asm__ __volatile__("wrmsr" : : "c" (XEN_HYPERCALL_MSR),
52 			     "a" (HCALL_REGION_GPA & 0xffffffff),
53 			     "d" (HCALL_REGION_GPA >> 32));
54 
55 	/* Set Hyper-V Guest OS ID */
56 	__asm__ __volatile__("wrmsr" : : "c" (HV_GUEST_OS_ID_MSR),
57 			     "a" (0x5a), "d" (0));
58 
59 	/* Hyper-V hypercall page */
60 	u64 msrval = HCALL_REGION_GPA + PAGE_SIZE + 1;
61 	__asm__ __volatile__("wrmsr" : : "c" (HV_HYPERCALL_MSR),
62 			     "a" (msrval & 0xffffffff),
63 			     "d" (msrval >> 32));
64 
65 	/* Invoke a Xen hypercall */
66 	__asm__ __volatile__("call *%1" : "=a"(rax) :
67 			     "r"(HCALL_REGION_GPA + INPUTVALUE * 32),
68 			     "a"(rax), "D"(rdi), "S"(rsi), "d"(rdx),
69 			     "r"(r10), "r"(r8), "r"(r9));
70 	GUEST_ASSERT(rax == RETVALUE);
71 
72 	/* Invoke a Hyper-V hypercall */
73 	rax = 0;
74 	rcx = HVCALL_SIGNAL_EVENT;	/* code */
75 	rdx = 0x5a5a5a5a;		/* ingpa (badly aligned) */
76 	__asm__ __volatile__("call *%1" : "=a"(rax) :
77 			     "r"(HCALL_REGION_GPA + PAGE_SIZE),
78 			     "a"(rax), "c"(rcx), "d"(rdx),
79 			     "r"(r8));
80 	GUEST_ASSERT(rax == HV_STATUS_INVALID_ALIGNMENT);
81 
82 	GUEST_DONE();
83 }
84 
85 int main(int argc, char *argv[])
86 {
87 	if (!(kvm_check_cap(KVM_CAP_XEN_HVM) &
88 	      KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL) ) {
89 		print_skip("KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL not available");
90 		exit(KSFT_SKIP);
91 	}
92 
93 	vm = vm_create_default(VCPU_ID, 0, (void *) guest_code);
94 	vcpu_set_hv_cpuid(vm, VCPU_ID);
95 
96 	struct kvm_xen_hvm_config hvmc = {
97 		.flags = KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL,
98 		.msr = XEN_HYPERCALL_MSR,
99 	};
100 	vm_ioctl(vm, KVM_XEN_HVM_CONFIG, &hvmc);
101 
102 	/* Map a region for the hypercall pages */
103 	vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS,
104 				    HCALL_REGION_GPA, HCALL_REGION_SLOT, 2, 0);
105 	virt_map(vm, HCALL_REGION_GPA, HCALL_REGION_GPA, 2);
106 
107 	for (;;) {
108 		volatile struct kvm_run *run = vcpu_state(vm, VCPU_ID);
109 		struct ucall uc;
110 
111 		vcpu_run(vm, VCPU_ID);
112 
113 		if (run->exit_reason == KVM_EXIT_XEN) {
114 			ASSERT_EQ(run->xen.type, KVM_EXIT_XEN_HCALL);
115 			ASSERT_EQ(run->xen.u.hcall.cpl, 0);
116 			ASSERT_EQ(run->xen.u.hcall.longmode, 1);
117 			ASSERT_EQ(run->xen.u.hcall.input, INPUTVALUE);
118 			ASSERT_EQ(run->xen.u.hcall.params[0], ARGVALUE(1));
119 			ASSERT_EQ(run->xen.u.hcall.params[1], ARGVALUE(2));
120 			ASSERT_EQ(run->xen.u.hcall.params[2], ARGVALUE(3));
121 			ASSERT_EQ(run->xen.u.hcall.params[3], ARGVALUE(4));
122 			ASSERT_EQ(run->xen.u.hcall.params[4], ARGVALUE(5));
123 			ASSERT_EQ(run->xen.u.hcall.params[5], ARGVALUE(6));
124 			run->xen.u.hcall.result = RETVALUE;
125 			continue;
126 		}
127 
128 		TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
129 			    "Got exit_reason other than KVM_EXIT_IO: %u (%s)\n",
130 			    run->exit_reason,
131 			    exit_reason_str(run->exit_reason));
132 
133 		switch (get_ucall(vm, VCPU_ID, &uc)) {
134 		case UCALL_ABORT:
135 			TEST_FAIL("%s", (const char *)uc.args[0]);
136 			/* NOT REACHED */
137 		case UCALL_SYNC:
138 			break;
139 		case UCALL_DONE:
140 			goto done;
141 		default:
142 			TEST_FAIL("Unknown ucall 0x%lx.", uc.cmd);
143 		}
144 	}
145 done:
146 	kvm_vm_free(vm);
147 	return 0;
148 }
149