// syscalls tapset macros
// Copyright (C) 2013-2019 Red Hat Inc.
//
// This file is part of systemtap, and is free software.  You can
// redistribute it and/or modify it under the terms of the GNU General
// Public License (GPL); either version 2, or (at your option) any
// later version.


@define __syscall_nr_gate(syscall_nr)
%(
	# Here we don't care if we're in a compat task or not, we just
	# want to make sure we've got the correct syscall number.
	#
	# If _stp_syscall_nr() fails, that means we aren't in user
	# context. So, skip this call.
	try { if (_stp_syscall_nr() != @syscall_nr) next } catch { next }
%)

@define __syscall_gate(syscall_nr)
%(
	# On some platforms (like s390x and ppc64), the 32-bit
	# syscalls use the same syscall number as the 64-bit
	# syscall. So, we have to check to see if this is a
	# 32-on-64-bit user task.
	@__syscall_gate_compat_simple

	# If _stp_syscall_nr() fails, that means we aren't in user
	# context. So, skip this call.
	try { if (_stp_syscall_nr() != @syscall_nr) next } catch { next }
%)

@define __syscall_gate2(syscall_nr1, syscall_nr2)
%(
	# On some platforms (like s390x and ppc64), the 32-bit
	# syscalls use the same syscall number as the 64-bit
	# syscall. So, we have to check to see if this is a
	# 32-on-64-bit user task.
	@__syscall_gate_compat_simple

	try { __nr = _stp_syscall_nr() } catch { next }
	if ((__nr != @syscall_nr1) && (__nr != @syscall_nr2)) next
%)

@define __syscall_compat_gate(syscall_nr, compat_syscall_nr)
%(
    %( CONFIG_COMPAT == "y" %?
	try { __nr = _stp_syscall_nr() } catch { next }
	if (@__compat_task) {
		if (__nr != @compat_syscall_nr)
			next
	}
	else if (__nr != @syscall_nr)
		next
    %:
	try { if (_stp_syscall_nr() != @syscall_nr) next } catch { next }
    %)
%)

@define __compat_syscall_gate(compat_syscall_nr)
%(
	@__syscall_gate_noncompat_simple
    %( CONFIG_COMPAT == "y" %?
	try { __nr = _stp_syscall_nr() } catch { next }
	if (@__compat_task) {
		if (__nr != @compat_syscall_nr) next
	}
    %)
%)

@define __compat_syscall_gate_negative(compat_syscall_nr)
%(
    %( CONFIG_COMPAT == "y" %?
	try { __nr = _stp_syscall_nr() } catch { next }
	if (@__compat_task) {
		if (__nr == @compat_syscall_nr) next
	}
    %)
%)

@define __syscall_gate_compat_simple
%(
    %( CONFIG_COMPAT == "y" %?
        if (@__compat_task) next
    %)
%)

@define __syscall_gate_noncompat_simple
%(
    %( CONFIG_COMPAT == "y" %?
        if (! @__compat_task) next
    %:
	next
    %)
%)
@define __pointer(val)
%(
    %( CONFIG_64BIT == "y" %?
	%( CONFIG_COMPAT == "y" %?
	    # If this is a compat task, the high bits of a pointer may be
	    # garbage.
	    (@__compat_task ? (@val & 0xffffffff) : @val)
	%:
	    @val
	%)
    %:
	(@val & 0xffffffff)
    %)
%)

@define _af_inet_info_u(my_addr_uaddr, addrlen)
%(
  %( systemtap_v >= "2.5" %?
     uaddr_af = _struct_sockaddr_u_sa_family(@my_addr_uaddr, @addrlen)
     if (_struct_sockaddr_u_sa_family(@my_addr_uaddr, @addrlen) =~ "AF_INET.*") {
       uaddr_ip = _struct_sockaddr_u_ip_addr(@my_addr_uaddr, @addrlen)
       uaddr_ip_port = _struct_sockaddr_u_tcp_port(@my_addr_uaddr, @addrlen)
       if (_struct_sockaddr_u_sa_family(@my_addr_uaddr, @addrlen) == "AF_INET6") {
         uaddr_ipv6_flowinfo =  _struct_sockaddr_u_ipv6_flowinfo(@my_addr_uaddr, @addrlen)
         uaddr_ipv6_scope_id =  _struct_sockaddr_u_ipv6_scope_id(@my_addr_uaddr, @addrlen)
       }
     }
  %)
%)

