/*
 * Copyright (c) 2021, 2024 Apple Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 *
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */

/* socd_client_kern.h: machine-independent API for interfacing with soc diagnostics data pipeline, kernel mode specific logic */

#ifndef _KERN_SOCD_CLIENT_KERN_H_
#define _KERN_SOCD_CLIENT_KERN_H_

#include <kern/socd_client.h>
#include <kern/kern_types.h>
#include <mach/vm_param.h>
#include <sys/cdefs.h>

__BEGIN_DECLS

/*
 * SOCD_TRACE
 * Trace an event to debug kernel and hardware hangs. Traced on all build variants of kernel.
 * x - kdebug debugid
 * a, b, c, d - 64 bit data arguments
 *
 * Usage:
 *    SOCD_TRACE is an expensive operation and must not be used on performance critical paths.
 *    Each data argument must be wrapped with SOCD_ADDR or SOCD_VAL macro. To enforce this rule,
 *    SOCD_ is prepended to all data arguments passed to SOCD_TRACE.
 *    1. Use ADDR when passing a kernel address. The macro verifies kernel address is slid, then it removes the slide.
 *       When kernel address is not slid, the macro returns 0.
 *    2. Use VALUE when passing an argument that's not expected to be a kernel address. When the argument
 *       is a kernel address, the macro returns 0.
 *    3. Use PACK_2X32 to pack two 32 bit values into one 64 bit argument. PACK_2X32 arguments must be wrapped with ADDR or VALUE macro as well.
 *    4. Use PACK_LSB to overwrite the least significant bit of a 64 bit value. PACK_LSB arguments must be wrapped with ADDR or VALUE macro as well.
 *
 *
 * Example:
 *      SOCD_TRACE(KDBG_EVENTID(DBG_DRIVERS, DBG_SOCDIAGS, SOCD_TRACE_EVENTID(SOCD_TRACE_CLASS_KERNEL, SOCD_TRACE_MODE_NONE, SOCD_TRACE_CODE_KERNEL_PANIC)),
 *                 ADDR(caller_function_ptr), VALUE(panic_options))
 */
#define SOCD_TRACE(x, ...) SOCD_TRACE_(x, ## __VA_ARGS__, 4, 3, 2, 1, 0)
#define SOCD_TRACE_(x, a, b, c, d, n, ...) SOCD_TRACE##n(x, a, b, c, d)
#define SOCD_TRACE0(x, a, b, c, d)         SOCD_TRACE_IMPL(x,        0,        0,        0,        0)
#define SOCD_TRACE1(x, a, b, c, d)         SOCD_TRACE_IMPL(x, SOCD_##a,        0,        0,        0)
#define SOCD_TRACE2(x, a, b, c, d)         SOCD_TRACE_IMPL(x, SOCD_##a, SOCD_##b,        0,        0)
#define SOCD_TRACE3(x, a, b, c, d)         SOCD_TRACE_IMPL(x, SOCD_##a, SOCD_##b, SOCD_##c,        0)
#define SOCD_TRACE4(x, a, b, c, d)         SOCD_TRACE_IMPL(x, SOCD_##a, SOCD_##b, SOCD_##c, SOCD_##d)

#if defined(__arm64__)
#  define SOCD_TRACE_ENABLED 1
#endif

#if SOCD_TRACE_ENABLED
#define SOCD_TRACE_IMPL(x, a, b, c, d) \
do { \
	socd_client_trace((x), (socd_client_trace_arg_t)(a), (socd_client_trace_arg_t)(b), \
	                        (socd_client_trace_arg_t)(c), (socd_client_trace_arg_t)(d)); \
} while (0)
#define SOCD_TRACE_REINIT() socd_client_reinit()
#else // SOCD_TRACE_ENABLED
#define SOCD_TRACE_IMPL(x, a, b, c, d)
#define SOCD_TRACE_REINIT()
#endif // !SOCD_TRACE_ENABLED

#define SOCD_ADDR(_a) (VM_KERNEL_UNSLIDE((vm_offset_t)(_a)))
#define SOCD_VALUE(_v) (VM_KERNEL_ADDRESS((vm_offset_t)(_v)) ? (socd_client_trace_arg_t)0 : (socd_client_trace_arg_t)(_v))
#define SOCD_PACK_2X32(h, l) ((((uint64_t)(SOCD_##h) & 0xffffffff) << 32) | ((uint64_t)(SOCD_##l) & 0xffffffff))
#define SOCD_PACK_LSB(h, lsb) ((((uint64_t)(SOCD_##h)) & 0xfffffffffffffffe) | ((uint64_t)(SOCD_##lsb) & 0x1))

/* Test macros for proper functionality locally before nominating. */
#if !defined(__arm64__)
static_assert(SOCD_PACK_2X32(VALUE(0xffff1000), VALUE(0xffff1200)) == 0xffff1000ffff1200, "PACK_2X32 failed to return expected output.");
static_assert(SOCD_PACK_LSB(VALUE(0xffff), VALUE(0x0)) == 0xfffe, "PACK_LSB failed to return expected output.");
#endif // !defined(__arm64__)

#define _SOCD_TRACE_XNU(cls, mode, func, ...) \
   SOCD_TRACE(KDBG_EVENTID(DBG_DRIVERS, DBG_SOCDIAGS, SOCD_TRACE_EVENTID(SOCD_TRACE_CLASS_XNU, mode, SOCD_TRACE_CODE_XNU_##cls)) | (func), ## __VA_ARGS__)
#define SOCD_TRACE_XNU(c, m, ...) _SOCD_TRACE_XNU(c, m, DBG_FUNC_NONE, ## __VA_ARGS__)
#define SOCD_TRACE_XNU_START(c, ...) _SOCD_TRACE_XNU(c, SOCD_TRACE_MODE_NONE, DBG_FUNC_START, ## __VA_ARGS__)
#define SOCD_TRACE_XNU_END(c, ...) _SOCD_TRACE_XNU(c, SOCD_TRACE_MODE_NONE, DBG_FUNC_END, ## __VA_ARGS__)

extern void socd_client_reinit(void);
extern void socd_client_trace(uint32_t debugid, socd_client_trace_arg_t arg1,
    socd_client_trace_arg_t arg2, socd_client_trace_arg_t arg3, socd_client_trace_arg_t arg4);

__END_DECLS

#endif /* !defined(_KERN_SOCD_CLIENT_KERN_H_) */
