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