/*-
 * Copyright (c) 2008-2009, Apple Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
 *     its contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _SECURITY_AUDIT_AUDIT_BSD_H
#define _SECURITY_AUDIT_AUDIT_BSD_H

#include <sys/cdefs.h>
#include <machine/endian.h>

#if defined(_KERNEL) || defined(KERNEL)
#include <kern/kalloc.h>

#if DIAGNOSTIC
#ifdef KASSERT
#undef KASSERT
#endif
#ifdef AUDIT_KASSERT_DEBUG
#define KASSERT(exp, msg) do {                                          \
	if (__builtin_expect(!(exp), 0)) {                              \
	        printf("%s:%d KASSERT failed: ", __FILE__, __LINE__);   \
	        printf msg;                                             \
	        printf("\n");                                           \
	}                                                               \
} while (0)
#else
#define KASSERT(exp, msg) do {                                          \
	if (__builtin_expect(!(exp), 0))                                \
	        panic msg;                                              \
} while (0)
#endif
#endif  /* DIAGNOSTIC */

#define AU_MAX_LCK_NAME 32

#if __DARWIN_BYTE_ORDER == __DARWIN_BIG_ENDIAN
#define be16enc(p, d)   *(p) = (d)
#define be32enc(p, d)   *(p) = (d)
#define be64enc(p, d)   *(p) = (d)

#else /* __DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN */

#include <libkern/OSByteOrder.h>

#define be16enc(p, d)   OSWriteSwapInt16(p, 0, d)
#define be32enc(p, d)   OSWriteSwapInt32(p, 0, d)
#define be64enc(p, d)   OSWriteSwapInt64(p, 0, d)
#endif /* __DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN */

/*
 * BSD kernel memory allocation.
 */
#define AUDIT_MALLOC_DEBUG       0      /* Change to 1 for malloc debugging. */

#define M_AUDITUNKNOWN           0
#define M_AUDITDATA              1
#define M_AUDITPATH              2
#define M_AUDITTEXT              3
#define M_AUDITBSM               4
#define M_AUDITEVCLASS           5
#define M_AUDIT_PIPE             6
#define M_AUDIT_PIPE_ENTRY       7
#define M_AUDIT_PIPE_PRESELECT   8
#define M_AU_SESSION             9
#define M_AU_EV_PLIST           10

#define NUM_MALLOC_TYPES        11

#ifdef  M_WAITOK
#undef  M_WAITOK
#define M_WAITOK        0x0000  /* ok to block */
#endif
#ifdef  M_NOWAIT
#undef  M_NOWAIT
#endif
#define M_NOWAIT        0x0001  /* do not block */
#ifdef  M_ZERO
#undef  M_ZERO
#endif
#define M_ZERO          0x0004  /* bzero the allocation */

#ifdef  M_MAGIC
#undef  M_MAGIC
#endif
#define M_MAGIC         877983977

#ifdef  MALLOC_DEFINE
#undef  MALLOC_DEFINE
#endif
#if AUDIT_MALLOC_DEBUG
struct  au_malloc_type {
	SInt64           mt_size;
	SInt64           mt_maxsize;
	SInt32           mt_inuse;
	SInt32           mt_maxused;
	unsigned         mt_type;
	unsigned         mt_magic;
	const char      *mt_shortdesc;
	const char      *mt_lastcaller;
};
typedef struct au_malloc_type   au_malloc_type_t;

#define MALLOC_DEFINE(type, shortdesc, longdesc)                \
	au_malloc_type_t  audit_##type[1] = {                   \
	        { 0, 0, 0, 0, (type < NUM_MALLOC_TYPES) ? type :\
	          M_AUDITUNKNOWN, M_MAGIC, shortdesc, NULL }    \
	}

extern au_malloc_type_t *audit_malloc_types[];

#else

struct  au_malloc_type {
	uint32_t         mt_magic;
	const char      *mt_shortdesc;
};
typedef struct au_malloc_type   au_malloc_type_t;

