]> git.saurik.com Git - apple/xnu.git/blame - osfmk/chud/i386/chud_thread_i386.c
xnu-1228.15.4.tar.gz
[apple/xnu.git] / osfmk / chud / i386 / chud_thread_i386.c
CommitLineData
0c530ab8 1/*
2d21ac55 2 * Copyright (c) 2003-2007 Apple Inc. All rights reserved.
0c530ab8 3 *
2d21ac55
A
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
0c530ab8
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
0c530ab8
A
27 */
28
29#include <mach/mach_types.h>
30#include <mach/task.h>
31#include <mach/thread_act.h>
32
33#include <kern/kern_types.h>
34#include <kern/processor.h>
35#include <kern/thread.h>
36
37#include <vm/vm_map.h>
38#include <vm/pmap.h>
39
40#include <chud/chud_xnu.h>
41#include <chud/chud_xnu_private.h>
42
43#include <i386/misc_protos.h>
44#include <i386/proc_reg.h>
45#include <i386/mp_desc.h>
46
47#pragma mark **** thread state ****
48
49__private_extern__ kern_return_t
50chudxnu_thread_user_state_available(thread_t thread)
51{
52#pragma unused (thread)
53 return KERN_SUCCESS;
54}
55
56__private_extern__ kern_return_t
57chudxnu_thread_get_state(
2d21ac55
A
58 thread_t thread,
59 thread_flavor_t flavor,
60 thread_state_t tstate,
61 mach_msg_type_number_t *count,
62 boolean_t user_only)
0c530ab8
A
63{
64 if (user_only) {
65 /* We can't get user state for kernel threads */
66 if (thread->task == kernel_task)
67 return KERN_FAILURE;
68 /* this properly handles deciding whether or not the thread is 64 bit or not */
69 return machine_thread_get_state(thread, flavor, tstate, count);
70 } else {
71 // i386 machine_thread_get_kern_state() is different from the PPC version which returns
72 // the previous save area - user or kernel - rather than kernel or NULL if no kernel
73 // interrupt state available
2d21ac55 74
0c530ab8
A
75 // the real purpose of this branch is the following:
76 // the user doesn't care if the thread states are user or kernel, he
77 // just wants the thread state, so we need to determine the proper one
78 // to return, kernel or user, for the given thread.
79 if(thread == current_thread() && current_cpu_datap()->cpu_int_state) {
80 // the above are conditions where we possibly can read the kernel
81 // state. we still need to determine if this interrupt happened in
82 // kernel or user context
83 if(USER_STATE(thread) == current_cpu_datap()->cpu_int_state &&
2d21ac55 84 current_cpu_datap()->cpu_interrupt_level == 1) {
0c530ab8
A
85 // interrupt happened in user land
86 return machine_thread_get_state(thread, flavor, tstate, count);
87 } else {
88 // kernel interrupt.
89 return machine_thread_get_kern_state(thread, flavor, tstate, count);
90 }
91 } else {
92 // get the user-mode thread state
93 return machine_thread_get_state(thread, flavor, tstate, count);
94 }
95 }
96}
97
98__private_extern__ kern_return_t
99chudxnu_thread_set_state(
2d21ac55
A
100 thread_t thread,
101 thread_flavor_t flavor,
102 thread_state_t tstate,
103 mach_msg_type_number_t count,
104 boolean_t user_only)
0c530ab8
A
105{
106#pragma unused (user_only)
107 return machine_thread_set_state(thread, flavor, tstate, count);
108}
109
110#pragma mark **** task memory read/write ****
2d21ac55 111
0c530ab8
A
112__private_extern__ kern_return_t
113chudxnu_task_read(
2d21ac55
A
114 task_t task,
115 void *kernaddr,
116 uint64_t usraddr,
117 vm_size_t size)
0c530ab8
A
118{
119 kern_return_t ret = KERN_SUCCESS;
2d21ac55 120 boolean_t old_level;
0c530ab8 121
2d21ac55
A
122 if(ml_at_interrupt_context()) {
123 return KERN_FAILURE; // Can't look at tasks on interrupt stack
124 }
125
126 /*
127 * pmap layer requires interrupts to be on
128 */
129 old_level = ml_set_interrupts_enabled(TRUE);
130
131 if(current_task()==task) {
132
0c530ab8
A
133 if(copyin(usraddr, kernaddr, size)) {
134 ret = KERN_FAILURE;
135 }
136 } else {
137 vm_map_t map = get_task_map(task);
138 ret = vm_map_read_user(map, usraddr, kernaddr, size);
139 }
140
2d21ac55
A
141 ml_set_interrupts_enabled(old_level);
142
0c530ab8
A
143 return ret;
144}
2d21ac55 145
0c530ab8
A
146__private_extern__ kern_return_t
147chudxnu_task_write(
2d21ac55
A
148 task_t task,
149 uint64_t useraddr,
150 void *kernaddr,
151 vm_size_t size)
0c530ab8
A
152{
153 kern_return_t ret = KERN_SUCCESS;
2d21ac55
A
154 boolean_t old_level;
155
156 if(ml_at_interrupt_context()) {
157 return KERN_FAILURE; // can't poke into tasks on interrupt stack
158 }
159
160 /*
161 * pmap layer requires interrupts to be on
162 */
163 old_level = ml_set_interrupts_enabled(TRUE);
0c530ab8 164
2d21ac55
A
165 if(current_task()==task) {
166
0c530ab8
A
167 if(copyout(kernaddr, useraddr, size)) {
168 ret = KERN_FAILURE;
169 }
170 } else {
171 vm_map_t map = get_task_map(task);
172 ret = vm_map_write_user(map, kernaddr, useraddr, size);
173 }
2d21ac55
A
174
175 ml_set_interrupts_enabled(old_level);
176
0c530ab8
A
177 return ret;
178}
179
180__private_extern__ kern_return_t
181chudxnu_kern_read(void *dstaddr, vm_offset_t srcaddr, vm_size_t size)
182{
2d21ac55
A
183 return (ml_nofault_copy(srcaddr, (vm_offset_t) dstaddr, size) == size ?
184 KERN_SUCCESS: KERN_FAILURE);
0c530ab8
A
185}
186
187__private_extern__ kern_return_t
188chudxnu_kern_write(
2d21ac55
A
189 vm_offset_t dstaddr,
190 void *srcaddr,
191 vm_size_t size)
0c530ab8 192{
2d21ac55
A
193 return (ml_nofault_copy((vm_offset_t) srcaddr, dstaddr, size) == size ?
194 KERN_SUCCESS: KERN_FAILURE);
0c530ab8
A
195}
196
197#define VALID_STACK_ADDRESS(supervisor, addr, minKernAddr, maxKernAddr) (supervisor ? (addr>=minKernAddr && addr<=maxKernAddr) : TRUE)
198// don't try to read in the hole
199#define VALID_STACK_ADDRESS64(supervisor, addr, minKernAddr, maxKernAddr) \
2d21ac55
A
200(supervisor ? (addr >= minKernAddr && addr <= maxKernAddr) : \
201(addr != 0 && (addr <= 0x00007FFFFFFFFFFFULL || addr >= 0xFFFF800000000000ULL)))
0c530ab8
A
202
203typedef struct _cframe64_t {
204 uint64_t prevFP; // can't use a real pointer here until we're a 64 bit kernel
205 uint64_t caller;
206 uint64_t args[0];
207}cframe64_t;
208
209
210typedef struct _cframe_t {
211 struct _cframe_t *prev; // when we go 64 bits, this needs to be capped at 32 bits
212 uint32_t caller;
213 uint32_t args[0];
214} cframe_t;
215
2d21ac55
A
216extern void * find_user_regs(thread_t);
217extern x86_saved_state32_t *find_kern_regs(thread_t);
218
219static kern_return_t do_backtrace32(
220 task_t task,
221 thread_t thread,
222 x86_saved_state32_t *regs,
223 uint64_t *frames,
224 mach_msg_type_number_t *start_idx,
225 mach_msg_type_number_t max_idx,
226 boolean_t supervisor)
227{
228 uint32_t tmpWord = 0UL;
229 uint64_t currPC = (uint64_t) regs->eip;
230 uint64_t currFP = (uint64_t) regs->ebp;
231 uint64_t prevPC = 0ULL;
232 uint64_t prevFP = 0ULL;
233 uint64_t kernStackMin = thread->kernel_stack;
234 uint64_t kernStackMax = kernStackMin + KERNEL_STACK_SIZE;
235 mach_msg_type_number_t ct = *start_idx;
236 kern_return_t kr = KERN_FAILURE;
237
238 if(ct >= max_idx)
239 return KERN_RESOURCE_SHORTAGE; // no frames traced
240
241 frames[ct++] = currPC;
242
243 // build a backtrace of this 32 bit state.
244 while(VALID_STACK_ADDRESS(supervisor, currFP, kernStackMin, kernStackMax)) {
245 cframe_t *fp = (cframe_t *) (uint32_t) currFP;
246
247 if(!currFP) {
248 currPC = 0;
249 break;
250 }
251
252 if(ct >= max_idx) {
253 *start_idx = ct;
254 return KERN_RESOURCE_SHORTAGE;
255 }
256
257 /* read our caller */
258 if(supervisor) {
259 kr = chudxnu_kern_read(&tmpWord, (vm_offset_t) &fp->caller, sizeof(uint32_t));
260 } else {
261 kr = chudxnu_task_read(task, &tmpWord, (vm_offset_t) &fp->caller, sizeof(uint32_t));
262 }
263
264 if(kr != KERN_SUCCESS) {
265 currPC = 0ULL;
266 break;
267 }
268
269 currPC = (uint64_t) tmpWord; // promote 32 bit address
270
271 /*
272 * retrive contents of the frame pointer and advance to the next stack
273 * frame if it's valid
274 */
275 prevFP = 0;
276 if(supervisor) {
277 kr = chudxnu_kern_read(&tmpWord, (vm_offset_t)&fp->prev, sizeof(uint32_t));
278 } else {
279 kr = chudxnu_task_read(task, &tmpWord, (vm_offset_t)&fp->prev, sizeof(uint32_t));
280 }
281 prevFP = (uint64_t) tmpWord; // promote 32 bit address
282
283 if(prevFP) {
284 frames[ct++] = currPC;
285 prevPC = currPC;
286 }
287 if(prevFP < currFP) {
288 break;
289 } else {
290 currFP = prevFP;
291 }
292 }
293
294 *start_idx = ct;
295 return KERN_SUCCESS;
296}
297
298static kern_return_t do_backtrace64(
299 task_t task,
300 thread_t thread,
301 x86_saved_state64_t *regs,
302 uint64_t *frames,
303 mach_msg_type_number_t *start_idx,
304 mach_msg_type_number_t max_idx,
305 boolean_t supervisor)
306{
307 uint64_t currPC = regs->isf.rip;
308 uint64_t currFP = regs->rbp;
309 uint64_t prevPC = 0ULL;
310 uint64_t prevFP = 0ULL;
311 uint64_t kernStackMin = (uint64_t)thread->kernel_stack;
312 uint64_t kernStackMax = (uint64_t)kernStackMin + KERNEL_STACK_SIZE;
313 mach_msg_type_number_t ct = *start_idx;
314 kern_return_t kr = KERN_FAILURE;
315
316 if(*start_idx >= max_idx)
317 return KERN_RESOURCE_SHORTAGE; // no frames traced
318
319 frames[ct++] = currPC;
320
321 // build a backtrace of this 32 bit state.
322 while(VALID_STACK_ADDRESS64(supervisor, currFP, kernStackMin, kernStackMax)) {
323 // this is the address where caller lives in the user thread
324 uint64_t caller = currFP + sizeof(uint64_t);
325
326 if(!currFP) {
327 currPC = 0;
328 break;
329 }
330
331 if(ct >= max_idx) {
332 *start_idx = ct;
333 return KERN_RESOURCE_SHORTAGE;
334 }
335
336 /* read our caller */
337 if(supervisor) {
338 kr = KERN_FAILURE;
339 } else {
340 kr = chudxnu_task_read(task, &currPC, caller, sizeof(uint64_t));
341 }
342
343 if(kr != KERN_SUCCESS) {
344 currPC = 0ULL;
345 break;
346 }
347
348 /*
349 * retrive contents of the frame pointer and advance to the next stack
350 * frame if it's valid
351 */
352 prevFP = 0;
353 if(supervisor) {
354 kr = KERN_FAILURE;
355 } else {
356 kr = chudxnu_task_read(task, &prevFP, currFP, sizeof(uint64_t));
357 }
358
359 if(VALID_STACK_ADDRESS64(supervisor, prevFP, kernStackMin, kernStackMax)) {
360 frames[ct++] = currPC;
361 prevPC = currPC;
362 }
363 if(prevFP < currFP) {
364 break;
365 } else {
366 currFP = prevFP;
367 }
368 }
369
370 *start_idx = ct;
371 return KERN_SUCCESS;
372}
373
0c530ab8
A
374__private_extern__
375kern_return_t chudxnu_thread_get_callstack64(
376 thread_t thread,
377 uint64_t *callstack,
378 mach_msg_type_number_t *count,
379 boolean_t user_only)
380{
2d21ac55 381 kern_return_t kr = KERN_FAILURE;
0c530ab8
A
382 task_t task = thread->task;
383 uint64_t currPC = 0;
2d21ac55
A
384 boolean_t supervisor = FALSE;
385 mach_msg_type_number_t bufferIndex = 0;
386 mach_msg_type_number_t bufferMaxIndex = *count;
387 x86_saved_state_t *tagged_regs = NULL; // kernel register state
388 x86_saved_state64_t *regs64 = NULL;
389 x86_saved_state32_t *regs32 = NULL;
390 x86_saved_state32_t *u_regs32 = NULL;
391 x86_saved_state64_t *u_regs64 = NULL;
392
393 if(ml_at_interrupt_context()) {
394
395 if(user_only) {
396 /* can't backtrace user state on interrupt stack. */
0c530ab8
A
397 return KERN_FAILURE;
398 }
2d21ac55
A
399
400 /* backtracing at interrupt context? */
401 if(thread == current_thread() && current_cpu_datap()->cpu_int_state) {
402 /*
403 * Locate the registers for the interrupted thread, assuming it is
404 * current_thread().
405 */
406 tagged_regs = current_cpu_datap()->cpu_int_state;
0c530ab8 407
2d21ac55
A
408 if(is_saved_state64(tagged_regs)) {
409 /* 64 bit registers */
410 regs64 = saved_state64(tagged_regs);
411 supervisor = ((regs64->isf.cs & SEL_PL) != SEL_PL_U);
0c530ab8 412 } else {
2d21ac55
A
413 /* 32 bit registers */
414 regs32 = saved_state32(tagged_regs);
415 supervisor = ((regs32->cs & SEL_PL) != SEL_PL_U);
0c530ab8 416 }
2d21ac55
A
417 }
418 }
0c530ab8 419
2d21ac55
A
420 if(!tagged_regs) {
421 /*
422 * not at interrupt context, or tracing a different thread than
423 * current_thread() at interrupt context
424 */
425 tagged_regs = USER_STATE(thread);
426 if(is_saved_state64(tagged_regs)) {
427 /* 64 bit registers */
428 regs64 = saved_state64(tagged_regs);
429 supervisor = ((regs64->isf.cs & SEL_PL) != SEL_PL_U);
430 } else {
431 /* 32 bit registers */
432 regs32 = saved_state32(tagged_regs);
433 supervisor = ((regs32->cs & SEL_PL) != SEL_PL_U);
0c530ab8 434 }
2d21ac55 435 }
0c530ab8 436
2d21ac55
A
437 *count = 0;
438
439 if(supervisor) {
440 // the caller only wants a user callstack.
0c530ab8 441 if(user_only) {
2d21ac55 442 // bail - we've only got kernel state
0c530ab8
A
443 return KERN_FAILURE;
444 }
2d21ac55
A
445 } else {
446 // regs32(64) is not in supervisor mode.
447 u_regs32 = regs32;
448 u_regs64 = regs64;
449 regs32 = NULL;
450 regs64 = NULL;
451 }
452
453 if (user_only) {
454 /* we only want to backtrace the user mode */
455 if(!(u_regs32 || u_regs64)) {
456 /* no user state to look at */
457 return KERN_FAILURE;
0c530ab8 458 }
2d21ac55 459 }
0c530ab8 460
2d21ac55
A
461 /*
462 * Order of preference for top of stack:
463 * 64 bit kernel state (not likely)
464 * 32 bit kernel state
465 * 64 bit user land state
466 * 32 bit user land state
467 */
468
469 if(regs64) {
470 currPC = regs64->isf.rip;
471 } else if(regs32) {
472 currPC = (uint64_t) regs32->eip;
473 } else if(u_regs64) {
474 currPC = u_regs64->isf.rip;
475 } else if(u_regs32) {
476 currPC = (uint64_t) u_regs32->eip;
477 }
0c530ab8 478
2d21ac55
A
479 if(!currPC) {
480 /* no top of the stack, bail out */
481 return KERN_FAILURE;
482 }
0c530ab8 483
2d21ac55 484 bufferIndex = 0;
0c530ab8 485
2d21ac55
A
486 if(bufferMaxIndex < 1) {
487 *count = 0;
488 return KERN_RESOURCE_SHORTAGE;
489 }
490
491 /* backtrace kernel */
492 if(regs64) {
493 uint64_t rsp = 0ULL;
494
495 // backtrace the 64bit side.
496 kr = do_backtrace64(task, thread, regs64, callstack, &bufferIndex,
497 bufferMaxIndex, TRUE);
498
499 if(KERN_SUCCESS == chudxnu_kern_read(&rsp, (addr64_t) regs64->isf.rsp, sizeof(uint64_t)) &&
500 bufferIndex < bufferMaxIndex) {
501 callstack[bufferIndex++] = rsp;
0c530ab8
A
502 }
503
2d21ac55
A
504 } else if(regs32) {
505 uint32_t esp = 0UL;
506
507 // backtrace the 32bit side.
508 kr = do_backtrace32(task, thread, regs32, callstack, &bufferIndex,
509 bufferMaxIndex, TRUE);
0c530ab8 510
2d21ac55
A
511 if(KERN_SUCCESS == chudxnu_kern_read(&esp, (addr64_t) regs32->uesp, sizeof(uint32_t)) &&
512 bufferIndex < bufferMaxIndex) {
513 callstack[bufferIndex++] = (uint64_t) esp;
0c530ab8 514 }
2d21ac55
A
515 } else if(u_regs64) {
516 /* backtrace user land */
517 uint64_t rsp = 0ULL;
518
519 kr = do_backtrace64(task, thread, u_regs64, callstack, &bufferIndex,
520 bufferMaxIndex, FALSE);
0c530ab8 521
2d21ac55
A
522 if(KERN_SUCCESS == chudxnu_task_read(task, &rsp, (addr64_t) u_regs64->isf.rsp, sizeof(uint64_t)) &&
523 bufferIndex < bufferMaxIndex) {
524 callstack[bufferIndex++] = rsp;
525 }
0c530ab8 526
2d21ac55
A
527 } else if(u_regs32) {
528 uint32_t esp = 0UL;
529
530 kr = do_backtrace32(task, thread, u_regs32, callstack, &bufferIndex,
531 bufferMaxIndex, FALSE);
0c530ab8 532
2d21ac55
A
533 if(KERN_SUCCESS == chudxnu_task_read(task, &esp, (addr64_t) u_regs32->uesp, sizeof(uint32_t)) &&
534 bufferIndex < bufferMaxIndex) {
535 callstack[bufferIndex++] = (uint64_t) esp;
0c530ab8 536 }
2d21ac55 537 }
0c530ab8 538
0c530ab8 539 *count = bufferIndex;
2d21ac55 540 return kr;
0c530ab8
A
541}
542
2d21ac55
A
543#pragma mark **** DEPRECATED ****
544
545// DEPRECATED
0c530ab8
A
546__private_extern__ kern_return_t
547chudxnu_thread_get_callstack(
2d21ac55
A
548 thread_t thread,
549 uint32_t *callStack,
550 mach_msg_type_number_t *count,
551 boolean_t user_only)
0c530ab8
A
552{
553 kern_return_t kr;
554 task_t task = thread->task;
555 uint32_t currPC;
556 uint32_t currFP;
557 uint32_t prevFP = 0;
558 uint32_t prevPC = 0;
559 uint32_t esp = 0;
2d21ac55
A
560 uint32_t kernStackMin = thread->kernel_stack;
561 uint32_t kernStackMax = kernStackMin + KERNEL_STACK_SIZE;
0c530ab8
A
562 uint32_t *buffer = callStack;
563 int bufferIndex = 0;
564 int bufferMaxIndex = *count;
565 boolean_t supervisor;
566 x86_saved_state32_t *regs = NULL;
2d21ac55 567
0c530ab8
A
568 if (user_only) {
569 /* We can't get user state for kernel threads */
570 if (task == kernel_task) {
571 return KERN_FAILURE;
572 }
573 regs = USER_REGS32(thread);
574 } else {
575 regs = saved_state32(current_cpu_datap()->cpu_int_state);
576 }
2d21ac55 577
0c530ab8
A
578 if (regs == NULL) {
579 *count = 0;
580 return KERN_FAILURE;
581 }
582
583 supervisor = ((regs->cs & SEL_PL) != SEL_PL_U);
584
585 currPC = regs->eip;
586 currFP = regs->ebp;
587
588 bufferIndex = 0;
589 if(!supervisor)
590 bufferMaxIndex -= 1; // allot space for saving userland %esp on stack
591 if (bufferMaxIndex < 1) {
592 *count = 0;
593 return KERN_RESOURCE_SHORTAGE;
594 }
595 buffer[bufferIndex++] = currPC; //save PC in position 0.
2d21ac55 596
0c530ab8
A
597 // Now, fill buffer with stack backtraces.
598 while (VALID_STACK_ADDRESS(supervisor, currFP, kernStackMin, kernStackMax)) {
599 cframe_t *fp = (cframe_t *) currFP;
2d21ac55 600
0c530ab8
A
601 if (bufferIndex >= bufferMaxIndex) {
602 *count = bufferMaxIndex;
603 return KERN_RESOURCE_SHORTAGE;
604 }
2d21ac55 605
0c530ab8
A
606 if (supervisor) {
607 kr = chudxnu_kern_read(
2d21ac55
A
608 &currPC,
609 (vm_offset_t) &fp->caller,
610 sizeof(currPC));
0c530ab8
A
611 } else {
612 kr = chudxnu_task_read(
2d21ac55
A
613 task,
614 &currPC,
615 (vm_offset_t) &fp->caller,
616 sizeof(currPC));
0c530ab8
A
617 }
618 if (kr != KERN_SUCCESS)
619 break;
2d21ac55 620
0c530ab8
A
621 //retrieve the contents of the frame pointer
622 // and advance to the prev stack frame if it's valid
623 prevFP = 0;
624 if (supervisor) {
625 kr = chudxnu_kern_read(
2d21ac55
A
626 &prevFP,
627 (vm_offset_t) &fp->prev,
628 sizeof(prevFP));
0c530ab8
A
629 } else {
630 kr = chudxnu_task_read(
2d21ac55
A
631 task,
632 &prevFP,
633 (vm_offset_t) &fp->prev,
634 sizeof(prevFP));
0c530ab8
A
635 }
636 if (prevFP) {
637 buffer[bufferIndex++] = currPC;
638 prevPC = currPC;
639 }
640 if (prevFP < currFP) {
641 break;
642 } else {
643 currFP = prevFP;
644 }
645 }
2d21ac55 646
0c530ab8
A
647 // put the stack pointer on the bottom of the backtrace
648 if(!supervisor) {
649 kr = chudxnu_task_read(task, &esp, regs->uesp, sizeof(uint32_t));
650 if(kr == KERN_SUCCESS) {
651 buffer[bufferIndex++] = esp;
652 }
653 }
2d21ac55 654
0c530ab8
A
655 *count = bufferIndex;
656 return KERN_SUCCESS;
657}
658