# The following argstr processing is modeled on strace's handling of
# futex. Because it is so complicated and is used in 4 places
# (syscall.futex, syscall.compat_futex, nd_syscall.futex, and
# nd_syscall.compat_futex), a macro is used to handle all the
# details.
@define __futex_argstr(uaddr, op, val, utime, uaddr2, val3)
%(
	if (@op == @const("FUTEX_WAKE_BITSET")) {
		argstr = sprintf("%p, %s, %d, %#x", @uaddr, _futex_op_str(@op),
				 @val, @val3)
	}
	else if (@op == @const("FUTEX_WAIT")) {
		argstr = sprintf("%p, %s, %d, %s", @uaddr, _futex_op_str(@op),
				 @val,
				 (@__compat_task
				  ? _struct_compat_timespec_u(@utime, 1)
				  : _struct_timespec_u(@utime, 1)))
	}
	else if (@op == @const("FUTEX_WAIT_BITSET")) {
		argstr = sprintf("%p, %s, %d, %s, %#x", @uaddr,
				 _futex_op_str(@op), @val,
				 (@__compat_task
				  ? _struct_compat_timespec_u(@utime, 1)
				  : _struct_timespec_u(@utime, 1)), @val3)
	}
	else if (@op == @const("FUTEX_REQUEUE")) {
		argstr = sprintf("%p, %s, %d, %d, %p", @uaddr,
				 _futex_op_str(@op), @val, @utime,
				 @uaddr2)
	}
	else if (@op == @const("FUTEX_CMP_REQUEUE")
		 || @op == @const("FUTEX_CMP_REQUEUE_PI")) {
		argstr = sprintf("%p, %s, %d, %u, %p, %d", @uaddr,
				 _futex_op_str(@op), @val, @utime,
				 @uaddr2, @val3)
	}
	else if (@op == @const("FUTEX_WAKE_OP")) {
		argstr = sprintf("%p, %s, %d, %u, %p, %s", @uaddr,
				 _futex_op_str(@op), @val, @utime,
				 @uaddr2, _futex_wake_op_str(@val3))
	}
	else if (@op == @const("FUTEX_WAIT_REQUEUE_PI")) {
		argstr = sprintf("%p, %s, %d, %s, %p", @uaddr,
				 _futex_op_str(@op), @val,
				 (@__compat_task
				  ? _struct_compat_timespec_u(@utime, 1)
				  : _struct_timespec_u(@utime, 1)),
				 @uaddr2)
	}
	else {
		argstr = sprintf("%p, %s, %d", @uaddr, _futex_op_str(@op),
				 @val)
	}
%)

%( systemtap_v <= "2.9" %?
@define __int32_compat(arg)
%(
    %( CONFIG_64BIT == "y" %?
        (@__compat_task ? __int32(@arg) : @arg)
    %:
        (__int32(@arg))
    %)
%)
%)