#define MALLOC_DEFINE(type, shortdesc, longdesc)                \
	au_malloc_type_t  audit_##type[1] = {                   \
	        {M_MAGIC, shortdesc }                           \
	}

#endif /* AUDIT_MALLOC_DEBUG */

#ifdef  MALLOC_DECLARE
#undef  MALLOC_DECLARE
#endif
#define MALLOC_DECLARE(type) \
	extern au_malloc_type_t audit_##type[]

#if AUDIT_MALLOC_DEBUG
#define malloc(sz, tp, fl)      _audit_malloc(sz, audit_##tp, fl, __FUNCTION__)
void *_audit_malloc(size_t size, au_malloc_type_t *type, int flags,
    const char *fn);
#else
#define malloc(sz, tp, fl)      _audit_malloc(sz, audit_##tp, fl)
void *_audit_malloc(size_t size, au_malloc_type_t *type, int flags);
#endif

#define free(ad, tp)            _audit_free(ad, audit_##tp)
void _audit_free(void *addr, au_malloc_type_t *type);

/*
 * BSD condition variable.
 */
struct cv {
	const char      *cv_description;
	int              cv_waiters;
};

/*
 * BSD mutex.
 */
struct mtx {
	lck_mtx_t       *mtx_lock;
#if DIAGNOSTIC
	char             mtx_name[AU_MAX_LCK_NAME];
#endif
};

/*
 * BSD rw lock.
 */
struct rwlock {
	lck_rw_t        *rw_lock;
#if DIAGNOSTIC
	char             rw_name[AU_MAX_LCK_NAME];
#endif
};

/*
 * Sleep lock.
 */
struct slck {
	lck_mtx_t       *sl_mtx;
	int              sl_locked;
	int              sl_waiting;
#if DIAGNOSTIC
	char             sl_name[AU_MAX_LCK_NAME];
#endif
};

/*
 * Recursive lock.
 */
struct rlck {
	lck_mtx_t       *rl_mtx;
	uint32_t         rl_recurse;
	thread_t         rl_thread;
#if DIAGNOSTIC
	char             rl_name[AU_MAX_LCK_NAME];
#endif
};

/*
 * BSD condition variables functions.
 */
void    _audit_cv_init(struct cv *cvp, const char *desc);
void    _audit_cv_destroy(struct cv *cvp);
void    _audit_cv_signal(struct cv *cvp);
void    _audit_cv_broadcast(struct cv *cvp);
void    _audit_cv_wait(struct cv *cvp, lck_mtx_t *mp, const char *desc);
int     _audit_cv_wait_sig(struct cv *cvp, lck_mtx_t *mp, const char *desc);
int     _audit_cv_wait_continuation(struct cv *cvp, lck_mtx_t *mp,
    thread_continue_t function);
#define cv_init(cvp, desc)        _audit_cv_init(cvp, desc)
#define cv_destroy(cvp)           _audit_cv_destroy(cvp)
#define cv_signal(cvp)            _audit_cv_signal(cvp)
#define cv_broadcast(cvp)         _audit_cv_broadcast(cvp)
#define cv_broadcastpri(cvp, pri) _audit_cv_broadcast(cvp)
#define cv_wait(cvp, mp)          _audit_cv_wait(cvp, (mp)->mtx_lock, #cvp)
#define cv_wait_sig(cvp, mp)      _audit_cv_wait_sig(cvp, (mp)->mtx_lock, #cvp)
#define cv_wait_continuation(cvp, mp, f) \
    _audit_cv_wait_continuation(cvp, (mp)->mtx_lock, f)

/*
 * BSD Mutexes.
 */
void    _audit_mtx_init(struct mtx *mp, const char *name);
void    _audit_mtx_destroy(struct mtx *mp);
#define mtx_init(mp, name, type, opts) \
	                        _audit_mtx_init(mp, name)
#define mtx_lock(mp)            lck_mtx_lock((mp)->mtx_lock)
#define mtx_unlock(mp)          lck_mtx_unlock((mp)->mtx_lock)
#define mtx_destroy(mp)         _audit_mtx_destroy(mp)
#define mtx_yield(mp)           lck_mtx_yield((mp)->mtx_lock)

