xref: /xnu-8020.140.41/osfmk/i386/x86_hypercall.c (revision 27b03b360a988dfd3dfdf34262bb0042026747cc)
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