@define __keyctl_argstr(option, arg2, arg3, arg4, arg5)
%(
        if ((@option == @const("KEYCTL_READ")) ||
                        (@option == @const("KEYCTL_DESCRIBE")) ||
                        (@option == @const("KEYCTL_GET_SECURITY"))) {
                argstr = sprintf("%s, %s, %p, %d", _stp_keyctl_cmd_str(@option),
				 _stp_keyctl_keyring_shortcut_str(@arg2),
				 @arg3, @__compat_long(@arg4))
        }
        else if (@option == @const("KEYCTL_UPDATE")) {
                argstr = sprintf("%s, %d, %s, %d", _stp_keyctl_cmd_str(@option),
				 @__compat_long(@arg2), user_string_quoted(@arg3),
				 @__compat_long(@arg4))
        }
        else if ((@option == @const("KEYCTL_REVOKE")) ||
                        (@option == @const("KEYCTL_CLEAR")) ||
                        (@option == @const("KEYCTL_INVALIDATE")) ||
                        (@option == @const("KEYCTL_ASSUME_AUTHORITY"))) {
                argstr = sprintf("%s, %d", _stp_keyctl_cmd_str(@option),
				 @__compat_long(@arg2))
        }
        else if ((@option == @const("KEYCTL_LINK")) ||
                        (@option == @const("KEYCTL_UNLINK"))) {
                argstr = sprintf("%s, %d, %d", _stp_keyctl_cmd_str(@option),
				 @__compat_long(@arg2), @__compat_long(@arg3))
        }
        else if (@option == @const("KEYCTL_SEARCH")) {
                argstr = sprintf("%s, %d, %s, %s, %d", _stp_keyctl_cmd_str(@option),
				 @__compat_long(@arg2), user_string_quoted(@arg3),
				 user_string_quoted(@arg4), @__compat_long(@arg5))
        }
        else if (@option == @const("KEYCTL_CHOWN")) {
                argstr = sprintf("%s, %d, %d, %d", _stp_keyctl_cmd_str(@option),
				 @__compat_long(@arg2), @__compat_long(@arg3),
				 @__compat_long(@arg4))
        }
        else if (@option == @const("KEYCTL_SETPERM")) {
                argstr = sprintf("%s, %d, %s", _stp_keyctl_cmd_str(@option),
				 @__compat_long(@arg2), _stp_keyctl_perm_str(@arg3))
        }
        else if ((@option == @const("KEYCTL_INSTANTIATE"))) {
                argstr = sprintf("%s, %d, %s, %d, %d", _stp_keyctl_cmd_str(@option),
				 @__compat_long(@arg2), user_string_quoted(@arg3),
				 @__compat_long(@arg4), @__compat_long(@arg5))
        }
        else if ((@option == @const("KEYCTL_INSTANTIATE_IOV"))) {
                argstr = sprintf("%s, %d, %p, %d, %d", _stp_keyctl_cmd_str(@option),
				 @__compat_long(@arg2), @arg3, @__compat_long(@arg4),
				 @__compat_long(@arg5))
        }
        else if (@option == @const("KEYCTL_NEGATE")) {
                argstr = sprintf("%s, %d, %u, %d", _stp_keyctl_cmd_str(@option),
				 @__compat_long(@arg2), @arg3, @__compat_long(@arg4))
        }
        else if (@option == @const("KEYCTL_REJECT")) {
                argstr = sprintf("%s, %d, %u, %u, %d", _stp_keyctl_cmd_str(@option),
				 @__compat_long(@arg2), @arg3, @arg4,
				 @__compat_long(@arg5))
        }
        else if (@option == @const("KEYCTL_SET_TIMEOUT")) {
                argstr = sprintf("%s, %d, %u", _stp_keyctl_cmd_str(@option),
				 @__compat_long(@arg2), @arg3)
        }
        else if (@option == @const("KEYCTL_GET_KEYRING_ID")) {
                argstr = sprintf("%s, %s, %d", _stp_keyctl_cmd_str(@option),
				 _stp_keyctl_keyring_shortcut_str(@arg2),
				 @__compat_long(@arg3))
        }
        else if (@option == @const("KEYCTL_SET_REQKEY_KEYRING")) {
                argstr = sprintf("%s, %s", _stp_keyctl_cmd_str(@option),
				 _stp_keyctl_default_keyrings_str(@arg2))
        }
        else if (@option == @const("KEYCTL_JOIN_SESSION_KEYRING")) {
                argstr = sprintf("%s, %p", _stp_keyctl_cmd_str(@option), @arg2)
        }
        else if (@option == @const("KEYCTL_GET_PERSISTENT")) {
                argstr = sprintf("%s, %d, %d", _stp_keyctl_cmd_str(@option),
				 @__compat_long(@arg2), @__compat_long(@arg3))
        }
        else {
                argstr = sprintf("%d, %d, %d, %d, %d", @option,
				 @__compat_long(@arg2), @__compat_long(@arg3),
				 @__compat_long(@arg4), @__compat_long(@arg5))
        }
%)

