]>
Commit | Line | Data |
---|---|---|
1c79356b | 1 | /* |
f427ee49 | 2 | * Copyright (c) 2000-2020 Apple Computer, Inc. All rights reserved. |
1c79356b | 3 | * |
2d21ac55 | 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
0a7de745 | 5 | * |
2d21ac55 A |
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. | |
0a7de745 | 14 | * |
2d21ac55 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
0a7de745 | 17 | * |
2d21ac55 A |
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 | |
8f6c56a5 A |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
2d21ac55 A |
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. | |
0a7de745 | 25 | * |
2d21ac55 | 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
1c79356b A |
27 | */ |
28 | /* | |
29 | * @OSF_COPYRIGHT@ | |
30 | */ | |
0a7de745 | 31 | /* |
1c79356b A |
32 | * Mach Operating System |
33 | * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University | |
34 | * All Rights Reserved. | |
0a7de745 | 35 | * |
1c79356b A |
36 | * Permission to use, copy, modify and distribute this software and its |
37 | * documentation is hereby granted, provided that both the copyright | |
38 | * notice and this permission notice appear in all copies of the | |
39 | * software, derivative works or modified versions, and any portions | |
40 | * thereof, and that both notices appear in supporting documentation. | |
0a7de745 | 41 | * |
1c79356b A |
42 | * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" |
43 | * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR | |
44 | * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. | |
0a7de745 | 45 | * |
1c79356b | 46 | * Carnegie Mellon requests users of this software to return to |
0a7de745 | 47 | * |
1c79356b A |
48 | * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU |
49 | * School of Computer Science | |
50 | * Carnegie Mellon University | |
51 | * Pittsburgh PA 15213-3890 | |
0a7de745 | 52 | * |
1c79356b A |
53 | * any improvements or extensions that they make and grant Carnegie Mellon |
54 | * the rights to redistribute these changes. | |
55 | */ | |
56 | /* | |
57 | */ | |
58 | /* | |
59 | * File: kern/kalloc.c | |
60 | * Author: Avadis Tevanian, Jr. | |
61 | * Date: 1985 | |
62 | * | |
63 | * General kernel memory allocator. This allocator is designed | |
64 | * to be used by the kernel to manage dynamic memory fast. | |
65 | */ | |
66 | ||
1c79356b | 67 | #include <mach/boolean.h> |
a39ff7e2 | 68 | #include <mach/sdt.h> |
1c79356b A |
69 | #include <mach/machine/vm_types.h> |
70 | #include <mach/vm_param.h> | |
71 | #include <kern/misc_protos.h> | |
f427ee49 | 72 | #include <kern/zalloc_internal.h> |
1c79356b | 73 | #include <kern/kalloc.h> |
316670eb | 74 | #include <kern/ledger.h> |
f427ee49 | 75 | #include <kern/backtrace.h> |
1c79356b A |
76 | #include <vm/vm_kern.h> |
77 | #include <vm/vm_object.h> | |
78 | #include <vm/vm_map.h> | |
39037602 | 79 | #include <sys/kdebug.h> |
1c79356b | 80 | |
5ba3f43e | 81 | #include <san/kasan.h> |
f427ee49 | 82 | #include <libkern/section_keywords.h> |
5ba3f43e | 83 | |
f427ee49 | 84 | /* #define KALLOC_DEBUG 1 */ |
1c79356b | 85 | |
2d21ac55 A |
86 | #define KALLOC_MAP_SIZE_MIN (16 * 1024 * 1024) |
87 | #define KALLOC_MAP_SIZE_MAX (128 * 1024 * 1024) | |
f427ee49 A |
88 | |
89 | static SECURITY_READ_ONLY_LATE(vm_offset_t) kalloc_map_min; | |
90 | static SECURITY_READ_ONLY_LATE(vm_offset_t) kalloc_map_max; | |
91 | static SECURITY_READ_ONLY_LATE(vm_size_t) kalloc_max; | |
92 | SECURITY_READ_ONLY_LATE(vm_size_t) kalloc_max_prerounded; | |
93 | /* size of kallocs that can come from kernel map */ | |
94 | SECURITY_READ_ONLY_LATE(vm_size_t) kalloc_kernmap_size; | |
95 | SECURITY_READ_ONLY_LATE(vm_map_t) kalloc_map; | |
96 | #if DEBUG || DEVELOPMENT | |
97 | static TUNABLE(bool, kheap_temp_debug, "kheap_temp_debug", false); | |
98 | ||
99 | #define KHT_BT_COUNT 14 | |
100 | struct kheap_temp_header { | |
101 | queue_chain_t kht_hdr_link; | |
102 | uintptr_t kht_hdr_pcs[KHT_BT_COUNT]; | |
103 | }; | |
104 | #endif | |
1c79356b | 105 | |
3e170ce0 A |
106 | /* how many times we couldn't allocate out of kalloc_map and fell back to kernel_map */ |
107 | unsigned long kalloc_fallback_count; | |
108 | ||
cb323159 A |
109 | uint_t kalloc_large_inuse; |
110 | vm_size_t kalloc_large_total; | |
111 | vm_size_t kalloc_large_max; | |
112 | vm_size_t kalloc_largest_allocated = 0; | |
113 | uint64_t kalloc_large_sum; | |
6d2010ae | 114 | |
f427ee49 A |
115 | LCK_GRP_DECLARE(kalloc_lck_grp, "kalloc.large"); |
116 | LCK_SPIN_DECLARE(kalloc_lock, &kalloc_lck_grp); | |
1c79356b | 117 | |
f427ee49 A |
118 | #define kalloc_spin_lock() lck_spin_lock(&kalloc_lock) |
119 | #define kalloc_unlock() lck_spin_unlock(&kalloc_lock) | |
6d2010ae | 120 | |
f427ee49 | 121 | #pragma mark initialization |
6d2010ae | 122 | |
1c79356b | 123 | /* |
a39ff7e2 A |
124 | * All allocations of size less than kalloc_max are rounded to the next nearest |
125 | * sized zone. This allocator is built on top of the zone allocator. A zone | |
126 | * is created for each potential size that we are willing to get in small | |
127 | * blocks. | |
1c79356b | 128 | * |
a39ff7e2 | 129 | * We assume that kalloc_max is not greater than 64K; |
1c79356b | 130 | * |
a39ff7e2 A |
131 | * Note that kalloc_max is somewhat confusingly named. It represents the first |
132 | * power of two for which no zone exists. kalloc_max_prerounded is the | |
133 | * smallest allocation size, before rounding, for which no zone exists. | |
316670eb | 134 | * |
a39ff7e2 A |
135 | * Also if the allocation size is more than kalloc_kernmap_size then allocate |
136 | * from kernel map rather than kalloc_map. | |
316670eb A |
137 | */ |
138 | ||
a39ff7e2 | 139 | #define KiB(x) (1024 * (x)) |
316670eb | 140 | |
0a7de745 | 141 | /* |
f427ee49 | 142 | * The k_zone_cfg table defines the configuration of zones on various platforms. |
0a7de745 | 143 | * The currently defined list of zones and their per-CPU caching behavior are as |
f427ee49 A |
144 | * follows |
145 | * | |
146 | * X:zone not present | |
147 | * N:zone present no cpu-caching | |
148 | * Y:zone present with cpu-caching | |
0a7de745 A |
149 | * |
150 | * Size macOS(64-bit) embedded(32-bit) embedded(64-bit) | |
151 | *-------- ---------------- ---------------- ---------------- | |
152 | * | |
153 | * 8 X Y X | |
154 | * 16 Y Y Y | |
155 | * 24 X Y X | |
156 | * 32 Y Y Y | |
157 | * 40 X Y X | |
158 | * 48 Y Y Y | |
159 | * 64 Y Y Y | |
160 | * 72 X Y X | |
161 | * 80 Y X Y | |
162 | * 88 X Y X | |
163 | * 96 Y X Y | |
164 | * 112 X Y X | |
165 | * 128 Y Y Y | |
166 | * 160 Y X Y | |
167 | * 192 Y Y Y | |
168 | * 224 Y X Y | |
169 | * 256 Y Y Y | |
170 | * 288 Y Y Y | |
171 | * 368 Y X Y | |
172 | * 384 X Y X | |
173 | * 400 Y X Y | |
174 | * 440 X Y X | |
175 | * 512 Y Y Y | |
176 | * 576 Y N N | |
177 | * 768 Y N N | |
178 | * 1024 Y Y Y | |
179 | * 1152 N N N | |
180 | * 1280 N N N | |
181 | * 1536 X N X | |
182 | * 1664 N X N | |
183 | * 2048 Y N N | |
184 | * 2128 X N X | |
185 | * 3072 X N X | |
186 | * 4096 Y N N | |
187 | * 6144 N N N | |
188 | * 8192 Y N N | |
cb323159 | 189 | * 12288 N X X |
94ff46dc A |
190 | * 16384 N X N |
191 | * 32768 X X N | |
0a7de745 A |
192 | * |
193 | */ | |
f427ee49 | 194 | struct kalloc_zone_cfg { |
0a7de745 | 195 | bool kzc_caching; |
f427ee49 | 196 | uint32_t kzc_size; |
a39ff7e2 | 197 | const char *kzc_name; |
f427ee49 A |
198 | }; |
199 | static SECURITY_READ_ONLY_LATE(struct kalloc_zone_cfg) k_zone_cfg[] = { | |
200 | #define KZC_ENTRY(SIZE, caching) { \ | |
201 | .kzc_caching = (caching), \ | |
202 | .kzc_size = (SIZE), \ | |
203 | .kzc_name = "kalloc." #SIZE \ | |
204 | } | |
0a7de745 | 205 | |
f427ee49 | 206 | #if !defined(XNU_TARGET_OS_OSX) |
316670eb | 207 | |
a39ff7e2 | 208 | #if KALLOC_MINSIZE == 16 && KALLOC_LOG2_MINALIGN == 4 |
0a7de745 A |
209 | /* Zone config for embedded 64-bit platforms */ |
210 | KZC_ENTRY(16, true), | |
211 | KZC_ENTRY(32, true), | |
212 | KZC_ENTRY(48, true), | |
213 | KZC_ENTRY(64, true), | |
214 | KZC_ENTRY(80, true), | |
215 | KZC_ENTRY(96, true), | |
216 | KZC_ENTRY(128, true), | |
217 | KZC_ENTRY(160, true), | |
218 | KZC_ENTRY(192, true), | |
219 | KZC_ENTRY(224, true), | |
220 | KZC_ENTRY(256, true), | |
221 | KZC_ENTRY(288, true), | |
222 | KZC_ENTRY(368, true), | |
223 | KZC_ENTRY(400, true), | |
224 | KZC_ENTRY(512, true), | |
225 | KZC_ENTRY(576, false), | |
226 | KZC_ENTRY(768, false), | |
227 | KZC_ENTRY(1024, true), | |
228 | KZC_ENTRY(1152, false), | |
229 | KZC_ENTRY(1280, false), | |
230 | KZC_ENTRY(1664, false), | |
231 | KZC_ENTRY(2048, false), | |
232 | KZC_ENTRY(4096, false), | |
233 | KZC_ENTRY(6144, false), | |
234 | KZC_ENTRY(8192, false), | |
235 | KZC_ENTRY(16384, false), | |
236 | KZC_ENTRY(32768, false), | |
237 | ||
316670eb | 238 | #elif KALLOC_MINSIZE == 8 && KALLOC_LOG2_MINALIGN == 3 |
0a7de745 A |
239 | /* Zone config for embedded 32-bit platforms */ |
240 | KZC_ENTRY(8, true), | |
241 | KZC_ENTRY(16, true), | |
242 | KZC_ENTRY(24, true), | |
243 | KZC_ENTRY(32, true), | |
244 | KZC_ENTRY(40, true), | |
245 | KZC_ENTRY(48, true), | |
246 | KZC_ENTRY(64, true), | |
247 | KZC_ENTRY(72, true), | |
248 | KZC_ENTRY(88, true), | |
249 | KZC_ENTRY(112, true), | |
250 | KZC_ENTRY(128, true), | |
251 | KZC_ENTRY(192, true), | |
252 | KZC_ENTRY(256, true), | |
253 | KZC_ENTRY(288, true), | |
254 | KZC_ENTRY(384, true), | |
255 | KZC_ENTRY(440, true), | |
256 | KZC_ENTRY(512, true), | |
257 | KZC_ENTRY(576, false), | |
258 | KZC_ENTRY(768, false), | |
259 | KZC_ENTRY(1024, true), | |
260 | KZC_ENTRY(1152, false), | |
261 | KZC_ENTRY(1280, false), | |
262 | KZC_ENTRY(1536, false), | |
263 | KZC_ENTRY(2048, false), | |
264 | KZC_ENTRY(2128, false), | |
265 | KZC_ENTRY(3072, false), | |
266 | KZC_ENTRY(4096, false), | |
267 | KZC_ENTRY(6144, false), | |
268 | KZC_ENTRY(8192, false), | |
94ff46dc A |
269 | /* To limit internal fragmentation, only add the following zones if the |
270 | * page size is greater than 4K. | |
271 | * Note that we use ARM_PGBYTES here (instead of one of the VM macros) | |
272 | * since it's guaranteed to be a compile time constant. | |
273 | */ | |
274 | #if ARM_PGBYTES > 4096 | |
0a7de745 A |
275 | KZC_ENTRY(16384, false), |
276 | KZC_ENTRY(32768, false), | |
94ff46dc | 277 | #endif /* ARM_PGBYTES > 4096 */ |
0a7de745 | 278 | |
316670eb | 279 | #else |
5c9f4661 | 280 | #error missing or invalid zone size parameters for kalloc |
316670eb A |
281 | #endif |
282 | ||
f427ee49 | 283 | #else /* !defined(XNU_TARGET_OS_OSX) */ |
0a7de745 A |
284 | |
285 | /* Zone config for macOS 64-bit platforms */ | |
286 | KZC_ENTRY(16, true), | |
287 | KZC_ENTRY(32, true), | |
288 | KZC_ENTRY(48, true), | |
289 | KZC_ENTRY(64, true), | |
290 | KZC_ENTRY(80, true), | |
291 | KZC_ENTRY(96, true), | |
292 | KZC_ENTRY(128, true), | |
293 | KZC_ENTRY(160, true), | |
294 | KZC_ENTRY(192, true), | |
295 | KZC_ENTRY(224, true), | |
296 | KZC_ENTRY(256, true), | |
297 | KZC_ENTRY(288, true), | |
298 | KZC_ENTRY(368, true), | |
299 | KZC_ENTRY(400, true), | |
300 | KZC_ENTRY(512, true), | |
301 | KZC_ENTRY(576, true), | |
302 | KZC_ENTRY(768, true), | |
303 | KZC_ENTRY(1024, true), | |
304 | KZC_ENTRY(1152, false), | |
305 | KZC_ENTRY(1280, false), | |
306 | KZC_ENTRY(1664, false), | |
307 | KZC_ENTRY(2048, true), | |
308 | KZC_ENTRY(4096, true), | |
309 | KZC_ENTRY(6144, false), | |
310 | KZC_ENTRY(8192, true), | |
cb323159 A |
311 | KZC_ENTRY(12288, false), |
312 | KZC_ENTRY(16384, false) | |
0a7de745 | 313 | |
f427ee49 | 314 | #endif /* !defined(XNU_TARGET_OS_OSX) */ |
0a7de745 | 315 | |
a39ff7e2 | 316 | #undef KZC_ENTRY |
316670eb A |
317 | }; |
318 | ||
f427ee49 | 319 | #define MAX_K_ZONE(kzc) (uint32_t)(sizeof(kzc) / sizeof(kzc[0])) |
316670eb A |
320 | |
321 | /* | |
322 | * Many kalloc() allocations are for small structures containing a few | |
f427ee49 | 323 | * pointers and longs - the dlut[] direct lookup table, indexed by |
316670eb A |
324 | * size normalized to the minimum alignment, finds the right zone index |
325 | * for them in one dereference. | |
326 | */ | |
327 | ||
f427ee49 A |
328 | #define INDEX_ZDLUT(size) (((size) + KALLOC_MINALIGN - 1) / KALLOC_MINALIGN) |
329 | #define MAX_SIZE_ZDLUT ((KALLOC_DLUT_SIZE - 1) * KALLOC_MINALIGN) | |
330 | ||
331 | static SECURITY_READ_ONLY_LATE(zone_t) k_zone_default[MAX_K_ZONE(k_zone_cfg)]; | |
332 | static SECURITY_READ_ONLY_LATE(zone_t) k_zone_data_buffers[MAX_K_ZONE(k_zone_cfg)]; | |
333 | static SECURITY_READ_ONLY_LATE(zone_t) k_zone_kext[MAX_K_ZONE(k_zone_cfg)]; | |
334 | ||
335 | #if VM_MAX_TAG_ZONES | |
336 | #if __LP64__ | |
337 | static_assert(VM_MAX_TAG_ZONES >= | |
338 | MAX_K_ZONE(k_zone_cfg) + MAX_K_ZONE(k_zone_cfg) + MAX_K_ZONE(k_zone_cfg)); | |
339 | #else | |
340 | static_assert(VM_MAX_TAG_ZONES >= MAX_K_ZONE(k_zone_cfg)); | |
341 | #endif | |
342 | #endif | |
316670eb | 343 | |
f427ee49 A |
344 | const char * const kalloc_heap_names[] = { |
345 | [KHEAP_ID_NONE] = "", | |
346 | [KHEAP_ID_DEFAULT] = "default.", | |
347 | [KHEAP_ID_DATA_BUFFERS] = "data.", | |
348 | [KHEAP_ID_KEXT] = "kext.", | |
349 | }; | |
316670eb A |
350 | |
351 | /* | |
f427ee49 | 352 | * Default kalloc heap configuration |
316670eb | 353 | */ |
f427ee49 A |
354 | static SECURITY_READ_ONLY_LATE(struct kheap_zones) kalloc_zones_default = { |
355 | .cfg = k_zone_cfg, | |
356 | .heap_id = KHEAP_ID_DEFAULT, | |
357 | .k_zone = k_zone_default, | |
358 | .max_k_zone = MAX_K_ZONE(k_zone_cfg) | |
359 | }; | |
360 | SECURITY_READ_ONLY_LATE(struct kalloc_heap) KHEAP_DEFAULT[1] = { | |
361 | { | |
362 | .kh_zones = &kalloc_zones_default, | |
363 | .kh_name = "default.", | |
364 | .kh_heap_id = KHEAP_ID_DEFAULT, | |
365 | } | |
366 | }; | |
316670eb | 367 | |
f427ee49 | 368 | KALLOC_HEAP_DEFINE(KHEAP_TEMP, "temp allocations", KHEAP_ID_DEFAULT); |
1c79356b | 369 | |
316670eb | 370 | |
f427ee49 A |
371 | /* |
372 | * Bag of bytes heap configuration | |
373 | */ | |
374 | static SECURITY_READ_ONLY_LATE(struct kheap_zones) kalloc_zones_data_buffers = { | |
375 | .cfg = k_zone_cfg, | |
376 | .heap_id = KHEAP_ID_DATA_BUFFERS, | |
377 | .k_zone = k_zone_data_buffers, | |
378 | .max_k_zone = MAX_K_ZONE(k_zone_cfg) | |
379 | }; | |
380 | SECURITY_READ_ONLY_LATE(struct kalloc_heap) KHEAP_DATA_BUFFERS[1] = { | |
381 | { | |
382 | .kh_zones = &kalloc_zones_data_buffers, | |
383 | .kh_name = "data.", | |
384 | .kh_heap_id = KHEAP_ID_DATA_BUFFERS, | |
385 | } | |
386 | }; | |
91447636 | 387 | |
6d2010ae | 388 | |
f427ee49 A |
389 | /* |
390 | * Kext heap configuration | |
391 | */ | |
392 | static SECURITY_READ_ONLY_LATE(struct kheap_zones) kalloc_zones_kext = { | |
393 | .cfg = k_zone_cfg, | |
394 | .heap_id = KHEAP_ID_KEXT, | |
395 | .k_zone = k_zone_kext, | |
396 | .max_k_zone = MAX_K_ZONE(k_zone_cfg) | |
397 | }; | |
398 | SECURITY_READ_ONLY_LATE(struct kalloc_heap) KHEAP_KEXT[1] = { | |
399 | { | |
400 | .kh_zones = &kalloc_zones_kext, | |
401 | .kh_name = "kext.", | |
402 | .kh_heap_id = KHEAP_ID_KEXT, | |
403 | } | |
404 | }; | |
6d2010ae | 405 | |
f427ee49 | 406 | KALLOC_HEAP_DEFINE(KERN_OS_MALLOC, "kern_os_malloc", KHEAP_ID_KEXT); |
6d2010ae | 407 | |
f427ee49 A |
408 | /* |
409 | * Initialize kalloc heap: Create zones, generate direct lookup table and | |
410 | * do a quick test on lookups | |
411 | */ | |
412 | __startup_func | |
413 | static void | |
414 | kalloc_zones_init(struct kheap_zones *zones) | |
415 | { | |
416 | struct kalloc_zone_cfg *cfg = zones->cfg; | |
417 | zone_t *k_zone = zones->k_zone; | |
418 | vm_size_t size; | |
91447636 | 419 | |
f427ee49 A |
420 | /* |
421 | * Allocate a zone for each size we are going to handle. | |
422 | */ | |
423 | for (uint32_t i = 0; i < zones->max_k_zone && | |
424 | (size = cfg[i].kzc_size) < kalloc_max; i++) { | |
425 | zone_create_flags_t flags = ZC_KASAN_NOREDZONE | | |
426 | ZC_KASAN_NOQUARANTINE | ZC_KALLOC_HEAP; | |
427 | if (cfg[i].kzc_caching) { | |
428 | flags |= ZC_CACHING; | |
429 | } | |
6d2010ae | 430 | |
f427ee49 A |
431 | k_zone[i] = zone_create_ext(cfg[i].kzc_name, size, flags, |
432 | ZONE_ID_ANY, ^(zone_t z){ | |
433 | z->kalloc_heap = zones->heap_id; | |
434 | }); | |
435 | /* | |
436 | * Set the updated elem size back to the config | |
437 | */ | |
438 | cfg[i].kzc_size = k_zone[i]->z_elem_size; | |
439 | } | |
6d2010ae | 440 | |
f427ee49 A |
441 | /* |
442 | * Count all the "raw" views for zones in the heap. | |
443 | */ | |
444 | zone_view_count += zones->max_k_zone; | |
445 | ||
446 | /* | |
447 | * Build the Direct LookUp Table for small allocations | |
448 | * As k_zone_cfg is shared between the heaps the | |
449 | * Direct LookUp Table is also shared and doesn't need to | |
450 | * be rebuilt per heap. | |
451 | */ | |
452 | size = 0; | |
453 | for (int i = 0; i <= KALLOC_DLUT_SIZE; i++, size += KALLOC_MINALIGN) { | |
454 | uint8_t zindex = 0; | |
455 | ||
456 | while ((vm_size_t)(cfg[zindex].kzc_size) < size) { | |
457 | zindex++; | |
458 | } | |
459 | ||
460 | if (i == KALLOC_DLUT_SIZE) { | |
461 | zones->k_zindex_start = zindex; | |
462 | break; | |
463 | } | |
464 | zones->dlut[i] = zindex; | |
465 | } | |
91447636 | 466 | |
f427ee49 A |
467 | #ifdef KALLOC_DEBUG |
468 | printf("kalloc_init: k_zindex_start %d\n", zones->k_zindex_start); | |
469 | ||
470 | /* | |
471 | * Do a quick synthesis to see how well/badly we can | |
472 | * find-a-zone for a given size. | |
473 | * Useful when debugging/tweaking the array of zone sizes. | |
474 | * Cache misses probably more critical than compare-branches! | |
475 | */ | |
476 | for (uint32_t i = 0; i < zones->max_k_zone; i++) { | |
477 | vm_size_t testsize = (vm_size_t)(cfg[i].kzc_size - 1); | |
478 | int compare = 0; | |
479 | uint8_t zindex; | |
480 | ||
481 | if (testsize < MAX_SIZE_ZDLUT) { | |
482 | compare += 1; /* 'if' (T) */ | |
483 | ||
484 | long dindex = INDEX_ZDLUT(testsize); | |
485 | zindex = (int)zones->dlut[dindex]; | |
486 | } else if (testsize < kalloc_max_prerounded) { | |
487 | compare += 2; /* 'if' (F), 'if' (T) */ | |
488 | ||
489 | zindex = zones->k_zindex_start; | |
490 | while ((vm_size_t)(cfg[zindex].kzc_size) < testsize) { | |
491 | zindex++; | |
492 | compare++; /* 'while' (T) */ | |
493 | } | |
494 | compare++; /* 'while' (F) */ | |
495 | } else { | |
496 | break; /* not zone-backed */ | |
497 | } | |
498 | zone_t z = k_zone[zindex]; | |
499 | printf("kalloc_init: req size %4lu: %8s.%16s took %d compare%s\n", | |
500 | (unsigned long)testsize, kalloc_heap_names[zones->heap_id], | |
501 | z->z_name, compare, compare == 1 ? "" : "s"); | |
502 | } | |
503 | #endif | |
504 | } | |
91447636 | 505 | |
1c79356b A |
506 | /* |
507 | * Initialize the memory allocator. This should be called only | |
508 | * once on a system wide basis (i.e. first processor to get here | |
509 | * does the initialization). | |
510 | * | |
511 | * This initializes all of the zones. | |
512 | */ | |
513 | ||
f427ee49 A |
514 | __startup_func |
515 | static void | |
516 | kalloc_init(void) | |
1c79356b A |
517 | { |
518 | kern_return_t retval; | |
519 | vm_offset_t min; | |
f427ee49 | 520 | vm_size_t kalloc_map_size; |
5ba3f43e | 521 | vm_map_kernel_flags_t vmk_flags; |
1c79356b | 522 | |
0a7de745 A |
523 | /* |
524 | * Scale the kalloc_map_size to physical memory size: stay below | |
b0d623f7 | 525 | * 1/8th the total zone map size, or 128 MB (for a 32-bit kernel). |
2d21ac55 | 526 | */ |
b0d623f7 A |
527 | kalloc_map_size = (vm_size_t)(sane_size >> 5); |
528 | #if !__LP64__ | |
0a7de745 | 529 | if (kalloc_map_size > KALLOC_MAP_SIZE_MAX) { |
2d21ac55 | 530 | kalloc_map_size = KALLOC_MAP_SIZE_MAX; |
0a7de745 | 531 | } |
b0d623f7 | 532 | #endif /* !__LP64__ */ |
0a7de745 | 533 | if (kalloc_map_size < KALLOC_MAP_SIZE_MIN) { |
2d21ac55 | 534 | kalloc_map_size = KALLOC_MAP_SIZE_MIN; |
0a7de745 | 535 | } |
2d21ac55 | 536 | |
5ba3f43e A |
537 | vmk_flags = VM_MAP_KERNEL_FLAGS_NONE; |
538 | vmk_flags.vmkf_permanent = TRUE; | |
539 | ||
1c79356b | 540 | retval = kmem_suballoc(kernel_map, &min, kalloc_map_size, |
f427ee49 A |
541 | FALSE, VM_FLAGS_ANYWHERE, vmk_flags, |
542 | VM_KERN_MEMORY_KALLOC, &kalloc_map); | |
91447636 | 543 | |
0a7de745 | 544 | if (retval != KERN_SUCCESS) { |
1c79356b | 545 | panic("kalloc_init: kmem_suballoc failed"); |
0a7de745 | 546 | } |
1c79356b | 547 | |
b0d623f7 A |
548 | kalloc_map_min = min; |
549 | kalloc_map_max = min + kalloc_map_size - 1; | |
550 | ||
f427ee49 A |
551 | struct kheap_zones *khz_default = &kalloc_zones_default; |
552 | kalloc_max = (khz_default->cfg[khz_default->max_k_zone - 1].kzc_size << 1); | |
3e170ce0 | 553 | if (kalloc_max < KiB(16)) { |
0a7de745 | 554 | kalloc_max = KiB(16); |
3e170ce0 A |
555 | } |
556 | assert(kalloc_max <= KiB(64)); /* assumption made in size arrays */ | |
1c79356b | 557 | |
1c79356b | 558 | kalloc_max_prerounded = kalloc_max / 2 + 1; |
3e170ce0 | 559 | /* allocations larger than 16 times kalloc_max go directly to kernel map */ |
0c530ab8 | 560 | kalloc_kernmap_size = (kalloc_max * 16) + 1; |
b0d623f7 | 561 | kalloc_largest_allocated = kalloc_kernmap_size; |
1c79356b | 562 | |
f427ee49 A |
563 | /* Initialize kalloc default heap */ |
564 | kalloc_zones_init(&kalloc_zones_default); | |
a39ff7e2 | 565 | |
f427ee49 A |
566 | /* Initialize kalloc data buffers heap */ |
567 | if (ZSECURITY_OPTIONS_SUBMAP_USER_DATA & zsecurity_options) { | |
568 | kalloc_zones_init(&kalloc_zones_data_buffers); | |
569 | } else { | |
570 | *KHEAP_DATA_BUFFERS = *KHEAP_DEFAULT; | |
1c79356b | 571 | } |
316670eb | 572 | |
f427ee49 A |
573 | /* Initialize kalloc kext heap */ |
574 | if (ZSECURITY_OPTIONS_SEQUESTER_KEXT_KALLOC & zsecurity_options) { | |
575 | kalloc_zones_init(&kalloc_zones_kext); | |
576 | } else { | |
577 | *KHEAP_KEXT = *KHEAP_DEFAULT; | |
316670eb | 578 | } |
f427ee49 A |
579 | } |
580 | STARTUP(ZALLOC, STARTUP_RANK_THIRD, kalloc_init); | |
316670eb | 581 | |
316670eb | 582 | |
f427ee49 | 583 | #pragma mark accessors |
316670eb | 584 | |
f427ee49 A |
585 | static void |
586 | KALLOC_ZINFO_SALLOC(vm_size_t bytes) | |
587 | { | |
588 | thread_t thr = current_thread(); | |
589 | ledger_debit_thread(thr, thr->t_ledger, task_ledgers.tkm_shared, bytes); | |
590 | } | |
316670eb | 591 | |
f427ee49 A |
592 | static void |
593 | KALLOC_ZINFO_SFREE(vm_size_t bytes) | |
594 | { | |
595 | thread_t thr = current_thread(); | |
596 | ledger_credit_thread(thr, thr->t_ledger, task_ledgers.tkm_shared, bytes); | |
597 | } | |
316670eb | 598 | |
f427ee49 A |
599 | static inline vm_map_t |
600 | kalloc_map_for_addr(vm_address_t addr) | |
601 | { | |
602 | if (addr >= kalloc_map_min && addr < kalloc_map_max) { | |
603 | return kalloc_map; | |
316670eb | 604 | } |
f427ee49 | 605 | return kernel_map; |
316670eb | 606 | } |
6d2010ae | 607 | |
f427ee49 A |
608 | static inline vm_map_t |
609 | kalloc_map_for_size(vm_size_t size) | |
316670eb | 610 | { |
f427ee49 A |
611 | if (size < kalloc_kernmap_size) { |
612 | return kalloc_map; | |
613 | } | |
614 | return kernel_map; | |
316670eb A |
615 | } |
616 | ||
f427ee49 A |
617 | zone_t |
618 | kalloc_heap_zone_for_size(kalloc_heap_t kheap, vm_size_t size) | |
316670eb | 619 | { |
f427ee49 | 620 | struct kheap_zones *khz = kheap->kh_zones; |
316670eb | 621 | |
f427ee49 A |
622 | if (size < MAX_SIZE_ZDLUT) { |
623 | uint32_t zindex = khz->dlut[INDEX_ZDLUT(size)]; | |
624 | return khz->k_zone[zindex]; | |
0a7de745 | 625 | } |
316670eb | 626 | |
f427ee49 A |
627 | if (size < kalloc_max_prerounded) { |
628 | uint32_t zindex = khz->k_zindex_start; | |
629 | while (khz->cfg[zindex].kzc_size < size) { | |
630 | zindex++; | |
631 | } | |
632 | assert(zindex < khz->max_k_zone); | |
633 | return khz->k_zone[zindex]; | |
634 | } | |
316670eb | 635 | |
f427ee49 | 636 | return ZONE_NULL; |
1c79356b A |
637 | } |
638 | ||
39037602 | 639 | static vm_size_t |
f427ee49 | 640 | vm_map_lookup_kalloc_entry_locked(vm_map_t map, void *addr) |
39037602 | 641 | { |
f427ee49 | 642 | vm_map_entry_t vm_entry = NULL; |
0a7de745 | 643 | |
f427ee49 A |
644 | if (!vm_map_lookup_entry(map, (vm_map_offset_t)addr, &vm_entry)) { |
645 | panic("address %p not allocated via kalloc, map %p", | |
646 | addr, map); | |
39037602 A |
647 | } |
648 | if (vm_entry->vme_start != (vm_map_offset_t)addr) { | |
f427ee49 A |
649 | panic("address %p inside vm entry %p [%p:%p), map %p", |
650 | addr, vm_entry, (void *)vm_entry->vme_start, | |
651 | (void *)vm_entry->vme_end, map); | |
39037602 A |
652 | } |
653 | if (!vm_entry->vme_atomic) { | |
f427ee49 A |
654 | panic("address %p not managed by kalloc (entry %p, map %p)", |
655 | addr, vm_entry, map); | |
39037602 | 656 | } |
0a7de745 | 657 | return vm_entry->vme_end - vm_entry->vme_start; |
39037602 A |
658 | } |
659 | ||
5ba3f43e A |
660 | #if KASAN_KALLOC |
661 | /* | |
662 | * KASAN kalloc stashes the original user-requested size away in the poisoned | |
663 | * area. Return that directly. | |
664 | */ | |
665 | vm_size_t | |
666 | kalloc_size(void *addr) | |
667 | { | |
668 | (void)vm_map_lookup_kalloc_entry_locked; /* silence warning */ | |
669 | return kasan_user_size((vm_offset_t)addr); | |
670 | } | |
671 | #else | |
39037602 | 672 | vm_size_t |
f427ee49 | 673 | kalloc_size(void *addr) |
39037602 | 674 | { |
f427ee49 A |
675 | vm_map_t map; |
676 | vm_size_t size; | |
39037602 A |
677 | |
678 | size = zone_element_size(addr, NULL); | |
679 | if (size) { | |
680 | return size; | |
681 | } | |
f427ee49 A |
682 | |
683 | map = kalloc_map_for_addr((vm_offset_t)addr); | |
39037602 A |
684 | vm_map_lock_read(map); |
685 | size = vm_map_lookup_kalloc_entry_locked(map, addr); | |
686 | vm_map_unlock_read(map); | |
687 | return size; | |
688 | } | |
5ba3f43e | 689 | #endif |
39037602 A |
690 | |
691 | vm_size_t | |
f427ee49 | 692 | kalloc_bucket_size(vm_size_t size) |
39037602 | 693 | { |
f427ee49 A |
694 | zone_t z = kalloc_heap_zone_for_size(KHEAP_DEFAULT, size); |
695 | vm_map_t map = kalloc_map_for_size(size); | |
0a7de745 | 696 | |
f427ee49 A |
697 | if (z) { |
698 | return zone_elem_size(z); | |
0a7de745 | 699 | } |
f427ee49 A |
700 | return vm_map_round_page(size, VM_MAP_PAGE_MASK(map)); |
701 | } | |
0a7de745 | 702 | |
f427ee49 | 703 | #pragma mark kalloc |
39037602 | 704 | |
f427ee49 A |
705 | void |
706 | kheap_temp_leak_panic(thread_t self) | |
707 | { | |
708 | #if DEBUG || DEVELOPMENT | |
709 | if (__improbable(kheap_temp_debug)) { | |
710 | struct kheap_temp_header *hdr = qe_dequeue_head(&self->t_temp_alloc_list, | |
711 | struct kheap_temp_header, kht_hdr_link); | |
712 | ||
713 | panic_plain("KHEAP_TEMP leak on thread %p (%d), allocated at:\n" | |
714 | " %#016lx\n" " %#016lx\n" " %#016lx\n" " %#016lx\n" | |
715 | " %#016lx\n" " %#016lx\n" " %#016lx\n" " %#016lx\n" | |
716 | " %#016lx\n" " %#016lx\n" " %#016lx\n" " %#016lx\n" | |
717 | " %#016lx\n" " %#016lx\n", | |
718 | self, self->t_temp_alloc_count, | |
719 | hdr->kht_hdr_pcs[0], hdr->kht_hdr_pcs[1], | |
720 | hdr->kht_hdr_pcs[2], hdr->kht_hdr_pcs[3], | |
721 | hdr->kht_hdr_pcs[4], hdr->kht_hdr_pcs[5], | |
722 | hdr->kht_hdr_pcs[6], hdr->kht_hdr_pcs[7], | |
723 | hdr->kht_hdr_pcs[8], hdr->kht_hdr_pcs[9], | |
724 | hdr->kht_hdr_pcs[10], hdr->kht_hdr_pcs[11], | |
725 | hdr->kht_hdr_pcs[12], hdr->kht_hdr_pcs[13]); | |
0a7de745 | 726 | } |
f427ee49 A |
727 | panic("KHEAP_TEMP leak on thread %p (%d) " |
728 | "(boot with kheap_temp_debug=1 to debug)", | |
729 | self, self->t_temp_alloc_count); | |
730 | #else /* !DEBUG && !DEVELOPMENT */ | |
731 | panic("KHEAP_TEMP leak on thread %p (%d)", | |
732 | self, self->t_temp_alloc_count); | |
733 | #endif /* !DEBUG && !DEVELOPMENT */ | |
39037602 A |
734 | } |
735 | ||
f427ee49 A |
736 | __abortlike |
737 | static void | |
738 | kheap_temp_overuse_panic(thread_t self) | |
5ba3f43e | 739 | { |
f427ee49 A |
740 | panic("too many KHEAP_TEMP allocations in flight: %d", |
741 | self->t_temp_alloc_count); | |
5ba3f43e | 742 | } |
39037602 | 743 | |
f427ee49 A |
744 | __attribute__((noinline)) |
745 | static struct kalloc_result | |
746 | kalloc_large( | |
747 | kalloc_heap_t kheap, | |
748 | vm_size_t req_size, | |
749 | vm_size_t size, | |
750 | zalloc_flags_t flags, | |
751 | vm_allocation_site_t *site) | |
752 | { | |
753 | int kma_flags = KMA_ATOMIC | KMA_KOBJECT; | |
754 | vm_tag_t tag = VM_KERN_MEMORY_KALLOC; | |
755 | vm_map_t alloc_map; | |
756 | vm_offset_t addr; | |
757 | ||
758 | if (flags & Z_NOFAIL) { | |
759 | panic("trying to kalloc(Z_NOFAIL) with a large size (%zd)", | |
760 | (size_t)size); | |
761 | } | |
762 | /* kmem_alloc could block so we return if noblock */ | |
763 | if (flags & Z_NOWAIT) { | |
764 | return (struct kalloc_result){ }; | |
39037602 A |
765 | } |
766 | ||
f427ee49 A |
767 | if (flags & Z_NOPAGEWAIT) { |
768 | kma_flags |= KMA_NOPAGEWAIT; | |
39037602 | 769 | } |
f427ee49 A |
770 | if (flags & Z_ZERO) { |
771 | kma_flags |= KMA_ZERO; | |
39037602 A |
772 | } |
773 | ||
f427ee49 A |
774 | #if KASAN_KALLOC |
775 | /* large allocation - use guard pages instead of small redzones */ | |
776 | size = round_page(req_size + 2 * PAGE_SIZE); | |
777 | assert(size >= MAX_SIZE_ZDLUT && size >= kalloc_max_prerounded); | |
778 | #else | |
779 | size = round_page(size); | |
5ba3f43e A |
780 | #endif |
781 | ||
f427ee49 | 782 | alloc_map = kalloc_map_for_size(size); |
39037602 | 783 | |
f427ee49 A |
784 | if (site) { |
785 | tag = vm_tag_alloc(site); | |
786 | } | |
316670eb | 787 | |
f427ee49 A |
788 | if (kmem_alloc_flags(alloc_map, &addr, size, tag, kma_flags) != KERN_SUCCESS) { |
789 | if (alloc_map != kernel_map) { | |
790 | if (kalloc_fallback_count++ == 0) { | |
791 | printf("%s: falling back to kernel_map\n", __func__); | |
792 | } | |
793 | if (kmem_alloc_flags(kernel_map, &addr, size, tag, kma_flags) != KERN_SUCCESS) { | |
794 | addr = 0; | |
795 | } | |
796 | } else { | |
797 | addr = 0; | |
798 | } | |
799 | } | |
5ba3f43e | 800 | |
f427ee49 A |
801 | if (addr != 0) { |
802 | kalloc_spin_lock(); | |
316670eb | 803 | /* |
f427ee49 A |
804 | * Thread-safe version of the workaround for 4740071 |
805 | * (a double FREE()) | |
316670eb | 806 | */ |
f427ee49 A |
807 | if (size > kalloc_largest_allocated) { |
808 | kalloc_largest_allocated = size; | |
809 | } | |
810 | ||
811 | kalloc_large_inuse++; | |
812 | assert(kalloc_large_total + size >= kalloc_large_total); /* no wrap around */ | |
813 | kalloc_large_total += size; | |
814 | kalloc_large_sum += size; | |
1c79356b | 815 | |
f427ee49 A |
816 | if (kalloc_large_total > kalloc_large_max) { |
817 | kalloc_large_max = kalloc_large_total; | |
1c79356b | 818 | } |
0c530ab8 | 819 | |
f427ee49 A |
820 | kalloc_unlock(); |
821 | ||
822 | KALLOC_ZINFO_SALLOC(size); | |
823 | } | |
5ba3f43e | 824 | #if KASAN_KALLOC |
f427ee49 A |
825 | /* fixup the return address to skip the redzone */ |
826 | addr = kasan_alloc(addr, size, req_size, PAGE_SIZE); | |
827 | /* | |
828 | * Initialize buffer with unique pattern only if memory | |
829 | * wasn't expected to be zeroed. | |
830 | */ | |
831 | if (!(flags & Z_ZERO)) { | |
832 | kasan_leak_init(addr, req_size); | |
833 | } | |
cb323159 | 834 | #else |
f427ee49 | 835 | req_size = size; |
5ba3f43e A |
836 | #endif |
837 | ||
f427ee49 A |
838 | if (addr && kheap == KHEAP_TEMP) { |
839 | thread_t self = current_thread(); | |
0c530ab8 | 840 | |
f427ee49 A |
841 | if (self->t_temp_alloc_count++ > UINT16_MAX) { |
842 | kheap_temp_overuse_panic(self); | |
0a7de745 | 843 | } |
f427ee49 A |
844 | #if DEBUG || DEVELOPMENT |
845 | if (__improbable(kheap_temp_debug)) { | |
846 | struct kheap_temp_header *hdr = (void *)addr; | |
847 | enqueue_head(&self->t_temp_alloc_list, | |
848 | &hdr->kht_hdr_link); | |
849 | backtrace(hdr->kht_hdr_pcs, KHT_BT_COUNT, NULL); | |
850 | req_size -= sizeof(struct kheap_temp_header); | |
851 | addr += sizeof(struct kheap_temp_header); | |
b0d623f7 | 852 | } |
f427ee49 A |
853 | #endif /* DEBUG || DEVELOPMENT */ |
854 | } | |
1c79356b | 855 | |
f427ee49 A |
856 | DTRACE_VM3(kalloc, vm_size_t, size, vm_size_t, req_size, void*, addr); |
857 | return (struct kalloc_result){ .addr = (void *)addr, .size = req_size }; | |
858 | } | |
6d2010ae | 859 | |
f427ee49 A |
860 | struct kalloc_result |
861 | kalloc_ext( | |
862 | kalloc_heap_t kheap, | |
863 | vm_size_t req_size, | |
864 | zalloc_flags_t flags, | |
865 | vm_allocation_site_t *site) | |
866 | { | |
867 | vm_tag_t tag = VM_KERN_MEMORY_KALLOC; | |
868 | vm_size_t size; | |
869 | void *addr; | |
870 | zone_t z; | |
6d2010ae | 871 | |
f427ee49 A |
872 | #if DEBUG || DEVELOPMENT |
873 | if (__improbable(kheap_temp_debug)) { | |
874 | if (kheap == KHEAP_TEMP) { | |
875 | req_size += sizeof(struct kheap_temp_header); | |
1c79356b | 876 | } |
f427ee49 A |
877 | } |
878 | #endif /* DEBUG || DEVELOPMENT */ | |
879 | ||
880 | /* | |
881 | * Kasan for kalloc heaps will put the redzones *inside* | |
882 | * the allocation, and hence augment its size. | |
883 | * | |
884 | * kalloc heaps do not use zone_t::kasan_redzone. | |
885 | */ | |
5ba3f43e | 886 | #if KASAN_KALLOC |
f427ee49 | 887 | size = kasan_alloc_resize(req_size); |
5ba3f43e | 888 | #else |
f427ee49 | 889 | size = req_size; |
5ba3f43e | 890 | #endif |
f427ee49 A |
891 | z = kalloc_heap_zone_for_size(kheap, size); |
892 | if (__improbable(z == ZONE_NULL)) { | |
893 | return kalloc_large(kheap, req_size, size, flags, site); | |
1c79356b | 894 | } |
f427ee49 | 895 | |
316670eb | 896 | #ifdef KALLOC_DEBUG |
f427ee49 A |
897 | if (size > zone_elem_size(z)) { |
898 | panic("%s: z %p (%s%s) but requested size %lu", __func__, z, | |
899 | kalloc_heap_names[kheap->kh_zones->heap_id], z->z_name, | |
900 | (unsigned long)size); | |
0a7de745 | 901 | } |
316670eb | 902 | #endif |
f427ee49 | 903 | assert(size <= zone_elem_size(z)); |
5ba3f43e A |
904 | |
905 | #if VM_MAX_TAG_ZONES | |
0a7de745 | 906 | if (z->tags && site) { |
5ba3f43e | 907 | tag = vm_tag_alloc(site); |
f427ee49 | 908 | if ((flags & (Z_NOWAIT | Z_NOPAGEWAIT)) && !vm_allocation_zone_totals[tag]) { |
0a7de745 A |
909 | tag = VM_KERN_MEMORY_KALLOC; |
910 | } | |
911 | } | |
5ba3f43e | 912 | #endif |
f427ee49 A |
913 | addr = zalloc_ext(z, kheap->kh_stats ?: z->z_stats, |
914 | flags | Z_VM_TAG(tag), zone_elem_size(z) - size); | |
5ba3f43e A |
915 | |
916 | #if KASAN_KALLOC | |
f427ee49 A |
917 | addr = (void *)kasan_alloc((vm_offset_t)addr, zone_elem_size(z), |
918 | req_size, KASAN_GUARD_SIZE); | |
5ba3f43e | 919 | #else |
f427ee49 | 920 | req_size = zone_elem_size(z); |
5ba3f43e A |
921 | #endif |
922 | ||
f427ee49 A |
923 | if (addr && kheap == KHEAP_TEMP) { |
924 | thread_t self = current_thread(); | |
925 | ||
926 | if (self->t_temp_alloc_count++ > UINT16_MAX) { | |
927 | kheap_temp_overuse_panic(self); | |
928 | } | |
929 | #if DEBUG || DEVELOPMENT | |
930 | if (__improbable(kheap_temp_debug)) { | |
931 | struct kheap_temp_header *hdr = (void *)addr; | |
932 | enqueue_head(&self->t_temp_alloc_list, | |
933 | &hdr->kht_hdr_link); | |
934 | backtrace(hdr->kht_hdr_pcs, KHT_BT_COUNT, NULL); | |
935 | req_size -= sizeof(struct kheap_temp_header); | |
936 | addr += sizeof(struct kheap_temp_header); | |
937 | } | |
938 | #endif /* DEBUG || DEVELOPMENT */ | |
939 | } | |
940 | ||
941 | DTRACE_VM3(kalloc, vm_size_t, size, vm_size_t, req_size, void*, addr); | |
942 | return (struct kalloc_result){ .addr = addr, .size = req_size }; | |
1c79356b A |
943 | } |
944 | ||
91447636 | 945 | void * |
f427ee49 | 946 | kalloc_external(vm_size_t size); |
91447636 | 947 | void * |
f427ee49 | 948 | kalloc_external(vm_size_t size) |
1c79356b | 949 | { |
f427ee49 | 950 | return kheap_alloc_tag_bt(KHEAP_KEXT, size, Z_WAITOK, VM_KERN_MEMORY_KALLOC); |
1c79356b A |
951 | } |
952 | ||
f427ee49 A |
953 | |
954 | #pragma mark kfree | |
955 | ||
956 | __attribute__((noinline)) | |
957 | static void | |
958 | kfree_large(vm_offset_t addr, vm_size_t size) | |
1c79356b | 959 | { |
f427ee49 A |
960 | vm_map_t map = kalloc_map_for_addr(addr); |
961 | kern_return_t ret; | |
962 | vm_offset_t end; | |
963 | ||
964 | if (addr < VM_MIN_KERNEL_AND_KEXT_ADDRESS || | |
965 | os_add_overflow(addr, size, &end) || | |
966 | end > VM_MAX_KERNEL_ADDRESS) { | |
967 | panic("kfree: address range (%p, %ld) doesn't belong to the kernel", | |
968 | (void *)addr, (uintptr_t)size); | |
969 | } | |
970 | ||
971 | if (size == 0) { | |
972 | vm_map_lock(map); | |
973 | size = vm_map_lookup_kalloc_entry_locked(map, (void *)addr); | |
974 | ret = vm_map_remove_locked(map, | |
975 | vm_map_trunc_page(addr, VM_MAP_PAGE_MASK(map)), | |
976 | vm_map_round_page(addr + size, VM_MAP_PAGE_MASK(map)), | |
977 | VM_MAP_REMOVE_KUNWIRE); | |
978 | if (ret != KERN_SUCCESS) { | |
979 | panic("kfree: vm_map_remove_locked() failed for " | |
980 | "addr: %p, map: %p ret: %d", (void *)addr, map, ret); | |
981 | } | |
982 | vm_map_unlock(map); | |
983 | } else { | |
984 | size = round_page(size); | |
985 | ||
986 | if (size > kalloc_largest_allocated) { | |
987 | panic("kfree: size %lu > kalloc_largest_allocated %lu", | |
988 | (uintptr_t)size, (uintptr_t)kalloc_largest_allocated); | |
989 | } | |
990 | kmem_free(map, addr, size); | |
991 | } | |
992 | ||
993 | kalloc_spin_lock(); | |
994 | ||
995 | assert(kalloc_large_total >= size); | |
996 | kalloc_large_total -= size; | |
997 | kalloc_large_inuse--; | |
998 | ||
999 | kalloc_unlock(); | |
1000 | ||
1001 | #if !KASAN_KALLOC | |
1002 | DTRACE_VM3(kfree, vm_size_t, size, vm_size_t, size, void*, addr); | |
1003 | #endif | |
1004 | ||
1005 | KALLOC_ZINFO_SFREE(size); | |
1006 | return; | |
1007 | } | |
1008 | ||
1009 | __abortlike | |
1010 | static void | |
1011 | kfree_heap_confusion_panic(kalloc_heap_t kheap, void *data, size_t size, zone_t z) | |
1012 | { | |
1013 | if (z->kalloc_heap == KHEAP_ID_NONE) { | |
1014 | panic("kfree: addr %p, size %zd found in regular zone '%s%s'", | |
1015 | data, size, zone_heap_name(z), z->z_name); | |
1016 | } else { | |
1017 | panic("kfree: addr %p, size %zd found in heap %s* instead of %s*", | |
1018 | data, size, zone_heap_name(z), | |
1019 | kalloc_heap_names[kheap->kh_heap_id]); | |
1020 | } | |
1021 | } | |
1022 | ||
1023 | __abortlike | |
1024 | static void | |
1025 | kfree_size_confusion_panic(zone_t z, void *data, size_t size, size_t zsize) | |
1026 | { | |
1027 | if (z) { | |
1028 | panic("kfree: addr %p, size %zd found in zone '%s%s' " | |
1029 | "with elem_size %zd", | |
1030 | data, size, zone_heap_name(z), z->z_name, zsize); | |
1031 | } else { | |
1032 | panic("kfree: addr %p, size %zd not found in any zone", | |
1033 | data, size); | |
1034 | } | |
1035 | } | |
1036 | ||
1037 | __abortlike | |
1038 | static void | |
1039 | kfree_size_invalid_panic(void *data, size_t size) | |
1040 | { | |
1041 | panic("kfree: addr %p trying to free with nonsensical size %zd", | |
1042 | data, size); | |
1043 | } | |
1044 | ||
1045 | __abortlike | |
1046 | static void | |
1047 | krealloc_size_invalid_panic(void *data, size_t size) | |
1048 | { | |
1049 | panic("krealloc: addr %p trying to free with nonsensical size %zd", | |
1050 | data, size); | |
1051 | } | |
1052 | ||
1053 | __abortlike | |
1054 | static void | |
1055 | kfree_temp_imbalance_panic(void *data, size_t size) | |
1056 | { | |
1057 | panic("kfree: KHEAP_TEMP allocation imbalance freeing addr %p, size %zd", | |
1058 | data, size); | |
1059 | } | |
1060 | ||
1061 | /* used to implement kheap_free_addr() */ | |
1062 | #define KFREE_UNKNOWN_SIZE ((vm_size_t)~0) | |
1063 | #define KFREE_ABSURD_SIZE \ | |
1064 | ((VM_MAX_KERNEL_ADDRESS - VM_MIN_KERNEL_AND_KEXT_ADDRESS) / 2) | |
1065 | ||
1066 | static void | |
1067 | kfree_ext(kalloc_heap_t kheap, void *data, vm_size_t size) | |
1068 | { | |
1069 | zone_stats_t zs = NULL; | |
316670eb | 1070 | zone_t z; |
f427ee49 A |
1071 | vm_size_t zsize; |
1072 | ||
1073 | if (__improbable(data == NULL)) { | |
1074 | return; | |
1075 | } | |
1076 | ||
1077 | if (kheap == KHEAP_TEMP) { | |
1078 | assert(size != KFREE_UNKNOWN_SIZE); | |
1079 | if (current_thread()->t_temp_alloc_count-- == 0) { | |
1080 | kfree_temp_imbalance_panic(data, size); | |
1081 | } | |
1082 | #if DEBUG || DEVELOPMENT | |
1083 | if (__improbable(kheap_temp_debug)) { | |
1084 | size += sizeof(struct kheap_temp_header); | |
1085 | data -= sizeof(struct kheap_temp_header); | |
1086 | remqueue(&((struct kheap_temp_header *)data)->kht_hdr_link); | |
1087 | } | |
1088 | #endif /* DEBUG || DEVELOPMENT */ | |
1089 | } | |
316670eb | 1090 | |
5ba3f43e A |
1091 | #if KASAN_KALLOC |
1092 | /* | |
1093 | * Resize back to the real allocation size and hand off to the KASan | |
1094 | * quarantine. `data` may then point to a different allocation. | |
1095 | */ | |
1096 | vm_size_t user_size = size; | |
f427ee49 A |
1097 | if (size == KFREE_UNKNOWN_SIZE) { |
1098 | user_size = size = kalloc_size(data); | |
1099 | } | |
5ba3f43e A |
1100 | kasan_check_free((vm_address_t)data, size, KASAN_HEAP_KALLOC); |
1101 | data = (void *)kasan_dealloc((vm_address_t)data, &size); | |
1102 | kasan_free(&data, &size, KASAN_HEAP_KALLOC, NULL, user_size, true); | |
1103 | if (!data) { | |
1104 | return; | |
1105 | } | |
1106 | #endif | |
1107 | ||
f427ee49 A |
1108 | if (size >= kalloc_max_prerounded && size != KFREE_UNKNOWN_SIZE) { |
1109 | return kfree_large((vm_offset_t)data, size); | |
1110 | } | |
1c79356b | 1111 | |
f427ee49 A |
1112 | zsize = zone_element_size(data, &z); |
1113 | if (size == KFREE_UNKNOWN_SIZE) { | |
1114 | if (zsize == 0) { | |
1115 | return kfree_large((vm_offset_t)data, 0); | |
b0d623f7 | 1116 | } |
f427ee49 A |
1117 | size = zsize; |
1118 | } else if (size > zsize) { | |
1119 | kfree_size_confusion_panic(z, data, size, zsize); | |
1120 | } | |
1c79356b | 1121 | |
f427ee49 A |
1122 | if (kheap != KHEAP_ANY) { |
1123 | if (kheap->kh_heap_id != z->kalloc_heap) { | |
1124 | kfree_heap_confusion_panic(kheap, data, size, z); | |
1125 | } | |
1126 | zs = kheap->kh_stats; | |
1127 | } else if (z->kalloc_heap != KHEAP_ID_DEFAULT && | |
1128 | z->kalloc_heap != KHEAP_ID_KEXT) { | |
1129 | kfree_heap_confusion_panic(kheap, data, size, z); | |
1130 | } | |
6d2010ae | 1131 | |
a39ff7e2 | 1132 | #if !KASAN_KALLOC |
f427ee49 | 1133 | DTRACE_VM3(kfree, vm_size_t, size, vm_size_t, zsize, void*, data); |
a39ff7e2 | 1134 | #endif |
f427ee49 A |
1135 | zfree_ext(z, zs ?: z->z_stats, data); |
1136 | } | |
a39ff7e2 | 1137 | |
f427ee49 A |
1138 | void |
1139 | (kfree)(void *addr, vm_size_t size) | |
1140 | { | |
1141 | if (size > KFREE_ABSURD_SIZE) { | |
1142 | kfree_size_invalid_panic(addr, size); | |
1c79356b | 1143 | } |
f427ee49 A |
1144 | kfree_ext(KHEAP_ANY, addr, size); |
1145 | } | |
1c79356b | 1146 | |
f427ee49 A |
1147 | void |
1148 | (kheap_free)(kalloc_heap_t kheap, void *addr, vm_size_t size) | |
1149 | { | |
1150 | if (size > KFREE_ABSURD_SIZE) { | |
1151 | kfree_size_invalid_panic(addr, size); | |
0a7de745 | 1152 | } |
f427ee49 | 1153 | kfree_ext(kheap, addr, size); |
1c79356b A |
1154 | } |
1155 | ||
f427ee49 A |
1156 | void |
1157 | (kheap_free_addr)(kalloc_heap_t kheap, void *addr) | |
1c79356b | 1158 | { |
f427ee49 A |
1159 | kfree_ext(kheap, addr, KFREE_UNKNOWN_SIZE); |
1160 | } | |
1161 | ||
1162 | static struct kalloc_result | |
1163 | _krealloc_ext( | |
1164 | kalloc_heap_t kheap, | |
1165 | void *addr, | |
1166 | vm_size_t old_size, | |
1167 | vm_size_t new_size, | |
1168 | zalloc_flags_t flags, | |
1169 | vm_allocation_site_t *site) | |
1170 | { | |
1171 | vm_size_t old_bucket_size, new_bucket_size, min_size; | |
1172 | struct kalloc_result kr; | |
1173 | ||
1174 | if (new_size == 0) { | |
1175 | kfree_ext(kheap, addr, old_size); | |
1176 | return (struct kalloc_result){ }; | |
0a7de745 | 1177 | } |
f427ee49 A |
1178 | |
1179 | if (addr == NULL) { | |
1180 | return kalloc_ext(kheap, new_size, flags, site); | |
0a7de745 | 1181 | } |
f427ee49 A |
1182 | |
1183 | /* | |
1184 | * Find out the size of the bucket in which the new sized allocation | |
1185 | * would land. If it matches the bucket of the original allocation, | |
1186 | * simply return the same address. | |
1187 | */ | |
1188 | new_bucket_size = kalloc_bucket_size(new_size); | |
1189 | if (old_size == KFREE_UNKNOWN_SIZE) { | |
1190 | old_size = old_bucket_size = kalloc_size(addr); | |
1191 | } else { | |
1192 | old_bucket_size = kalloc_bucket_size(old_size); | |
1193 | } | |
1194 | min_size = MIN(old_size, new_size); | |
1195 | ||
1196 | if (old_bucket_size == new_bucket_size) { | |
1197 | kr.addr = addr; | |
1198 | #if KASAN_KALLOC | |
1199 | kr.size = new_size; | |
1200 | #else | |
1201 | kr.size = new_bucket_size; | |
1c79356b | 1202 | #endif |
f427ee49 A |
1203 | } else { |
1204 | kr = kalloc_ext(kheap, new_size, flags & ~Z_ZERO, site); | |
1205 | if (kr.addr == NULL) { | |
1206 | return kr; | |
1207 | } | |
1c79356b | 1208 | |
f427ee49 A |
1209 | memcpy(kr.addr, addr, min_size); |
1210 | kfree_ext(kheap, addr, old_size); | |
1211 | } | |
1212 | if ((flags & Z_ZERO) && kr.size > min_size) { | |
1213 | bzero(kr.addr + min_size, kr.size - min_size); | |
1214 | } | |
1215 | return kr; | |
1216 | } | |
1217 | ||
1218 | struct kalloc_result | |
1219 | krealloc_ext( | |
1220 | kalloc_heap_t kheap, | |
1221 | void *addr, | |
1222 | vm_size_t old_size, | |
1223 | vm_size_t new_size, | |
1224 | zalloc_flags_t flags, | |
1225 | vm_allocation_site_t *site) | |
1226 | { | |
1227 | if (old_size > KFREE_ABSURD_SIZE) { | |
1228 | krealloc_size_invalid_panic(addr, old_size); | |
1229 | } | |
1230 | return _krealloc_ext(kheap, addr, old_size, new_size, flags, site); | |
1231 | } | |
1232 | ||
1233 | struct kalloc_result | |
1234 | kheap_realloc_addr( | |
1235 | kalloc_heap_t kheap, | |
1236 | void *addr, | |
1237 | vm_size_t size, | |
1238 | zalloc_flags_t flags, | |
1239 | vm_allocation_site_t *site) | |
1240 | { | |
1241 | return _krealloc_ext(kheap, addr, KFREE_UNKNOWN_SIZE, size, flags, site); | |
1242 | } | |
1243 | ||
1244 | __startup_func | |
91447636 | 1245 | void |
f427ee49 | 1246 | kheap_startup_init(kalloc_heap_t kheap) |
91447636 | 1247 | { |
f427ee49 A |
1248 | struct kheap_zones *zones; |
1249 | ||
1250 | switch (kheap->kh_heap_id) { | |
1251 | case KHEAP_ID_DEFAULT: | |
1252 | zones = KHEAP_DEFAULT->kh_zones; | |
1253 | break; | |
1254 | case KHEAP_ID_DATA_BUFFERS: | |
1255 | zones = KHEAP_DATA_BUFFERS->kh_zones; | |
1256 | break; | |
1257 | case KHEAP_ID_KEXT: | |
1258 | zones = KHEAP_KEXT->kh_zones; | |
1259 | break; | |
1260 | default: | |
1261 | panic("kalloc_heap_startup_init: invalid KHEAP_ID: %d", | |
1262 | kheap->kh_heap_id); | |
1263 | } | |
1264 | ||
1265 | kheap->kh_heap_id = zones->heap_id; | |
1266 | kheap->kh_zones = zones; | |
1267 | kheap->kh_stats = zalloc_percpu_permanent_type(struct zone_stats); | |
1268 | kheap->kh_next = zones->views; | |
1269 | zones->views = kheap; | |
6d2010ae | 1270 | |
f427ee49 | 1271 | zone_view_count += 1; |
91447636 A |
1272 | } |
1273 | ||
f427ee49 A |
1274 | #pragma mark OSMalloc |
1275 | /* | |
1276 | * This is a deprecated interface, here only for legacy reasons. | |
1277 | * There is no internal variant of any of these symbols on purpose. | |
1278 | */ | |
1279 | #define OSMallocDeprecated | |
1280 | #include <libkern/OSMalloc.h> | |
1281 | ||
1282 | static KALLOC_HEAP_DEFINE(OSMALLOC, "osmalloc", KHEAP_ID_KEXT); | |
1283 | static queue_head_t OSMalloc_tag_list = QUEUE_HEAD_INITIALIZER(OSMalloc_tag_list); | |
1284 | static LCK_GRP_DECLARE(OSMalloc_tag_lck_grp, "OSMalloc_tag"); | |
1285 | static LCK_SPIN_DECLARE(OSMalloc_tag_lock, &OSMalloc_tag_lck_grp); | |
1286 | ||
1287 | #define OSMalloc_tag_spin_lock() lck_spin_lock(&OSMalloc_tag_lock) | |
1288 | #define OSMalloc_tag_unlock() lck_spin_unlock(&OSMalloc_tag_lock) | |
1289 | ||
1290 | extern typeof(OSMalloc_Tagalloc) OSMalloc_Tagalloc_external; | |
91447636 | 1291 | OSMallocTag |
f427ee49 | 1292 | OSMalloc_Tagalloc_external(const char *str, uint32_t flags) |
91447636 | 1293 | { |
f427ee49 | 1294 | OSMallocTag OSMTag; |
91447636 | 1295 | |
f427ee49 | 1296 | OSMTag = kheap_alloc(OSMALLOC, sizeof(*OSMTag), Z_WAITOK | Z_ZERO); |
91447636 | 1297 | |
0a7de745 | 1298 | if (flags & OSMT_PAGEABLE) { |
91447636 | 1299 | OSMTag->OSMT_attr = OSMT_ATTR_PAGEABLE; |
0a7de745 | 1300 | } |
91447636 A |
1301 | |
1302 | OSMTag->OSMT_refcnt = 1; | |
1303 | ||
3e170ce0 | 1304 | strlcpy(OSMTag->OSMT_name, str, OSMT_MAX_NAME); |
91447636 | 1305 | |
6d2010ae | 1306 | OSMalloc_tag_spin_lock(); |
91447636 | 1307 | enqueue_tail(&OSMalloc_tag_list, (queue_entry_t)OSMTag); |
6d2010ae | 1308 | OSMalloc_tag_unlock(); |
91447636 | 1309 | OSMTag->OSMT_state = OSMT_VALID; |
0a7de745 | 1310 | return OSMTag; |
91447636 A |
1311 | } |
1312 | ||
f427ee49 A |
1313 | static void |
1314 | OSMalloc_Tagref(OSMallocTag tag) | |
91447636 | 1315 | { |
0a7de745 | 1316 | if (!((tag->OSMT_state & OSMT_VALID_MASK) == OSMT_VALID)) { |
f427ee49 A |
1317 | panic("OSMalloc_Tagref():'%s' has bad state 0x%08X\n", |
1318 | tag->OSMT_name, tag->OSMT_state); | |
0a7de745 | 1319 | } |
91447636 | 1320 | |
cb323159 | 1321 | os_atomic_inc(&tag->OSMT_refcnt, relaxed); |
91447636 A |
1322 | } |
1323 | ||
f427ee49 A |
1324 | static void |
1325 | OSMalloc_Tagrele(OSMallocTag tag) | |
91447636 | 1326 | { |
0a7de745 | 1327 | if (!((tag->OSMT_state & OSMT_VALID_MASK) == OSMT_VALID)) { |
f427ee49 A |
1328 | panic("OSMalloc_Tagref():'%s' has bad state 0x%08X\n", |
1329 | tag->OSMT_name, tag->OSMT_state); | |
0a7de745 | 1330 | } |
91447636 | 1331 | |
f427ee49 A |
1332 | if (os_atomic_dec(&tag->OSMT_refcnt, relaxed) != 0) { |
1333 | return; | |
1334 | } | |
1335 | ||
1336 | if (os_atomic_cmpxchg(&tag->OSMT_state, | |
1337 | OSMT_VALID | OSMT_RELEASED, OSMT_VALID | OSMT_RELEASED, acq_rel)) { | |
1338 | OSMalloc_tag_spin_lock(); | |
1339 | (void)remque((queue_entry_t)tag); | |
1340 | OSMalloc_tag_unlock(); | |
1341 | kheap_free(OSMALLOC, tag, sizeof(*tag)); | |
1342 | } else { | |
1343 | panic("OSMalloc_Tagrele():'%s' has refcnt 0\n", tag->OSMT_name); | |
91447636 A |
1344 | } |
1345 | } | |
1346 | ||
f427ee49 | 1347 | extern typeof(OSMalloc_Tagfree) OSMalloc_Tagfree_external; |
91447636 | 1348 | void |
f427ee49 | 1349 | OSMalloc_Tagfree_external(OSMallocTag tag) |
91447636 | 1350 | { |
f427ee49 A |
1351 | if (!os_atomic_cmpxchg(&tag->OSMT_state, |
1352 | OSMT_VALID, OSMT_VALID | OSMT_RELEASED, acq_rel)) { | |
1353 | panic("OSMalloc_Tagfree():'%s' has bad state 0x%08X \n", | |
1354 | tag->OSMT_name, tag->OSMT_state); | |
0a7de745 | 1355 | } |
91447636 | 1356 | |
cb323159 | 1357 | if (os_atomic_dec(&tag->OSMT_refcnt, relaxed) == 0) { |
6d2010ae | 1358 | OSMalloc_tag_spin_lock(); |
91447636 | 1359 | (void)remque((queue_entry_t)tag); |
6d2010ae | 1360 | OSMalloc_tag_unlock(); |
f427ee49 | 1361 | kheap_free(OSMALLOC, tag, sizeof(*tag)); |
91447636 A |
1362 | } |
1363 | } | |
1364 | ||
f427ee49 | 1365 | extern typeof(OSMalloc) OSMalloc_external; |
91447636 | 1366 | void * |
f427ee49 A |
1367 | OSMalloc_external( |
1368 | uint32_t size, OSMallocTag tag) | |
91447636 | 1369 | { |
f427ee49 | 1370 | void *addr = NULL; |
0a7de745 | 1371 | kern_return_t kr; |
91447636 A |
1372 | |
1373 | OSMalloc_Tagref(tag); | |
f427ee49 A |
1374 | if ((tag->OSMT_attr & OSMT_PAGEABLE) && (size & ~PAGE_MASK)) { |
1375 | if ((kr = kmem_alloc_pageable_external(kernel_map, | |
1376 | (vm_offset_t *)&addr, size)) != KERN_SUCCESS) { | |
2d21ac55 | 1377 | addr = NULL; |
0a7de745 A |
1378 | } |
1379 | } else { | |
f427ee49 A |
1380 | addr = kheap_alloc_tag_bt(OSMALLOC, size, |
1381 | Z_WAITOK, VM_KERN_MEMORY_KALLOC); | |
0a7de745 | 1382 | } |
91447636 | 1383 | |
0a7de745 | 1384 | if (!addr) { |
2d21ac55 | 1385 | OSMalloc_Tagrele(tag); |
0a7de745 | 1386 | } |
2d21ac55 | 1387 | |
0a7de745 | 1388 | return addr; |
91447636 A |
1389 | } |
1390 | ||
f427ee49 | 1391 | extern typeof(OSMalloc_nowait) OSMalloc_nowait_external; |
91447636 | 1392 | void * |
f427ee49 | 1393 | OSMalloc_nowait_external(uint32_t size, OSMallocTag tag) |
91447636 | 1394 | { |
0a7de745 | 1395 | void *addr = NULL; |
91447636 | 1396 | |
0a7de745 A |
1397 | if (tag->OSMT_attr & OSMT_PAGEABLE) { |
1398 | return NULL; | |
1399 | } | |
91447636 A |
1400 | |
1401 | OSMalloc_Tagref(tag); | |
1402 | /* XXX: use non-blocking kalloc for now */ | |
f427ee49 A |
1403 | addr = kheap_alloc_tag_bt(OSMALLOC, (vm_size_t)size, |
1404 | Z_NOWAIT, VM_KERN_MEMORY_KALLOC); | |
0a7de745 | 1405 | if (addr == NULL) { |
91447636 | 1406 | OSMalloc_Tagrele(tag); |
0a7de745 | 1407 | } |
91447636 | 1408 | |
0a7de745 | 1409 | return addr; |
91447636 A |
1410 | } |
1411 | ||
f427ee49 | 1412 | extern typeof(OSMalloc_noblock) OSMalloc_noblock_external; |
91447636 | 1413 | void * |
f427ee49 | 1414 | OSMalloc_noblock_external(uint32_t size, OSMallocTag tag) |
91447636 | 1415 | { |
0a7de745 | 1416 | void *addr = NULL; |
91447636 | 1417 | |
0a7de745 A |
1418 | if (tag->OSMT_attr & OSMT_PAGEABLE) { |
1419 | return NULL; | |
1420 | } | |
91447636 A |
1421 | |
1422 | OSMalloc_Tagref(tag); | |
f427ee49 A |
1423 | addr = kheap_alloc_tag_bt(OSMALLOC, (vm_size_t)size, |
1424 | Z_NOWAIT, VM_KERN_MEMORY_KALLOC); | |
0a7de745 | 1425 | if (addr == NULL) { |
91447636 | 1426 | OSMalloc_Tagrele(tag); |
0a7de745 | 1427 | } |
91447636 | 1428 | |
0a7de745 | 1429 | return addr; |
91447636 A |
1430 | } |
1431 | ||
f427ee49 | 1432 | extern typeof(OSFree) OSFree_external; |
91447636 | 1433 | void |
f427ee49 | 1434 | OSFree_external(void *addr, uint32_t size, OSMallocTag tag) |
91447636 A |
1435 | { |
1436 | if ((tag->OSMT_attr & OSMT_PAGEABLE) | |
1437 | && (size & ~PAGE_MASK)) { | |
1438 | kmem_free(kernel_map, (vm_offset_t)addr, size); | |
0a7de745 | 1439 | } else { |
f427ee49 | 1440 | kheap_free(OSMALLOC, addr, size); |
0a7de745 | 1441 | } |
91447636 A |
1442 | |
1443 | OSMalloc_Tagrele(tag); | |
1444 | } | |
39037602 | 1445 | |
f427ee49 A |
1446 | #pragma mark kern_os_malloc |
1447 | ||
1448 | void * | |
1449 | kern_os_malloc_external(size_t size); | |
1450 | void * | |
1451 | kern_os_malloc_external(size_t size) | |
39037602 | 1452 | { |
f427ee49 A |
1453 | if (size == 0) { |
1454 | return NULL; | |
1455 | } | |
1456 | ||
1457 | return kheap_alloc_tag_bt(KERN_OS_MALLOC, size, Z_WAITOK | Z_ZERO, | |
1458 | VM_KERN_MEMORY_LIBKERN); | |
1459 | } | |
1460 | ||
1461 | void | |
1462 | kern_os_free_external(void *addr); | |
1463 | void | |
1464 | kern_os_free_external(void *addr) | |
1465 | { | |
1466 | kheap_free_addr(KERN_OS_MALLOC, addr); | |
1467 | } | |
1468 | ||
1469 | void * | |
1470 | kern_os_realloc_external(void *addr, size_t nsize); | |
1471 | void * | |
1472 | kern_os_realloc_external(void *addr, size_t nsize) | |
1473 | { | |
1474 | VM_ALLOC_SITE_STATIC(VM_TAG_BT, VM_KERN_MEMORY_LIBKERN); | |
1475 | ||
1476 | return kheap_realloc_addr(KERN_OS_MALLOC, addr, nsize, | |
1477 | Z_WAITOK | Z_ZERO, &site).addr; | |
1478 | } | |
1479 | ||
1480 | void | |
1481 | kern_os_zfree(zone_t zone, void *addr, vm_size_t size) | |
1482 | { | |
1483 | if (zsecurity_options & ZSECURITY_OPTIONS_STRICT_IOKIT_FREE | |
1484 | || zone_owns(zone, addr)) { | |
1485 | zfree(zone, addr); | |
1486 | } else { | |
1487 | /* | |
1488 | * Third party kexts might not know about the operator new | |
1489 | * and be allocated from the KEXT heap | |
1490 | */ | |
1491 | printf("kern_os_zfree: kheap_free called for object from zone %s\n", | |
1492 | zone->z_name); | |
1493 | kheap_free(KHEAP_KEXT, addr, size); | |
1494 | } | |
1495 | } | |
1496 | ||
1497 | void | |
1498 | kern_os_kfree(void *addr, vm_size_t size) | |
1499 | { | |
1500 | if (zsecurity_options & ZSECURITY_OPTIONS_STRICT_IOKIT_FREE) { | |
1501 | kheap_free(KHEAP_DEFAULT, addr, size); | |
1502 | } else { | |
1503 | /* | |
1504 | * Third party kexts may not know about newly added operator | |
1505 | * default new/delete. If they call new for any iokit object | |
1506 | * it will end up coming from the KEXT heap. If these objects | |
1507 | * are freed by calling release() or free(), the internal | |
1508 | * version of operator delete is called and the kernel ends | |
1509 | * up freeing the object to the DEFAULT heap. | |
1510 | */ | |
1511 | kheap_free(KHEAP_ANY, addr, size); | |
1512 | } | |
39037602 | 1513 | } |