/* Dwarfless register access for mips */

global _reg_offsets, _stp_regs_registered

function _stp_register_regs() {
	offset_acx = 0
%( CONFIG_64BIT == "y" %?
        /* n32/n64 registers naming scheme */
	/* Same order as struct pt_regs */
	_reg_offsets["zero"] = 0
	_reg_offsets["at"] = 8
	_reg_offsets["v0"] = 16
	_reg_offsets["v1"] = 24
	_reg_offsets["a0"] = 32
	_reg_offsets["a1"] = 40
	_reg_offsets["a2"] = 48
	_reg_offsets["a3"] = 56
	_reg_offsets["a4"] = 64
	_reg_offsets["a5"] = 72
	_reg_offsets["a6"] = 80
	_reg_offsets["a7"] = 88
	_reg_offsets["t0"] = 96
	_reg_offsets["t1"] = 104
	_reg_offsets["t2"] = 112
	_reg_offsets["t3"] = 120
	_reg_offsets["s0"] = 128
	_reg_offsets["s1"] = 136
	_reg_offsets["s2"] = 144
	_reg_offsets["s3"] = 152
	_reg_offsets["s4"] = 160
	_reg_offsets["s5"] = 168
	_reg_offsets["s6"] = 176
	_reg_offsets["s7"] = 184
	_reg_offsets["t8"] = 192
	_reg_offsets["t9"] = 200
	_reg_offsets["k0"] = 208
	_reg_offsets["k1"] = 216
	_reg_offsets["gp"] = 224
	_reg_offsets["sp"] = 232
	_reg_offsets["s8"] = 240       _reg_offsets["fp"] = 240
	_reg_offsets["ra"] = 248

	_reg_offsets["status"] = 256
	_reg_offsets["hi"] = 264
	_reg_offsets["lo"] = 272

%( CONFIG_CPU_HAS_SMARTMIPS == "y" %?
	_reg_offsets["acx"] = 280
	offset_acx = 8
%)
	_reg_offsets["badvaddr"] = 280 + offset_acx
	_reg_offsets["cause"] = 288 + offset_acx
	_reg_offsets["epc"] = 296 + offset_acx

        /* no cp0_tcstatus register for now */
%( CONFIG_CPU_CAVIUM_OCTEON == "y" %?
	_reg_offsets["mpl0"] = 304 + offset_acx
	_reg_offsets["mpl1"] = 310 + offset_acx
	_reg_offsets["mpl2"] = 318 + offset_acx
	_reg_offsets["mtp0"] = 316 + offset_acx
	_reg_offsets["mtp1"] = 324 + offset_acx
	_reg_offsets["mtp2"] = 330 + offset_acx
%)

%:
        /* o32 registers naming scheme */
	/* Same order as struct pt_regs */
	_reg_offsets["zero"] = 24
	_reg_offsets["at"] = 28
	_reg_offsets["v0"] = 32
	_reg_offsets["v1"] = 36
	_reg_offsets["a0"] = 40
	_reg_offsets["a1"] = 44
	_reg_offsets["a2"] = 48
	_reg_offsets["a3"] = 52
	_reg_offsets["t0"] = 56
	_reg_offsets["t1"] = 60
	_reg_offsets["t2"] = 64
	_reg_offsets["t3"] = 68
	_reg_offsets["t4"] = 72
	_reg_offsets["t5"] = 76
	_reg_offsets["t6"] = 80
	_reg_offsets["t7"] = 84
	_reg_offsets["s0"] = 88
	_reg_offsets["s1"] = 92
	_reg_offsets["s2"] = 96
	_reg_offsets["s3"] = 100
	_reg_offsets["s4"] = 104
	_reg_offsets["s5"] = 108
	_reg_offsets["s6"] = 112
	_reg_offsets["s7"] = 116
	_reg_offsets["t8"] = 120
	_reg_offsets["t9"] = 124
	_reg_offsets["k0"] = 128
	_reg_offsets["k1"] = 132
	_reg_offsets["gp"] = 136
	_reg_offsets["sp"] = 140
	_reg_offsets["s8"] = 144     _reg_offsets["fp"] = 144
	_reg_offsets["ra"] = 148

	_reg_offsets["status"] = 152
	_reg_offsets["hi"] = 156
	_reg_offsets["lo"] = 160

%( CONFIG_CPU_HAS_SMARTMIPS == "y" %?
	_reg_offsets["acx"] = 164
	offset_acx = 4
%)
	_reg_offsets["badvaddr"] = 164 + offset_acx
	_reg_offsets["cause"] = 168 + offset_acx
	_reg_offsets["epc"] = 172 + offset_acx

        /* no cp0_tcstatus register for now */
        /* no mpl and mtp registers for now */
%)
	_stp_regs_registered = 1
}