@define __quotactl_argstr(subcmd, arg1, arg2, arg3, arg4)
%(
	if (@subcmd == @const("Q_QUOTAON")) {
		argstr = sprintf("%s, %s, %s, %s",
		                 _quotactl_cmd_str(@arg1), user_string_quoted(@arg2),
		                 _quotactl_quota_type_str(@arg3),
		                 user_string_quoted(@arg4))
	}
	else if ((@subcmd == @const("Q_GETQUOTA")) ||
	         (@subcmd == @const("Q_SETQUOTA"))) {
		argstr = sprintf("%s, %s, %d, %s",
		                 _quotactl_cmd_str(@arg1), user_string_quoted(@arg2),
		                 @arg3, _struct_dqblk_u(@arg4))
	}
	else if ((@subcmd == @const("Q_GETINFO")) ||
	         (@subcmd == @const("Q_SETINFO"))) {
		argstr = sprintf("%s, %s, %d, %s",
		                 _quotactl_cmd_str(@arg1), user_string_quoted(@arg2),
		                 @arg3, _struct_dqinfo_u(@arg4))
	}
	else if (@subcmd == @const("Q_GETFMT")) {
		argstr = sprintf("%s, %s, %d, %x",
		                 _quotactl_cmd_str(@arg1), user_string_quoted(@arg2),
		                 @arg3, @arg4)
	}
	else if ((@subcmd == @const("Q_SYNC")) ||
	         (@subcmd == @const("Q_QUOTAOFF"))) {
		argstr = sprintf("%s, %s, %d, %p",
		                 _quotactl_cmd_str(@arg1), user_string_quoted(@arg2),
		                 @arg3, @arg4)
	}
	else {
		argstr = sprintf("%s, %p, %d, %p", _quotactl_cmd_str(@arg1),
		                 @arg2, @arg3, @arg4)
	}
%)

# @__compat_ulong() macro adds the ability to distinguish between compat and non-compat
# task to the __ulong() function making it full-featured couterpart to ulong_arg().
@define __compat_ulong(arg)
%(
	%( CONFIG_COMPAT == "y" %?
		(@__compat_task ? __uint32(@arg) : __ulong(@arg))
	%:
		__ulong(@arg)
	%)
%)

# @__compat_long() macro adds the ability to distinguish between compat and non-compat
# task to the __long() function making it full-featured couterpart to long_arg().
@define __compat_long(arg)
%(
	%( CONFIG_COMPAT == "y" %?
		(@__compat_task ? __int32(@arg) : __long(@arg))
	%:
		__long(@arg)
	%)
%)

@define __BIG_ENDIAN
%(
%{ /* pure */
#ifdef __BIG_ENDIAN
  1
#else
  0
#endif
%}
%)

# @__pos_from_hilo() is lifted straight from fs/read_write.c and only
# used by preadv*/pwritev*.
@define __pos_from_hilo(high, low)
%(
  (((__ulong(@high) << (@const("BITS_PER_LONG") / 2)) << (@const("BITS_PER_LONG") / 2)) | __ulong(@low))
%)

# Here's an explanation of the @cast() 3rd argument here. In RHEL6-era
# kernels (2.6.32), the "ulong" and "unsigned int" definitions can be
# found in <linux/types.h>. However, in newer kernels those
# definitions have been moved to <uapi/linux/types.h>.  But, we can't
# do "kernel<linux/types.h><uapi/linux/types.h>" because the
# <uapi/linux/types.h> doesn't exist on RHEL6-era kernels. So, we list
# each header file separately.

