/* Dwarfless register access for arm64 */
/* TODO: handle 32-bit access of registers */

@__private30 global _reg_offsets[37], _r32_offsets[31]

probe init {
	/* Same order as pt_regs */
	_reg_offsets["x0"] =  0
	_reg_offsets["x1"] =  8
	_reg_offsets["x2"] = 16
	_reg_offsets["x3"] = 24
	_reg_offsets["x4"] = 32
	_reg_offsets["x5"] = 40
	_reg_offsets["x6"] = 48
	_reg_offsets["x7"] = 56
	_reg_offsets["x8"] = 64
	_reg_offsets["x9"] = 72
	_reg_offsets["x10"] = 80
	_reg_offsets["x11"] = 88
	_reg_offsets["x12"] = 96
	_reg_offsets["x13"] = 104
	_reg_offsets["x14"] = 112
	_reg_offsets["x15"] = 120
	_reg_offsets["x16"] = 128
	_reg_offsets["x17"] = 136
	_reg_offsets["x18"] = 144
	_reg_offsets["x19"] = 152
	_reg_offsets["x20"] = 160
	_reg_offsets["x21"] = 168
	_reg_offsets["x22"] = 176
	_reg_offsets["x23"] = 184
	_reg_offsets["x24"] = 192
	_reg_offsets["x25"] = 200
	_reg_offsets["x26"] = 208
	_reg_offsets["x27"] = 216
	_reg_offsets["x28"] = 224
	_reg_offsets["x29"] = 232	_reg_offsets["fp"] = 232
	_reg_offsets["x30"] = 240 	_reg_offsets["lr"] = 240
	_reg_offsets["sp"] = 248
	_reg_offsets["pc"] = 256
	_reg_offsets["pstate"] = 264
	_reg_offsets["orig_x0"] = 272

	_r32_offsets["w0"] =  0
	_r32_offsets["w1"] =  8
	_r32_offsets["w2"] = 16
	_r32_offsets["w3"] = 24
	_r32_offsets["w4"] = 32
	_r32_offsets["w5"] = 40
	_r32_offsets["w6"] = 48
	_r32_offsets["w7"] = 56
	_r32_offsets["w8"] = 64
	_r32_offsets["w9"] = 72
	_r32_offsets["w10"] = 80
	_r32_offsets["w11"] = 88
	_r32_offsets["w12"] = 96
	_r32_offsets["w13"] = 104
	_r32_offsets["w14"] = 112
	_r32_offsets["w15"] = 120
	_r32_offsets["w16"] = 128
	_r32_offsets["w17"] = 136
	_r32_offsets["w18"] = 144
	_r32_offsets["w19"] = 152
	_r32_offsets["w20"] = 160
	_r32_offsets["w21"] = 168
	_r32_offsets["w22"] = 176
	_r32_offsets["w23"] = 184
	_r32_offsets["w24"] = 192
	_r32_offsets["w25"] = 200
	_r32_offsets["w26"] = 208
	_r32_offsets["w27"] = 216
	_r32_offsets["w28"] = 224
	_r32_offsets["w29"] = 232
	_r32_offsets["w30"] = 240
}

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
}

function _stp_get_register_by_offset:long (offset:long) %{ /* pure */
	long value;
	struct pt_regs *regs;
	if (CONTEXT->sregs)
	  regs = CONTEXT->sregs;
	else
	  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",
				(long long)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) {
	reg32 = 0
  assert(registers_valid(), "cannot access CPU registers in this context")
	offset = _reg_offsets[name]
	if (offset == 0 && !(name in _reg_offsets)) {
        offset = _r32_offsets[name]
        if (offset == 0 && !(name in _r32_offsets)) {
          assert(0, "Unknown register: " . name)
        }
        reg32 = 1;
    }
	value = _stp_get_register_by_offset(offset)
	if (reg32 || probing_32bit_app()) {
		if (sign_extend)
			value = _stp_sign_extend32(value)
		else
			value &= 0xffffffff
	}
	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)
}