function probing_app_with_32bit_regs() %{ /* pure */
	struct pt_regs *regs;
	regs = (CONTEXT->user_mode_p ? CONTEXT->uregs : NULL );
        STAP_RETVALUE = _stp_probing_app_with_32bit_regs(regs);
%}

function _stp_get_register_by_offset:long (offset:long) %{ /* pure */
	long value;
	struct pt_regs *regs;
	regs = (CONTEXT->user_mode_p ? CONTEXT->uregs : CONTEXT->kregs);

	if (!regs) {
		CONTEXT->last_error = "No registers available in this context";
		return;
	}
	if (STAP_ARG_offset < 0 || STAP_ARG_offset > sizeof(struct pt_regs) - sizeof(long)) {
		snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer),
				"Bad register offset: %lld", STAP_ARG_offset);
		CONTEXT->last_error = CONTEXT->error_buffer;
		return;
	}
	memcpy(&value, ((char *)regs) + STAP_ARG_offset, sizeof(value));
	STAP_RETVALUE = value;
%}

function _stp_sign_extend32:long (value:long) {
	if (value & 0x80000000)
		value |= (0xffffffff << 32)
	return value
}

function _stp_register:long (name:string, sign_extend:long) {
	if (!registers_valid()) {
		error("cannot access CPU registers in this context")
		return 0
	}
	if (!_stp_regs_registered)
		_stp_register_regs()
	offset = _reg_offsets[name]
	if (offset == 0 && !(name in _reg_offsets)) {
		error("Unknown register: " . name)
		return 0
	}
	value = _stp_get_register_by_offset(offset)
%( CONFIG_64BIT == "y" %?
	if (probing_app_with_32bit_regs()) {
%)
		if (sign_extend)
			value = _stp_sign_extend32(value)
		else
			value &= 0xffffffff
%( CONFIG_64BIT == "y" %?
	}
%)
	return value
}

/* Return the named register value as a signed value. */
function register:long (name:string) {
	return _stp_register(name, 1)
}

/*
 * Return the named register value as an unsigned value.  Specifically,
 * don't sign-extend the register value when promoting it to 64 bits.
 */
function u_register:long (name:string) {
	return _stp_register(name, 0)
}


/* Dwarfless register access for mips */

%{
// These functions are largely lifted from arch/mips/include/ptrace.h.

static inline unsigned long _stp_kernel_stack_pointer(struct pt_regs *regs)
{
	return regs->regs[31];
}

/**
 * _stp_regs_within_kernel_stack() - check the address in the stack
 * @regs:       pt_regs which contains kernel stack pointer.
 * @addr:       address which is checked.
 *
 * _stp_regs_within_kernel_stack() checks @addr is within the kernel stack page(s).
 * If @addr is within the kernel stack, it returns true. If not, returns false.
 */
static inline int _stp_regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
{
	return ((addr & ~(THREAD_SIZE - 1))  ==
		(_stp_kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1)));
}

/*
 * _stp_regs_get_kernel_stack_nth_addr() - get address of the Nth entry of the stack
 * @regs:       pt_regs which contains kernel stack pointer.
 * @n:          stack entry number.
 *
 * _stp_regs_get_kernel_stack_nth_addr() returns the address of the @n
 * th entry of the kernel stack which is specified by @regs. If the @n
 * th entry is NOT in the kernel stack, this returns 0.
 */
long *
_stp_regs_get_kernel_stack_nth_addr(struct pt_regs *regs, unsigned int n)
{
        long *addr = (unsigned long *)_stp_kernel_stack_pointer(regs);
        addr += n;
        if (_stp_regs_within_kernel_stack(regs, (unsigned long)addr))
                return addr;
        else
                return 0;
}

%}