@define ulong_cast(val)
%(
  @cast(@val, "ulong", "kernel<linux/types.h>:kernel<uapi/linux/types.h>")
%)

@define uint_cast(val)
%(
  @cast(@val, "unsigned int", "kernel<linux/types.h>:kernel<uapi/linux/types.h>")
%)


// As other architectures adopt the 4.17 syscall wrapper mechanism,
// these macros will have to be conditionalized on %( arch %).
        
%( arch == "ia64" %?
@define arch_syscall_prefix %( "" %)
@define arch_syscall0_prefix %( "" %)
@define SYSC_CLONE_CLONE_FLAGS_ARGNO %( 1 %)
@define SYSC_CLONE_STACK_START_ARGNO %( 2 %)
@define SYSC_CLONE_PARENT_TID_UADDR_ARGNO %( 4 %)
@define SYSC_CLONE_CHILD_TID_UADDR_ARGNO %( 5 %)
%)

%( arch == "s390" %?
@define arch_syscall_prefix %( "" %)
@define arch_syscall0_prefix %( "" %)
@define SYSC_CLONE_CLONE_FLAGS_ARGNO %( 2 %)
@define SYSC_CLONE_STACK_START_ARGNO %( 1 %)
@define SYSC_CLONE_PARENT_TID_UADDR_ARGNO %( 3 %)
@define SYSC_CLONE_CHILD_TID_UADDR_ARGNO %( 4 %)
%)

%( arch == "x86_64" %?
@define arch_syscall_prefix %( "__{ia32,x64}_" %)
@define arch_syscall0_prefix %( "__x64_" %)
@define SYSC_CLONE_CLONE_FLAGS_ARGNO %( 1 %)
@define SYSC_CLONE_STACK_START_ARGNO %( 2 %)
@define SYSC_CLONE_PARENT_TID_UADDR_ARGNO %( 3 %)
@define SYSC_CLONE_CHILD_TID_UADDR_ARGNO %( @__compat_task ? 5 : 4 %)
%)

%( arch == "arm64" %?
@define arch_syscall_prefix %( "__arm64_" %)
@define arch_syscall0_prefix %( "__arm64_" %)
@define SYSC_CLONE_CLONE_FLAGS_ARGNO %( 1 %)
@define SYSC_CLONE_STACK_START_ARGNO %( 2 %)
@define SYSC_CLONE_PARENT_TID_UADDR_ARGNO %( 3 %)
@define SYSC_CLONE_CHILD_TID_UADDR_ARGNO %( 5 %)
%)

%( arch == "i386" || arch == "arm" || arch == "powerpc" || arch == "mips" %?
@define arch_syscall_prefix %( "" %)
@define arch_syscall0_prefix %( "" %)
@define SYSC_CLONE_CLONE_FLAGS_ARGNO %( 1 %)
@define SYSC_CLONE_STACK_START_ARGNO %( 2 %)
@define SYSC_CLONE_PARENT_TID_UADDR_ARGNO %( 3 %)
@define SYSC_CLONE_CHILD_TID_UADDR_ARGNO %( 5 %)
%)


@define SYSC_RETVALSTR(val)
%(
  %( systemtap_v >= "4.0" %? retval = @val; %)
  %( systemtap_v <= "4.0" %? set_returnval(@val); %)
  retstr = return_str(1,@val);
%)

@define SYSC_RETVALSTR2(val)
%(
  %( systemtap_v >= "4.0" %? retval = @val; %)
  %( systemtap_v <= "4.0" %? set_returnval(@val); %)
  retstr = return_str(2,@val);
%)

@define SYSC_RETVALSTR3(val)
%(
  %( systemtap_v >= "4.0" %? retval = @val; %)
  %( systemtap_v <= "4.0" %? set_returnval(@val); %)
  retstr = return_str(3,@val);
%)
