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
36vm_offset_t kc_highest_nonlinkedit_vmaddr = 0;
37int 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
41typedef 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 */
49void *collection_base_pointers[KCNumKinds] = {};
50kernel_mach_header_t * collection_mach_headers[KCNumKinds] = {};
51uintptr_t collection_slide[KCNumKinds] = {};
52void * collection_vp[KCNumKinds] = {};
53#else
54
55SECURITY_READ_ONLY_LATE(void *) collection_base_pointers[KCNumKinds];
56SECURITY_READ_ONLY_LATE(kernel_mach_header_t *) collection_mach_headers[KCNumKinds];
57SECURITY_READ_ONLY_LATE(uintptr_t) collection_slide[KCNumKinds];
58SECURITY_READ_ONLY_LATE(void *) collection_vp[KCNumKinds];
59#endif //(__x86_64__) || defined(__i386__)
60
61static inline kc_index_t
62kc_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
78MARK_AS_FIXUP_TEXT void
79PE_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
107void
108PE_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
120void
121PE_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
131void *
132PE_get_kc_header(kc_kind_t type)
133{
134 return collection_mach_headers[kc_kind2index(type)];
135}
136
137void
138PE_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
146void *
147PE_get_kc_vp(kc_kind_t type)
148{
149 kc_index_t i = kc_kind2index(type);
150 return collection_vp[i];
151}
152
153void
154PE_reset_all_kc_vp(void)
155{
156 for (int i = 0; i < KCNumKinds; i++) {
157 if (collection_vp[i] != NULL) {
158 vnode_put(vp: collection_vp[i]);
159 collection_vp[i] = NULL;
160 }
161 }
162}
163
164const void * const *
165PE_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 */
180uintptr_t
181PE_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
189bool
190PE_get_primary_kc_format(kc_format_t *type)
191{
192 if (type != NULL) {
193 kernel_mach_header_t *mh = PE_get_kc_header(type: 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
213void *
214PE_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
230bool
231PE_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(type: 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