/*
 * 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.
 * TODO: 32-bit arm code has different calling conventions than arm64
 */
function _stp_arg:long (argnum:long, sign_extend:long, truncate:long) {
	val = 0
  assert(!(argnum < 1 || argnum > 8), sprintf("Cannot access arg(%d)", argnum))

	if (argnum == 1)
		val = u_register("x0")
	else if (argnum == 2)
		val = u_register("x1")
	else if (argnum == 3)
		val = u_register("x2")
	else if (argnum == 4)
		val = u_register("x3")
	else if (argnum == 5)
		val = u_register("x4")
	else if (argnum == 6)
		val = u_register("x5")
	else if (argnum == 7)
		val = u_register("x6")
	else if (argnum == 8)
		val = u_register("x7")

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

/*
 * Return a pointer to an argument given its offset in pt_regs.
 */
function _stp_offset_to_argptr:long (offset:long) %{
	struct pt_regs *regs = CONTEXT->sregs;

	if (!regs) {
                snprintf(CONTEXT->error_buffer, sizeof(CONTEXT->error_buffer),
                         "cannot access function args in this context");
                CONTEXT->last_error = CONTEXT->error_buffer;
                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",
				(long long)STAP_ARG_offset);
		CONTEXT->last_error = CONTEXT->error_buffer;
		return;
	}
	STAP_RETVALUE = (unsigned long)(((char *)regs) + STAP_ARG_offset);
%}

/*
 * Return a pointer to function argument #argnum (1=first arg).
 */
function _stp_get_argptr:long (argnum:long) {
	off = 0
  assert(!(argnum < 1 || argnum > 8), sprintf("Cannot access arg(%d)", argnum))

	if (argnum == 1)
		off = _reg_offsets["x0"]
	else if (argnum == 2)
		off = _reg_offsets["x1"]
	else if (argnum == 3)
		off = _reg_offsets["x2"]
	else if (argnum == 4)
		off = _reg_offsets["x3"]
	else if (argnum == 5)
		off = _reg_offsets["x4"]
	else if (argnum == 6)
		off = _reg_offsets["x5"]
	else if (argnum == 7)
		off = _reg_offsets["x6"]
	else if (argnum == 8)
		off = _reg_offsets["x7"]

	return _stp_offset_to_argptr(off)
}

/* 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_32bit_app()) {
		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)
}

/* Set the value of function arg #argnum (1=first arg) as a signed int. */
function set_int_arg:long (argnum:long, value:long) {
	return set_kernel_int(_stp_get_argptr(argnum), value)
}

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

function set_long_arg:long (argnum:long, value:long) {
	return set_kernel_long(_stp_get_argptr(argnum), value)
}

function set_ulong_arg:long (argnum:long, value:long) {
	return set_long_arg(argnum, value)
}

function set_longlong_arg:long (argnum:long, value:long) {
	if (probing_32bit_app()) {
		highbits = value >> 32
		lowbits = value & 0xffffffff
		set_u32_arg(argnum, lowbits)
		set_u32_arg(argnum+1, highbits)
	} else
		set_long_arg(argnum, value)
}

function set_ulonglong_arg:long (argnum:long, value:long) {
	return set_longlong_arg(argnum, value)
}

function set_pointer_arg:long (argnum:long, value:long) {
	return set_kernel_pointer(_stp_get_argptr(argnum), value)
}

function set_s32_arg:long (argnum:long, value:long) {
	return set_int_arg(argnum, value)
}

function set_u32_arg:long (argnum:long, value:long) {
	return set_uint_arg(argnum, value)
}

function set_s64_arg:long (argnum:long, value:long) {
	return set_longlong_arg(argnum, value)
}

function set_u64_arg:long (argnum:long, value:long) {
	return set_ulonglong_arg(argnum, value)
}
function asmlinkage() %{ /* pure */ %}

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

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