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 #include <kern/assert.h>
30 #include <kern/hvg_hypercall.h>
31 #include <i386/x86_hypercall.h>
32 #include <kdp/ml/i386/kdp_x86_common.h>
33 #include <i386/cpuid.h>
34 #include <os/log.h>
35 #include <vm/pmap.h>
36 #include <vm/vm_map_xnu.h>
37 #include <x86_64/lowglobals.h>
38
39 bool
hvg_is_hcall_available(hvg_hcall_code_t hcall)40 hvg_is_hcall_available(hvg_hcall_code_t hcall)
41 {
42 assert3u(hcall, <, HVG_HCALL_COUNT);
43
44 const uint64_t features = cpuid_vmm_get_applepv_features();
45 const uint64_t hcall_feature[HVG_HCALL_COUNT] = {
46 [HVG_HCALL_TRIGGER_DUMP] = CPUID_LEAF_FEATURE_COREDUMP,
47 [HVG_HCALL_SET_COREDUMP_DATA] = CPUID_LEAF_FEATURE_XNU_DEBUG,
48 [HVG_HCALL_GET_MABS_OFFSET] = CPUID_LEAF_FEATURE_MABS_OFFSET,
49 [HVG_HCALL_GET_BOOTSESSIONUUID] = CPUID_LEAF_FEATURE_BOOTSESSIONUUID,
50 };
51 return cpuid_vmm_present() && (features & hcall_feature[hcall]) != 0;
52 }
53
54 /*
55 * This routine issues an Apple hypercall that notifies the hypervisor to
56 * take a guest kernel coredump. If the vmcore argument is not NULL, the
57 * name tag of the vmcore file is copied into the caller's vmcore tag array.
58 * Otherwise the name tag is ignored.
59 */
60
61 hvg_hcall_return_t
hvg_hcall_trigger_dump(hvg_hcall_vmcore_file_t * vmcore,const hvg_hcall_dump_option_t dump_option)62 hvg_hcall_trigger_dump(hvg_hcall_vmcore_file_t *vmcore,
63 const hvg_hcall_dump_option_t dump_option)
64 {
65 assert(hvg_is_hcall_available(HVG_HCALL_TRIGGER_DUMP));
66 assert3u(dump_option, ==, HVG_HCALL_DUMP_OPTION_REGULAR); /* Only known option for now. */
67
68 hvg_hcall_output_regs_t output = {};
69 const hvg_hcall_return_t ret = hvg_hypercall1(HVG_HCALL_TRIGGER_DUMP,
70 dump_option,
71 &output);
72
73 if (ret != HVG_HCALL_SUCCESS) {
74 return ret;
75 }
76
77 if (vmcore) {
78 /* Caller requested vmcore tag to be returned */
79 static_assert(sizeof(vmcore->tag) > sizeof(output), "not enough room for tag");
80 static_assert(sizeof(vmcore->tag[0] * sizeof(uint64_t)) == sizeof(output.rax), "mis-match of tag and output sizes");
81
82 const size_t reg_size = sizeof(uint64_t);
83
84 memcpy(&vmcore->tag[reg_size * 0], &output.rax, reg_size);
85 memcpy(&vmcore->tag[reg_size * 1], &output.rdi, reg_size);
86 memcpy(&vmcore->tag[reg_size * 2], &output.rsi, reg_size);
87 memcpy(&vmcore->tag[reg_size * 3], &output.rdx, reg_size);
88 memcpy(&vmcore->tag[reg_size * 4], &output.rcx, reg_size);
89 memcpy(&vmcore->tag[reg_size * 5], &output.r8, reg_size);
90 memcpy(&vmcore->tag[reg_size * 6], &output.r9, reg_size);
91 vmcore->tag[reg_size * 7] = '\0';
92 }
93
94 return HVG_HCALL_SUCCESS;
95 }
96
97 extern vm_offset_t c_buffers;
98 extern vm_size_t c_buffers_size;
99
100 /*
101 * Inform the hypervisor of the kernel physical address of
102 * the low globals data and kernel CR3 value.
103 */
104 void
hvg_hcall_set_coredump_data(void)105 hvg_hcall_set_coredump_data(void)
106 {
107 assert(hvg_is_hcall_available(HVG_HCALL_SET_COREDUMP_DATA));
108
109 hvg_hcall_output_regs_t output = {};
110
111 /* Hypercall to set up necessary information for reliable coredump */
112 const hvg_hcall_return_t ret = hvg_hypercall6(HVG_HCALL_SET_COREDUMP_DATA,
113 lowGlo.lgStext, /* args[0]: KVA of kernel text */
114 kernel_map->min_offset, /* args[1]: KVA of kernel_map_start */
115 kernel_map->max_offset, /* args[2]: KVA of kernel_map_end */
116 kernel_pmap->pm_cr3, /* args[3]: Kernel CR3 */
117 c_buffers, /* args[4]: KVA of compressor buffers */
118 c_buffers_size, /* args[5]: Size of compressor buffers */
119 &output);
120
121 if (ret != HVG_HCALL_SUCCESS) {
122 os_log_error(OS_LOG_DEFAULT, "%s: hcall failed, ret %d\n",
123 __func__, ret);
124 }
125 }
126
127 hvg_hcall_return_t
hvg_hcall_get_mabs_offset(uint64_t * mabs_offset)128 hvg_hcall_get_mabs_offset(uint64_t *mabs_offset)
129 {
130 assert(hvg_is_hcall_available(HVG_HCALL_GET_MABS_OFFSET));
131
132 hvg_hcall_output_regs_t output = {};
133
134 const hvg_hcall_return_t ret = hvg_hypercall2(HVG_HCALL_GET_MABS_OFFSET,
135 pal_rtc_nanotime_info.tsc_base, pal_rtc_nanotime_info.ns_base,
136 &output);
137 if (ret != HVG_HCALL_SUCCESS) {
138 return ret;
139 }
140
141 *mabs_offset = output.rdi;
142 return HVG_HCALL_SUCCESS;
143 }
144
145 hvg_hcall_return_t
hvg_hcall_get_bootsessionuuid(uuid_string_t uuid)146 hvg_hcall_get_bootsessionuuid(uuid_string_t uuid)
147 {
148 assert(hvg_is_hcall_available(HVG_HCALL_GET_BOOTSESSIONUUID));
149
150 hvg_hcall_output_regs_t output = {};
151
152 const hvg_hcall_return_t ret = hvg_hypercall0(
153 HVG_HCALL_GET_BOOTSESSIONUUID, &output);
154 if (ret != HVG_HCALL_SUCCESS) {
155 return ret;
156 }
157
158 static_assert(sizeof(uuid_string_t) == 37,
159 "unexpected uuid string length");
160
161 memset(uuid, 0, sizeof(uuid_string_t));
162
163 memcpy(&uuid[0], &output.rax, 8);
164 memcpy(&uuid[8], &output.rdi, 8);
165 memcpy(&uuid[16], &output.rsi, 8);
166 memcpy(&uuid[24], &output.rdx, 8);
167 memcpy(&uuid[32], &output.rcx, 4);
168
169 return HVG_HCALL_SUCCESS;
170 }
171