1 /*
2 * Copyright (c) 2020 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
34 /*
35 * Apple Hypercall Calling Convention (x64)
36 *
37 * Registers | Usage |
38 * --------------------------------------------------------
39 * %rax | In: hypercall code |
40 * | Out: if RFLAGS.CF = 0 (success) |
41 * | hypercall output[0] |
42 * | if RFLAGS.CF = 1 (error) |
43 * | hypercall error value |
44 * %rdi | In: 1st argument |
45 * | Out: hypercall output[1] |
46 * %rsi | In: 2nd argument |
47 * | Out: hypercall output[2] |
48 * %rdx | In: 3rd argument |
49 * | Out: hypercall output[3] |
50 * %rcx | In: 4th argument |
51 * | Out: hypercall output[4] |
52 * %r8 | In: 5th argument |
53 * | Out: hypercall output[5] |
54 * %r9 | In: 6th argument |
55 * | Out: hypercall output[6] |
56 *
57 * %rax is used by the caller to specify hypercall code. When a hypercall fails,
58 * the hypervisor stores errno in %rax. A successful hypercall returns the
59 * output of the call in %rax, %rdi, %rsi, %rdx, %rcx, %r8, and %r9.
60 */
61
62 typedef struct hvg_hcall_output_regs {
63 uint64_t rax;
64 uint64_t rdi;
65 uint64_t rsi;
66 uint64_t rdx;
67 uint64_t rcx;
68 uint64_t r8;
69 uint64_t r9;
70 } hvg_hcall_output_regs_t;
71
72 /*
73 * To avoid collision with other hypercall interfaces (e.g., KVM) in the vmcall
74 * namespace, Apple hypercalls put "A" (0x41) in the top byte of %eax so that
75 * hypervisors can support multiple hypercall interfaces simultaneously and
76 * handle Apple hypercalls correctly for compatiblity.
77 *
78 * For example, KVM uses the same vmcall instruction and has call code 1 for
79 * KVM_HC_VAPIC_POLL_IRQ. When invoking an Apple hypercall with code 1, a
80 * hypervisor will not accidentially treat the Apple hypercall as a KVM call.
81 */
82
83 #define HVG_HCALL_CODE(code) ('A' << 24 | (code & 0xFFFFFF))
84
85
86 /*
87 * Caller is responsible for checking the existence of Apple Hypercall
88 * before invoking Apple hypercalls.
89 */
90
91 #define HVG_HCALL_RETURN(rax) {\
92 __asm__ __volatile__ goto (\
93 "jnc 2f \n\t" \
94 "jmp %l0 \n\t" \
95 "2: \n\t" \
96 : /* no output */ \
97 : /* no input */ \
98 : /* no clobber */ \
99 : error);\
100 return HVG_HCALL_SUCCESS;\
101 error:\
102 return (hvg_hcall_return_t)rax;\
103 }
104
105 static inline 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)106 hvg_hypercall6(uint64_t code, uint64_t rdi, uint64_t rsi, uint64_t rdx, uint64_t rcx, uint64_t r8, uint64_t r9,
107 hvg_hcall_output_regs_t *output)
108 {
109 __asm__ __volatile__ ("movq %12, %%r8 \n\t"
110 "movq %13, %%r9 \n\t"
111 "vmcall \n\t"
112 "movq %%r8, %5 \n\t"
113 "movq %%r9, %6 \n\t"
114 : "=a" (output->rax), /* %0: output[0] */
115 "=D" (output->rdi), /* %1: output[1] */
116 "=S" (output->rsi), /* %2: output[2] */
117 "=d" (output->rdx), /* %3: output[3] */
118 "=c" (output->rcx), /* %4: output[4] */
119 "=r" (output->r8), /* %5: output[5] */
120 "=r" (output->r9) /* %6: output[6] */
121 : "a" (HVG_HCALL_CODE(code)),/* %7: call code */
122 "D" (rdi), /* %8: arg[0] */
123 "S" (rsi), /* %9: arg[1] */
124 "d" (rdx), /* %10: arg[2] */
125 "c" (rcx), /* %11: arg[3] */
126 "r" (r8), /* %12: arg[4] */
127 "r" (r9) /* %13: arg[5] */
128 : "memory", "r8", "r9");
129 HVG_HCALL_RETURN(output->rax);
130 }
131
132 static inline hvg_hcall_return_t
hvg_hypercall0(const uint64_t code,hvg_hcall_output_regs_t * output)133 hvg_hypercall0(const uint64_t code,
134 hvg_hcall_output_regs_t *output)
135 {
136 return hvg_hypercall6(code, 0, 0, 0, 0, 0, 0, output);
137 }
138
139 static inline hvg_hcall_return_t
hvg_hypercall1(const uint64_t code,const uint64_t rdi,hvg_hcall_output_regs_t * output)140 hvg_hypercall1(const uint64_t code,
141 const uint64_t rdi,
142 hvg_hcall_output_regs_t *output)
143 {
144 return hvg_hypercall6(code, rdi, 0, 0, 0, 0, 0, output);
145 }
146
147 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)148 hvg_hypercall2(const uint64_t code,
149 const uint64_t rdi, const uint64_t rsi,
150 hvg_hcall_output_regs_t *output)
151 {
152 return hvg_hypercall6(code, rdi, rsi, 0, 0, 0, 0, output);
153 }
154
155 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)156 hvg_hypercall3(const uint64_t code,
157 const uint64_t rdi, const uint64_t rsi, const uint64_t rdx,
158 hvg_hcall_output_regs_t *output)
159 {
160 return hvg_hypercall6(code, rdi, rsi, rdx, 0, 0, 0, output);
161 }
162
163 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)164 hvg_hypercall4(const uint64_t code,
165 const uint64_t rdi, const uint64_t rsi, const uint64_t rdx, const uint64_t rcx,
166 hvg_hcall_output_regs_t *output)
167 {
168 return hvg_hypercall6(code, rdi, rsi, rdx, rcx, 0, 0, output);
169 }
170
171 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)172 hvg_hypercall5(const uint64_t code,
173 const uint64_t rdi, const uint64_t rsi, const uint64_t rdx, const uint64_t rcx, const uint64_t r8,
174 hvg_hcall_output_regs_t *output)
175 {
176 return hvg_hypercall6(code, rdi, rsi, rdx, rcx, r8, 0, output);
177 }
178
179 #endif /* _I386_X86_HYPERCALL_H_ */
180