zeroed area and reserved ASID. The user access routines
          restore the valid TTBR0_EL1 temporarily.
 
+config ARM64_TAGGED_ADDR_ABI
+       bool "Enable the tagged user addresses syscall ABI"
+       default y
+       help
+         When this option is enabled, user applications can opt in to a
+         relaxed ABI via prctl() allowing tagged addresses to be passed
+         to system calls as pointer arguments. For details, see
+         Documentation/arm64/tagged-address-abi.txt.
+
 menuconfig COMPAT
        bool "Kernel support for 32-bit EL0"
        depends on ARM64_4K_PAGES || EXPERT
 
 /* PR_PAC_RESET_KEYS prctl */
 #define PAC_RESET_KEYS(tsk, arg)       ptrauth_prctl_reset_keys(tsk, arg)
 
+#ifdef CONFIG_ARM64_TAGGED_ADDR_ABI
+/* PR_{SET,GET}_TAGGED_ADDR_CTRL prctl */
+long set_tagged_addr_ctrl(unsigned long arg);
+long get_tagged_addr_ctrl(void);
+#define SET_TAGGED_ADDR_CTRL(arg)      set_tagged_addr_ctrl(arg)
+#define GET_TAGGED_ADDR_CTRL()         get_tagged_addr_ctrl()
+#endif
+
 /*
  * For CONFIG_GCC_PLUGIN_STACKLEAK
  *
 
 #define TIF_SVE                        23      /* Scalable Vector Extension in use */
 #define TIF_SVE_VL_INHERIT     24      /* Inherit sve_vl_onexec across exec */
 #define TIF_SSBD               25      /* Wants SSB mitigation */
+#define TIF_TAGGED_ADDR                26      /* Allow tagged user addresses */
 
 #define _TIF_SIGPENDING                (1 << TIF_SIGPENDING)
 #define _TIF_NEED_RESCHED      (1 << TIF_NEED_RESCHED)
 
 {
        unsigned long ret, limit = current_thread_info()->addr_limit;
 
-       addr = untagged_addr(addr);
+       if (IS_ENABLED(CONFIG_ARM64_TAGGED_ADDR_ABI) &&
+           test_thread_flag(TIF_TAGGED_ADDR))
+               addr = untagged_addr(addr);
 
        __chk_user_ptr(addr);
        asm volatile(
 
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/stddef.h>
+#include <linux/sysctl.h>
 #include <linux/unistd.h>
 #include <linux/user.h>
 #include <linux/delay.h>
 #include <trace/events/power.h>
 #include <linux/percpu.h>
 #include <linux/thread_info.h>
+#include <linux/prctl.h>
 
 #include <asm/alternative.h>
 #include <asm/arch_gicv3.h>
        }
 }
 
+static void flush_tagged_addr_state(void)
+{
+       if (IS_ENABLED(CONFIG_ARM64_TAGGED_ADDR_ABI))
+               clear_thread_flag(TIF_TAGGED_ADDR);
+}
+
 void flush_thread(void)
 {
        fpsimd_flush_thread();
        tls_thread_flush();
        flush_ptrace_hw_breakpoint(current);
+       flush_tagged_addr_state();
 }
 
 void release_thread(struct task_struct *dead_task)
 
        ptrauth_thread_init_user(current);
 }
+
+#ifdef CONFIG_ARM64_TAGGED_ADDR_ABI
+/*
+ * Control the relaxed ABI allowing tagged user addresses into the kernel.
+ */
+static unsigned int tagged_addr_prctl_allowed = 1;
+
+long set_tagged_addr_ctrl(unsigned long arg)
+{
+       if (!tagged_addr_prctl_allowed)
+               return -EINVAL;
+       if (is_compat_task())
+               return -EINVAL;
+       if (arg & ~PR_TAGGED_ADDR_ENABLE)
+               return -EINVAL;
+
+       update_thread_flag(TIF_TAGGED_ADDR, arg & PR_TAGGED_ADDR_ENABLE);
+
+       return 0;
+}
+
+long get_tagged_addr_ctrl(void)
+{
+       if (!tagged_addr_prctl_allowed)
+               return -EINVAL;
+       if (is_compat_task())
+               return -EINVAL;
+
+       if (test_thread_flag(TIF_TAGGED_ADDR))
+               return PR_TAGGED_ADDR_ENABLE;
+
+       return 0;
+}
+
+/*
+ * Global sysctl to disable the tagged user addresses support. This control
+ * only prevents the tagged address ABI enabling via prctl() and does not
+ * disable it for tasks that already opted in to the relaxed ABI.
+ */
+static int zero;
+static int one = 1;
+
+static struct ctl_table tagged_addr_sysctl_table[] = {
+       {
+               .procname       = "tagged_addr",
+               .mode           = 0644,
+               .data           = &tagged_addr_prctl_allowed,
+               .maxlen         = sizeof(int),
+               .proc_handler   = proc_dointvec_minmax,
+               .extra1         = &zero,
+               .extra2         = &one,
+       },
+       { }
+};
+
+static int __init tagged_addr_init(void)
+{
+       if (!register_sysctl("abi", tagged_addr_sysctl_table))
+               return -EINVAL;
+       return 0;
+}
+
+core_initcall(tagged_addr_init);
+#endif /* CONFIG_ARM64_TAGGED_ADDR_ABI */
 
 # define PR_PAC_APDBKEY                        (1UL << 3)
 # define PR_PAC_APGAKEY                        (1UL << 4)
 
+/* Tagged user address controls for arm64 */
+#define PR_SET_TAGGED_ADDR_CTRL                55
+#define PR_GET_TAGGED_ADDR_CTRL                56
+# define PR_TAGGED_ADDR_ENABLE         (1UL << 0)
+
 #endif /* _LINUX_PRCTL_H */
 
 #ifndef PAC_RESET_KEYS
 # define PAC_RESET_KEYS(a, b)  (-EINVAL)
 #endif
+#ifndef SET_TAGGED_ADDR_CTRL
+# define SET_TAGGED_ADDR_CTRL(a)       (-EINVAL)
+#endif
+#ifndef GET_TAGGED_ADDR_CTRL
+# define GET_TAGGED_ADDR_CTRL()                (-EINVAL)
+#endif
 
 /*
  * this is where the system-wide overflow UID and GID are defined, for
                        return -EINVAL;
                error = PAC_RESET_KEYS(me, arg2);
                break;
+       case PR_SET_TAGGED_ADDR_CTRL:
+               error = SET_TAGGED_ADDR_CTRL(arg2);
+               break;
+       case PR_GET_TAGGED_ADDR_CTRL:
+               error = GET_TAGGED_ADDR_CTRL();
+               break;
        default:
                error = -EINVAL;
                break;