function _stp_get_stack_nth:long (n:long)
%{ /* pure */
        __label__ deref_fault;
        unsigned int n = (unsigned int)STAP_ARG_n;
        struct pt_regs *regs;
        long *addr;

        STAP_RETVALUE = 0;
        if (CONTEXT->user_mode_p) {
                // This function only handles kernel arguments off the stack.
                snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer),
                        "cannot access function args in this context");
                CONTEXT->last_error = CONTEXT->error_buffer;
                return;
        }
        regs = CONTEXT->kregs;
        if (!regs) {
                snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer),
                        "cannot access function args in this context");
                CONTEXT->last_error = CONTEXT->error_buffer;
                return;
        }

        /* Get the address of the nth item on the stack. */
        addr = _stp_regs_get_kernel_stack_nth_addr(regs, n);
        if (addr == NULL) {
                snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer),
                         "cannot access stack arg(%d)", n);
                CONTEXT->last_error = CONTEXT->error_buffer;
                return;
        }
        STAP_RETVALUE = kread(addr);
        return;

deref_fault: /* branched to from kread() */
        snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer),
                 "kernel fault at %#lx accessing stack arg(%d)",
                 (unsigned long)addr, n);
        CONTEXT->last_error = CONTEXT->error_buffer;
%}

/*
 * Return the value of function arg #argnum (1=first arg).
 * If truncate=1, mask off the top 32 bits.
 * If sign_extend=1 and (truncate=1 or the probepoint we've hit is in a
 * 32-bit app), sign-extend the 32-bit value.
 */
function _stp_arg:long (argnum:long, sign_extend:long, truncate:long) {
	val = 0
	if (argnum < 1 || argnum > 8) {
		error(sprintf("Cannot access arg(%d)", argnum))
		return 0
	}

	if (argnum == 1)
		val = u_register("a0")
	else if (argnum == 2)
		val = u_register("a1")
	else if (argnum == 3)
		val = u_register("a2")
	else if (argnum == 4)
		val = u_register("a3")
%( CONFIG_64BIT == "y" %?
	else if (argnum == 5)
		val = u_register("a4")
	else if (argnum == 6)
		val = u_register("a5")
	else if (argnum == 7)
		val = u_register("a6")
	else if (argnum == 8)
		val = u_register("a7")
%:
	else
		val = _stp_get_stack_nth(argnum - 5)
%)

	if (truncate) {
		if (sign_extend)
			val = _stp_sign_extend32(val)
		else
			/* High bits may be garbage. */
			val = (val & 0xffffffff);
	}
	return val;
}

function probing_32bit_app:long() %{ /* pure */
	STAP_RETVALUE = (CONTEXT->user_mode_p && _stp_is_compat_task());
%}

function arch_bytes:long() %{ /* pure */
  STAP_RETVALUE = sizeof(long);
%}

function uarch_bytes:long() {
  assert(user_mode(), "requires user mode")
  return probing_32bit_app() ? 4 : 8
}

/* Return the value of function arg #argnum (1=first arg) as a signed int. */
function int_arg:long (argnum:long) {
	return _stp_arg(argnum, 1, 1)
}

/* Return the value of function arg #argnum (1=first arg) as an unsigned int. */
function uint_arg:long (argnum:long) {
	return _stp_arg(argnum, 0, 1)
}

function long_arg:long (argnum:long) {
	return _stp_arg(argnum, 1, 0)
}

function ulong_arg:long (argnum:long) {
	return _stp_arg(argnum, 0, 0)
}

function longlong_arg:long (argnum:long) {
	if (probing_app_with_32bit_regs()) {
		lowbits = _stp_arg(argnum, 0, 1)
		highbits = _stp_arg(argnum+1, 0, 1)
		return ((highbits << 32) | lowbits)
	} else
		return _stp_arg(argnum, 0, 0)
}

function ulonglong_arg:long (argnum:long) {
	return longlong_arg(argnum)
}

function pointer_arg:long (argnum:long) {
	return _stp_arg(argnum, 0, 0)
}

function s32_arg:long (argnum:long) {
	return int_arg(argnum)
}

function u32_arg:long (argnum:long) {
	return uint_arg(argnum)
}

function s64_arg:long (argnum:long) {
	return longlong_arg(argnum)
}

function u64_arg:long (argnum:long) {
	return ulonglong_arg(argnum)
}

function asmlinkage() %{ /* pure */ %}

function fastcall() %{ /* pure */ %}

function regparm() %{
	snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer),
		"regparm is invalid on mips.");
	CONTEXT->last_error = CONTEXT->error_buffer;
%}
