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 <kdp/ml/i386/kdp_x86_common.h>
32 #include <i386/cpuid.h>
33 #include <os/log.h>
34 #include <vm/pmap.h>
35 #include <vm/vm_map.h>
36 #include <x86_64/lowglobals.h>
37
38 static bool
hvg_live_coredump_enabled(void)39 hvg_live_coredump_enabled(void)
40 {
41 return cpuid_vmm_present() && (cpuid_vmm_get_applepv_features() & CPUID_LEAF_FEATURE_COREDUMP) != 0;
42 }
43
44 static bool
hvg_xnu_debug_enabled(void)45 hvg_xnu_debug_enabled(void)
46 {
47 return cpuid_vmm_present() && (cpuid_vmm_get_applepv_features() & CPUID_LEAF_FEATURE_XNU_DEBUG) != 0;
48 }
49
50 /*
51 * This routine issues an Apple hypercall that notifies the hypervisor to
52 * take a guest kernel coredump. If the vmcore argument is not NULL, the
53 * name tag of the vmcore file is copied into the caller's vmcore tag array.
54 * Otherwise the name tag is ignored.
55 */
56
57 hvg_hcall_return_t
hvg_hcall_trigger_dump(hvg_hcall_vmcore_file_t * vmcore,const hvg_hcall_dump_option_t dump_option)58 hvg_hcall_trigger_dump(hvg_hcall_vmcore_file_t *vmcore,
59 const hvg_hcall_dump_option_t dump_option)
60 {
61 hvg_hcall_return_t ret;
62 hvg_hcall_output_regs_t output;
63 const size_t reg_size = sizeof(output.rax);
64
65 /* Does the hypervisor support feature: live kernel core dump? */
66 if (!hvg_live_coredump_enabled()) {
67 return HVG_HCALL_FEAT_DISABLED;
68 }
69
70 /* Make sure that we don't overflow vmcore tag array with hypercall output */
71 if (vmcore && (reg_size != sizeof(uint64_t))) {
72 os_log_error(OS_LOG_DEFAULT, "%s: invalid hcall register size, %zu bytes (expect %zu bytes)\n",
73 __func__, reg_size, sizeof(uint64_t));
74 return HVG_HCALL_INVALID_PARAMETER;
75 }
76
77 switch (dump_option) {
78 case HVG_HCALL_DUMP_OPTION_REGULAR:
79 /* Only regular dump-guest-memory is supported for now */
80 break;
81 default:
82 return HVG_HCALL_INVALID_PARAMETER;
83 }
84
85 /* Everything checks out, issue hypercall */
86 memset(&output, 0, sizeof(hvg_hcall_output_regs_t));
87 ret = hvg_hypercall1(HVG_HCALL_TRIGGER_DUMP,
88 dump_option,
89 &output);
90
91 if (ret == HVG_HCALL_SUCCESS) {
92 if (vmcore) {
93 /* Caller requested vmcore tag to be returned */
94 memcpy(&vmcore->tag[0], &output.rax, reg_size);
95 memcpy(&vmcore->tag[reg_size], &output.rdi, reg_size);
96 memcpy(&vmcore->tag[reg_size * 2], &output.rsi, reg_size);
97 memcpy(&vmcore->tag[reg_size * 3], &output.rdx, reg_size);
98 memcpy(&vmcore->tag[reg_size * 4], &output.rcx, reg_size);
99 memcpy(&vmcore->tag[reg_size * 5], &output.r8, reg_size);
100 memcpy(&vmcore->tag[reg_size * 6], &output.r9, reg_size);
101 vmcore->tag[reg_size * 7] = '\0';
102 }
103 }
104 return ret;
105 }
106
107 extern vm_offset_t c_buffers;
108 extern vm_size_t c_buffers_size;
109
110 /*
111 * Inform the hypervisor of the kernel physical address of
112 * the low globals data and kernel CR3 value.
113 */
114 void
hvg_hcall_set_coredump_data(void)115 hvg_hcall_set_coredump_data(void)
116 {
117 hvg_hcall_return_t ret;
118 hvg_hcall_output_regs_t output;
119
120 /* Does the hypervisor support feature: xnu debugging? */
121 if (!hvg_xnu_debug_enabled()) {
122 return;
123 }
124
125 /* Hypercall to set up necessary information for reliable coredump */
126 ret = hvg_hypercall6(HVG_HCALL_SET_COREDUMP_DATA,
127 lowGlo.lgStext, /* args[0]: KVA of kernel text */
128 kernel_map->min_offset, /* args[1]: KVA of kernel_map_start */
129 kernel_map->max_offset, /* args[2]: KVA of kernel_map_end */
130 kernel_pmap->pm_cr3, /* args[3]: Kernel CR3 */
131 c_buffers, /* args[4]: KVA of compressor buffers */
132 c_buffers_size, /* args[5]: Size of compressor buffers */
133 &output);
134
135 if (ret != HVG_HCALL_SUCCESS) {
136 os_log_error(OS_LOG_DEFAULT, "%s: hcall failed, ret %d\n",
137 __func__, ret);
138 }
139 }
140