chud_bsd_callback.c [plain text]
#include <stdint.h>
#include <mach/boolean.h>
#include <mach/mach_types.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <sys/sysproto.h>
#include <sys/kdebug.h>
#include <libkern/OSAtomic.h>
#ifdef __ppc__
#include <ppc/savearea.h>
#define FM_ARG0 0x38ULL // offset from r1 to first argument
#define SPILLED_WORD_COUNT 7 // number of 32-bit words spilled to the stack
extern struct savearea * find_user_regs( thread_t act);
#endif
#pragma mark **** kern debug ****
typedef void (*chudxnu_kdebug_callback_func_t)(uint32_t debugid, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4);
static void chud_null_kdebug(uint32_t debugid, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3, uintptr_t arg4);
static chudxnu_kdebug_callback_func_t kdebug_callback_fn = chud_null_kdebug;
kern_return_t chudxnu_kdebug_callback_enter(chudxnu_kdebug_callback_func_t);
kern_return_t chudxnu_kdebug_callback_cancel(void);
extern void kdbg_control_chud(int val, void *fn);
static void chud_null_kdebug(uint32_t debugid __unused, uintptr_t arg0 __unused,
uintptr_t arg1 __unused, uintptr_t arg2 __unused, uintptr_t arg3 __unused,
uintptr_t arg4 __unused) {
return;
}
static void
chudxnu_private_kdebug_callback(
uint32_t debugid,
uintptr_t arg0,
uintptr_t arg1,
uintptr_t arg2,
uintptr_t arg3,
uintptr_t arg4)
{
chudxnu_kdebug_callback_func_t fn = kdebug_callback_fn;
if(fn) {
(fn)(debugid, arg0, arg1, arg2, arg3, arg4);
}
}
__private_extern__ kern_return_t
chudxnu_kdebug_callback_enter(chudxnu_kdebug_callback_func_t func)
{
if(OSCompareAndSwapPtr(chud_null_kdebug, func,
(void * volatile *)&kdebug_callback_fn)) {
kdbg_control_chud(TRUE, (void *)chudxnu_private_kdebug_callback);
OSBitOrAtomic((UInt32)KDEBUG_ENABLE_CHUD, (volatile UInt32 *)&kdebug_enable);
return KERN_SUCCESS;
}
return KERN_FAILURE;
}
__private_extern__ kern_return_t
chudxnu_kdebug_callback_cancel(void)
{
OSBitAndAtomic((UInt32)~(KDEBUG_ENABLE_CHUD), (volatile UInt32 *)&kdebug_enable);
kdbg_control_chud(FALSE, NULL);
chudxnu_kdebug_callback_func_t old = kdebug_callback_fn;
while(!OSCompareAndSwapPtr(old, chud_null_kdebug,
(void * volatile *)&kdebug_callback_fn)) {
old = kdebug_callback_fn;
}
return KERN_SUCCESS;
}
#pragma mark **** CHUD syscall ****
typedef kern_return_t (*chudxnu_syscall_callback_func_t)(uint64_t code, uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4);
static kern_return_t chud_null_syscall(uint64_t code, uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4);
static chudxnu_syscall_callback_func_t syscall_callback_fn = chud_null_syscall;
kern_return_t chudxnu_syscall_callback_enter(chudxnu_syscall_callback_func_t func);
kern_return_t chudxnu_syscall_callback_cancel(void);
static kern_return_t chud_null_syscall(uint64_t code __unused,
uint64_t arg0 __unused, uint64_t arg1 __unused, uint64_t arg2 __unused,
uint64_t arg3 __unused, uint64_t arg4 __unused) {
return (kern_return_t)EINVAL;
}
int
chud(__unused proc_t p, struct chud_args *uap, int32_t *retval)
{
chudxnu_syscall_callback_func_t fn = syscall_callback_fn;
if(!fn) {
return EINVAL;
}
#ifdef __ppc__
if(!IS_64BIT_PROCESS(p)) {
struct savearea *regs = find_user_regs(current_thread());
if(!regs) {
return EINVAL;
}
uint32_t stackPointer = regs->save_r1;
uint32_t spilledSize = sizeof(struct chud_args) - (sizeof(uint32_t) * SPILLED_WORD_COUNT);
user_addr_t incomingAddr = (user_addr_t)stackPointer + FM_ARG0;
uint8_t *dstAddr = (uint8_t*)(&(uap->arg3)) + sizeof(uint32_t);
copyin(incomingAddr, dstAddr, spilledSize);
}
#endif
*retval = fn(uap->code, uap->arg1, uap->arg2, uap->arg3, uap->arg4, uap->arg5);
return 0;
}
__private_extern__ kern_return_t
chudxnu_syscall_callback_enter(chudxnu_syscall_callback_func_t func)
{
if(OSCompareAndSwapPtr(chud_null_syscall, func,
(void * volatile *)&syscall_callback_fn)) {
return KERN_SUCCESS;
}
return KERN_FAILURE;
}
__private_extern__ kern_return_t
chudxnu_syscall_callback_cancel(void)
{
chudxnu_syscall_callback_func_t old = syscall_callback_fn;
while(!OSCompareAndSwapPtr(old, chud_null_syscall,
(void * volatile *)&syscall_callback_fn)) {
old = syscall_callback_fn;
}
return KERN_SUCCESS;
}
typedef kern_return_t (*chudxnu_dtrace_callback_t)(uint64_t selector,
uint64_t *args, uint32_t count);
int chudxnu_dtrace_callback(uint64_t selector, uint64_t *args, uint32_t count);
kern_return_t chudxnu_dtrace_callback_enter(chudxnu_dtrace_callback_t fn);
void chudxnu_dtrace_callback_cancel(void);
int
chud_null_dtrace(uint64_t selector, uint64_t *args, uint32_t count);
static chudxnu_dtrace_callback_t
dtrace_callback = (chudxnu_dtrace_callback_t) chud_null_dtrace;
int
chud_null_dtrace(uint64_t selector __unused, uint64_t *args __unused,
uint32_t count __unused) {
return ENXIO;
}
int
chudxnu_dtrace_callback(uint64_t selector, uint64_t *args, uint32_t count)
{
int ret = ENXIO;
chudxnu_dtrace_callback_t fn = dtrace_callback;
if(fn) {
ret = fn(selector, args, count);
}
return ret;
}
__private_extern__ kern_return_t
chudxnu_dtrace_callback_enter(chudxnu_dtrace_callback_t fn)
{
if(!OSCompareAndSwapPtr(chud_null_dtrace, fn,
(void * volatile *) &dtrace_callback)) {
return KERN_FAILURE;
}
return KERN_SUCCESS;
}
__private_extern__ void
chudxnu_dtrace_callback_cancel(void)
{
chudxnu_dtrace_callback_t old_fn = dtrace_callback;
while(!OSCompareAndSwapPtr(old_fn, chud_null_dtrace,
(void * volatile *) &dtrace_callback)) {
old_fn = dtrace_callback;
}
}