/*
 * Sleep lock functions.
 */
void            _audit_slck_init(struct slck *lp, const char *grpname);
wait_result_t   _audit_slck_lock(struct slck *lp, int intr);
void            _audit_slck_unlock(struct slck *lp);
int             _audit_slck_trylock(struct slck *lp);
void            _audit_slck_assert(struct slck *lp, u_int assert);
void            _audit_slck_destroy(struct slck *lp);
#define slck_init(lp, name)     _audit_slck_init((lp), (name))
#define slck_lock(lp)           _audit_slck_lock((lp), 0)
#define slck_lock_sig(lp)       (_audit_slck_lock((lp), 1) != THREAD_AWAKENED)
#define slck_unlock(lp)         _audit_slck_unlock((lp))
#define slck_destroy(lp)        _audit_slck_destroy((lp))

/*
 * Recursive lock functions.
 */
void            _audit_rlck_init(struct rlck *lp, const char *grpname);
void            _audit_rlck_lock(struct rlck *lp);
void            _audit_rlck_unlock(struct rlck *lp);
void            _audit_rlck_assert(struct rlck *lp, u_int assert);
void            _audit_rlck_destroy(struct rlck *lp);
#define rlck_init(lp, name)     _audit_rlck_init((lp), (name))
#define rlck_lock(lp)           _audit_rlck_lock((lp))
#define rlck_unlock(lp)         _audit_rlck_unlock((lp))
#define rlck_destroy(lp)        _audit_rlck_destroy((lp))

/*
 * BSD rw locks.
 */
void    _audit_rw_init(struct rwlock *lp, const char *name);
void    _audit_rw_destroy(struct rwlock *lp);
#define rw_init(lp, name)       _audit_rw_init(lp, name)
#define rw_rlock(lp)            lck_rw_lock_shared((lp)->rw_lock)
#define rw_runlock(lp)          lck_rw_unlock_shared((lp)->rw_lock)
#define rw_wlock(lp)            lck_rw_lock_exclusive((lp)->rw_lock)
#define rw_wunlock(lp)          lck_rw_unlock_exclusive((lp)->rw_lock)
#define rw_destroy(lp)          _audit_rw_destroy(lp)

#define MA_OWNED                LCK_MTX_ASSERT_OWNED
#define RA_LOCKED               LCK_RW_ASSERT_HELD
#define RA_RLOCKED              LCK_RW_ASSERT_SHARED
#define RA_WLOCKED              LCK_RW_ASSERT_EXCLUSIVE
#define SA_LOCKED               LCK_RW_ASSERT_HELD
#define SA_XLOCKED              LCK_RW_ASSERT_EXCLUSIVE
#define SL_OWNED                LCK_MTX_ASSERT_OWNED
#define SL_NOTOWNED             LCK_MTX_ASSERT_NOTOWNED
#if DIAGNOSTIC
#define mtx_assert(mp, wht)     lck_mtx_assert((mp)->mtx_lock, wht)
#define rw_assert(lp, wht)      lck_rw_assert((lp)->rw_lock, wht)
#define sx_assert(lp, wht)      lck_rw_assert((lp)->sx_lock, wht)
#define rlck_assert(lp, wht)    _audit_rlck_assert((lp), wht)
#define slck_assert(lp, wht)    _audit_slck_assert((lp), wht)
#else
#define mtx_assert(mp, wht)
#define rw_assert(lp, wht)
#define sx_assert(lp, wht)
#define rlck_assert(lp, wht)
#define slck_assert(lp, wht)
#endif /* DIAGNOSTIC */

/*
 * BSD (IPv6) event rate limiter.
 */
int _audit_ppsratecheck(struct timeval *lasttime, int *curpps, int maxpps);
#define ppsratecheck(tv, cr, mr)        _audit_ppsratecheck(tv, cr, mr)

#endif /* defined(_KERNEL) || defined(KERNEL) */
#endif /* _SECURITY_AUDIT_AUDIT_BSD_H */
