xref: /xnu-12377.81.4/pexpert/gen/kcformat.c (revision 043036a2b3718f7f0be807e2870f8f47d3fa0796)
1 /*
2  * Copyright (c) 2000-2021 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  * @OSF_FREE_COPYRIGHT@
30  */
31 
32 #include <pexpert/pexpert.h>
33 #include <libkern/section_keywords.h>
34 #include <libkern/kernel_mach_header.h>
35 
36 vm_offset_t kc_highest_nonlinkedit_vmaddr = 0;
37 int vnode_put(void *vp);
38 
39 // FIXME: should come from mach-o/fixup_chains.h
40 // Index in basePointers array used by chained rebase in dyld_kernel_fixups.h
41 typedef enum kc_index {
42 	primary_kc_index = 0,
43 	pageable_kc_index = 1,
44 	auxiliary_kc_index = 3,
45 } kc_index_t;
46 
47 #if defined(__x86_64__) || defined(__i386__)
48 /* FIXME: This should be locked down during early boot */
49 void *collection_base_pointers[KCNumKinds] = {};
50 kernel_mach_header_t * collection_mach_headers[KCNumKinds] = {};
51 uintptr_t collection_slide[KCNumKinds] = {};
52 void * collection_vp[KCNumKinds] = {};
53 #else
54 
55 SECURITY_READ_ONLY_LATE(void *) collection_base_pointers[KCNumKinds];
56 SECURITY_READ_ONLY_LATE(kernel_mach_header_t *) collection_mach_headers[KCNumKinds];
57 SECURITY_READ_ONLY_LATE(uintptr_t) collection_slide[KCNumKinds];
58 SECURITY_READ_ONLY_LATE(void *) collection_vp[KCNumKinds];
59 #endif //(__x86_64__) || defined(__i386__)
60 
61 static inline __attribute__((__always_inline__)) kc_index_t
kc_kind2index(kc_kind_t type)62 kc_kind2index(kc_kind_t type)
63 {
64 	switch (type) {
65 	case KCKindPrimary:
66 		return primary_kc_index;
67 	case KCKindPageable:
68 		return pageable_kc_index;
69 	case KCKindAuxiliary:
70 		return auxiliary_kc_index;
71 	default:
72 		panic("Invalid KC Kind");
73 		break;
74 	}
75 	__builtin_unreachable();
76 }
77 
78 MARK_AS_FIXUP_TEXT void
PE_set_kc_header(kc_kind_t type,kernel_mach_header_t * header,uintptr_t slide)79 PE_set_kc_header(kc_kind_t type, kernel_mach_header_t *header, uintptr_t slide)
80 {
81 	kc_index_t i = kc_kind2index(type);
82 	assert(!collection_base_pointers[i]);
83 	assert(!collection_mach_headers[i]);
84 	collection_mach_headers[i] = header;
85 	collection_slide[i] = slide;
86 
87 	struct load_command *lc;
88 	struct segment_command_64 *seg;
89 	uint64_t lowest_vmaddr = ~0ULL;
90 
91 	lc = (struct load_command *)((uintptr_t)header + sizeof(*header));
92 	for (uint32_t j = 0; j < header->ncmds; j++,
93 	    lc = (struct load_command *)((uintptr_t)lc + lc->cmdsize)) {
94 		if (lc->cmd != LC_SEGMENT_64) {
95 			continue;
96 		}
97 		seg = (struct segment_command_64 *)(uintptr_t)lc;
98 		if (seg->vmaddr < lowest_vmaddr) {
99 			lowest_vmaddr = seg->vmaddr;
100 		}
101 	}
102 
103 	collection_base_pointers[i] = (void *)((uintptr_t)lowest_vmaddr + slide);
104 	assert((uint64_t)(uintptr_t)collection_base_pointers[i] != ~0ULL);
105 }
106 
107 void
PE_reset_kc_header(kc_kind_t type)108 PE_reset_kc_header(kc_kind_t type)
109 {
110 	if (type == KCKindPrimary) {
111 		return;
112 	}
113 
114 	kc_index_t i = kc_kind2index(type);
115 	collection_mach_headers[i] = 0;
116 	collection_base_pointers[i] = 0;
117 	collection_slide[i] = 0;
118 }
119 
120 void
PE_set_kc_header_and_base(kc_kind_t type,kernel_mach_header_t * header,void * base,uintptr_t slide)121 PE_set_kc_header_and_base(kc_kind_t type, kernel_mach_header_t * header, void *base, uintptr_t slide)
122 {
123 	kc_index_t i = kc_kind2index(type);
124 	assert(!collection_base_pointers[i]);
125 	assert(!collection_mach_headers[i]);
126 	collection_mach_headers[i] = header;
127 	collection_slide[i] = slide;
128 	collection_base_pointers[i] = base;
129 }
130 
131 void *
PE_get_kc_header(kc_kind_t type)132 PE_get_kc_header(kc_kind_t type)
133 {
134 	return collection_mach_headers[kc_kind2index(type)];
135 }
136 
137 void
PE_set_kc_vp(kc_kind_t type,void * vp)138 PE_set_kc_vp(kc_kind_t type, void *vp)
139 {
140 	kc_index_t i = kc_kind2index(type);
141 	assert(collection_vp[i] == NULL);
142 
143 	collection_vp[i] = vp;
144 }
145 
146 void *
PE_get_kc_vp(kc_kind_t type)147 PE_get_kc_vp(kc_kind_t type)
148 {
149 	kc_index_t i = kc_kind2index(type);
150 	return collection_vp[i];
151 }
152 
153 void
PE_reset_all_kc_vp(void)154 PE_reset_all_kc_vp(void)
155 {
156 	for (int i = 0; i < KCNumKinds; i++) {
157 		if (collection_vp[i] != NULL) {
158 			vnode_put(collection_vp[i]);
159 			collection_vp[i] = NULL;
160 		}
161 	}
162 }
163 
164 const void * const *
PE_get_kc_base_pointers(void)165 PE_get_kc_base_pointers(void)
166 {
167 	return (const void * const*)collection_base_pointers;
168 }
169 
170 /*
171  * Prelinked kexts in an MH_FILESET start with address 0,
172  * the slide for such kexts is calculated from the base
173  * address of the first kext mapped in that KC. Return the
174  * slide based on the type of the KC.
175  *
176  * Prelinked kexts booted from a non MH_FILESET KC are
177  * marked as KCKindUnknown, for such cases, return
178  * the kernel slide.
179  */
180 uintptr_t
PE_get_kc_slide(kc_kind_t type)181 PE_get_kc_slide(kc_kind_t type)
182 {
183 	if (type == KCKindUnknown) {
184 		return vm_kernel_slide;
185 	}
186 	return collection_slide[kc_kind2index(type)];
187 }
188 
189 bool
PE_get_primary_kc_format(kc_format_t * type)190 PE_get_primary_kc_format(kc_format_t *type)
191 {
192 	if (type != NULL) {
193 		kernel_mach_header_t *mh = PE_get_kc_header(KCKindPrimary);
194 		if (mh && mh->filetype == MH_FILESET) {
195 			*type = KCFormatFileset;
196 		} else {
197 #if defined(__arm64__)
198 			/* From osfmk/arm/arm_init.c */
199 			extern bool static_kernelcache;
200 			if (static_kernelcache) {
201 				*type = KCFormatStatic;
202 			} else {
203 				*type = KCFormatKCGEN;
204 			}
205 #else
206 			*type = KCFormatDynamic;
207 #endif
208 		}
209 	}
210 	return true;
211 }
212 
213 void *
PE_get_kc_baseaddress(kc_kind_t type)214 PE_get_kc_baseaddress(kc_kind_t type)
215 {
216 	kc_index_t i = kc_kind2index(type);
217 	switch (type) {
218 #if defined(__arm64__)
219 	case KCKindPrimary: {
220 		extern vm_offset_t segLOWESTTEXT;
221 		return (void*)segLOWESTTEXT;
222 	}
223 #endif
224 	default:
225 		return collection_base_pointers[i];
226 	}
227 	return NULL;
228 }
229 
230 bool
PE_get_kc_format(kc_kind_t type,kc_format_t * format)231 PE_get_kc_format(kc_kind_t type, kc_format_t *format)
232 {
233 	switch (type) {
234 	case KCKindPrimary:
235 		return PE_get_primary_kc_format(format);
236 	case KCKindPageable:
237 	case KCKindAuxiliary:
238 		*format = KCFormatFileset;
239 		break;
240 	default:
241 		*format = KCFormatUnknown;
242 		break;
243 	}
244 	return true;
245 }
246