xref: /xnu-12377.41.6/osfmk/i386/x86_hypercall.h (revision bbb1b6f9e71b8cdde6e5cd6f4841f207dee3d828)
1 /*
2  * Copyright (c) 2024 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #ifndef _I386_X86_HYPERCALL_H_
30 #define _I386_X86_HYPERCALL_H_
31 
32 #include <kern/hvg_hypercall.h>
33 #include <i386/cpuid.h>
34 
35 /*
36  * Apple Hypercall Calling Convention (x64)
37  *
38  * Registers |                Usage                       |
39  * --------------------------------------------------------
40  *      %rax |    In:  hypercall code                     |
41  *           |    Out: if RFLAGS.CF = 0 (success)         |
42  *           |           hypercall output[0]              |
43  *           |         if RFLAGS.CF = 1 (error)           |
44  *           |           hypercall error value            |
45  *      %rdi |    In:  1st argument                       |
46  *           |    Out: hypercall output[1]                |
47  *      %rsi |    In:  2nd argument                       |
48  *           |    Out: hypercall output[2]                |
49  *      %rdx |    In:  3rd argument                       |
50  *           |    Out: hypercall output[3]                |
51  *      %rcx |    In:  4th argument                       |
52  *           |    Out: hypercall output[4]                |
53  *      %r8  |    In:  5th argument                       |
54  *           |    Out: hypercall output[5]                |
55  *      %r9  |    In:  6th argument                       |
56  *           |    Out: hypercall output[6]                |
57  *
58  * %rax is used by the caller to specify hypercall code. When a hypercall fails,
59  * the hypervisor stores errno in %rax. A successful hypercall returns the
60  * output of the call in %rax, %rdi, %rsi, %rdx, %rcx, %r8, and %r9.
61  */
62 
63 typedef struct hvg_hcall_output_regs {
64 	uint64_t   rax;
65 	uint64_t   rdi;
66 	uint64_t   rsi;
67 	uint64_t   rdx;
68 	uint64_t   rcx;
69 	uint64_t   r8;
70 	uint64_t   r9;
71 } hvg_hcall_output_regs_t;
72 
73 /*
74  * To avoid collision with other hypercall interfaces (e.g., KVM) in the vmcall
75  * namespace, Apple hypercalls put "A" (0x41) in the top byte of %eax so that
76  * hypervisors can support multiple hypercall interfaces simultaneously and
77  * handle Apple hypercalls correctly for compatiblity.
78  *
79  * For example, KVM uses the same vmcall instruction and has call code 1 for
80  * KVM_HC_VAPIC_POLL_IRQ. When invoking an Apple hypercall with code 1, a
81  * hypervisor will not accidentially treat the Apple hypercall as a KVM call.
82  */
83 
84 #define HVG_HCALL_CODE(code) ('A' << 24 | (code & 0xFFFFFF))
85 
86 /*
87  * Caller is responsible for checking the existence of Apple Hypercall
88  * before invoking Apple hypercalls.
89  */
90 
91 #if defined(MACH_KERNEL_PRIVATE)
92 
93 #define HVG_HCALL_RETURN(rax) {\
94 	__asm__ __volatile__ goto (\
95 	                           "jnc 2f  \n\t" \
96 	                           "jmp %l0 \n\t" \
97 	                           "2:      \n\t" \
98 	                          : /* no output */ \
99 	                          : /* no input */  \
100 	                          : /* no clobber */ \
101 	                          : error);\
102 	return HVG_HCALL_SUCCESS;\
103 error:\
104 	return (hvg_hcall_return_t)rax;\
105 }
106 
107 static hvg_hcall_return_t
hvg_hypercall6(uint64_t code,uint64_t rdi,uint64_t rsi,uint64_t rdx,uint64_t rcx,uint64_t r8,uint64_t r9,hvg_hcall_output_regs_t * output)108 hvg_hypercall6(uint64_t code, uint64_t rdi, uint64_t rsi, uint64_t rdx, uint64_t rcx, uint64_t r8, uint64_t r9,
109     hvg_hcall_output_regs_t *output)
110 {
111 	__asm__ __volatile__ ("movq %12, %%r8  \n\t"
112                           "movq %13, %%r9  \n\t"
113                           "vmcall          \n\t"
114                           "movq %%r8, %5   \n\t"
115                           "movq %%r9, %6   \n\t"
116                         : "=a" (output->rax),         /* %0:  output[0] */
117                           "=D" (output->rdi),         /* %1:  output[1] */
118                           "=S" (output->rsi),         /* %2:  output[2] */
119                           "=d" (output->rdx),         /* %3:  output[3] */
120                           "=c" (output->rcx),         /* %4:  output[4] */
121                           "=r" (output->r8),          /* %5:  output[5] */
122                           "=r" (output->r9)           /* %6:  output[6] */
123                         : "a"  (HVG_HCALL_CODE(code)),/* %7:  call code */
124                           "D"  (rdi),                 /* %8:  arg[0]    */
125                           "S"  (rsi),                 /* %9:  arg[1]    */
126                           "d"  (rdx),                 /* %10: arg[2]    */
127                           "c"  (rcx),                 /* %11: arg[3]    */
128                           "r"  (r8),                  /* %12: arg[4]    */
129                           "r"  (r9)                   /* %13: arg[5]    */
130                         : "memory", "r8", "r9");
131 	HVG_HCALL_RETURN(output->rax);
132 }
133 
134 static inline hvg_hcall_return_t
hvg_hypercall0(const uint64_t code,hvg_hcall_output_regs_t * output)135 hvg_hypercall0(const uint64_t code,
136     hvg_hcall_output_regs_t *output)
137 {
138 	return hvg_hypercall6(code, 0, 0, 0, 0, 0, 0, output);
139 }
140 
141 static inline hvg_hcall_return_t
hvg_hypercall1(const uint64_t code,const uint64_t rdi,hvg_hcall_output_regs_t * output)142 hvg_hypercall1(const uint64_t code,
143     const uint64_t rdi,
144     hvg_hcall_output_regs_t *output)
145 {
146 	return hvg_hypercall6(code, rdi, 0, 0, 0, 0, 0, output);
147 }
148 
149 static inline hvg_hcall_return_t
hvg_hypercall2(const uint64_t code,const uint64_t rdi,const uint64_t rsi,hvg_hcall_output_regs_t * output)150 hvg_hypercall2(const uint64_t code,
151     const uint64_t rdi, const uint64_t rsi,
152     hvg_hcall_output_regs_t *output)
153 {
154 	return hvg_hypercall6(code, rdi, rsi, 0, 0, 0, 0, output);
155 }
156 
157 static inline hvg_hcall_return_t
hvg_hypercall3(const uint64_t code,const uint64_t rdi,const uint64_t rsi,const uint64_t rdx,hvg_hcall_output_regs_t * output)158 hvg_hypercall3(const uint64_t code,
159     const uint64_t rdi, const uint64_t rsi, const uint64_t rdx,
160     hvg_hcall_output_regs_t *output)
161 {
162 	return hvg_hypercall6(code, rdi, rsi, rdx, 0, 0, 0, output);
163 }
164 
165 static inline hvg_hcall_return_t
hvg_hypercall4(const uint64_t code,const uint64_t rdi,const uint64_t rsi,const uint64_t rdx,const uint64_t rcx,hvg_hcall_output_regs_t * output)166 hvg_hypercall4(const uint64_t code,
167     const uint64_t rdi, const uint64_t rsi, const uint64_t rdx, const uint64_t rcx,
168     hvg_hcall_output_regs_t *output)
169 {
170 	return hvg_hypercall6(code, rdi, rsi, rdx, rcx, 0, 0, output);
171 }
172 
173 static inline hvg_hcall_return_t
hvg_hypercall5(const uint64_t code,const uint64_t rdi,const uint64_t rsi,const uint64_t rdx,const uint64_t rcx,const uint64_t r8,hvg_hcall_output_regs_t * output)174 hvg_hypercall5(const uint64_t code,
175     const uint64_t rdi, const uint64_t rsi, const uint64_t rdx, const uint64_t rcx, const uint64_t r8,
176     hvg_hcall_output_regs_t *output)
177 {
178 	return hvg_hypercall6(code, rdi, rsi, rdx, rcx, r8, 0, output);
179 }
180 
181 static inline long
kvmcompat_hypercall2(unsigned long code,unsigned long a0,unsigned long a1)182 kvmcompat_hypercall2(unsigned long code, unsigned long a0, unsigned long a1)
183 {
184 	long retval;
185 	__asm__ __volatile__ (
186 		"vmcall"
187 		: "=a" (retval)
188 		: "a" (code), "b" (a0), "c" (a1)
189 		: "memory"
190 	);
191 	return retval;
192 }
193 #endif /* MACH_KERNEL_PRIVATE */
194 #endif /* _I386_X86_HYPERCALL_H_ */
195