#ifndef __ASSEMBLY__
 
+#include <linux/bug.h>
+#include <asm/processor.h> /* for signal_minsigstksz, used by ARCH_DLINFO */
+
 typedef unsigned long elf_greg_t;
 
 #define ELF_NGREG (sizeof(struct user_pt_regs) / sizeof(elf_greg_t))
 do {                                                                   \
        NEW_AUX_ENT(AT_SYSINFO_EHDR,                                    \
                    (elf_addr_t)current->mm->context.vdso);             \
+                                                                       \
+       /*                                                              \
+        * Should always be nonzero unless there's a kernel bug.        \
+        * If we haven't determined a sensible value to give to         \
+        * userspace, omit the entry:                                   \
+        */                                                             \
+       if (likely(signal_minsigstksz))                                 \
+               NEW_AUX_ENT(AT_MINSIGSTKSZ, signal_minsigstksz);        \
+       else                                                            \
+               NEW_AUX_ENT(AT_IGNORE, 0);                              \
 } while (0)
 
 #define ARCH_HAS_SETUP_ADDITIONAL_PAGES
 
 #ifdef __KERNEL__
 
 #include <linux/build_bug.h>
+#include <linux/cache.h>
+#include <linux/init.h>
 #include <linux/stddef.h>
 #include <linux/string.h>
 
 void cpu_enable_cache_maint_trap(const struct arm64_cpu_capabilities *__unused);
 void cpu_clear_disr(const struct arm64_cpu_capabilities *__unused);
 
+extern unsigned long __ro_after_init signal_minsigstksz; /* sigframe size */
+extern void __init minsigstksz_setup(void);
+
 /* Userspace interface for PR_SVE_{SET,GET}_VL prctl()s: */
 #define SVE_SET_VL(arg)        sve_set_current_vl(arg)
 #define SVE_GET_VL()   sve_get_current_vl()
 
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <linux/cache.h>
 #include <linux/compat.h>
 #include <linux/errno.h>
 #include <linux/kernel.h>
        return 0;
 }
 
-/* Determine the layout of optional records in the signal frame */
-static int setup_sigframe_layout(struct rt_sigframe_user_layout *user)
+/*
+ * Determine the layout of optional records in the signal frame
+ *
+ * add_all: if true, lays out the biggest possible signal frame for
+ *     this task; otherwise, generates a layout for the current state
+ *     of the task.
+ */
+static int setup_sigframe_layout(struct rt_sigframe_user_layout *user,
+                                bool add_all)
 {
        int err;
 
                return err;
 
        /* fault information, if valid */
-       if (current->thread.fault_code) {
+       if (add_all || current->thread.fault_code) {
                err = sigframe_alloc(user, &user->esr_offset,
                                     sizeof(struct esr_context));
                if (err)
        if (system_supports_sve()) {
                unsigned int vq = 0;
 
-               if (test_thread_flag(TIF_SVE))
-                       vq = sve_vq_from_vl(current->thread.sve_vl);
+               if (add_all || test_thread_flag(TIF_SVE)) {
+                       int vl = sve_max_vl;
+
+                       if (!add_all)
+                               vl = current->thread.sve_vl;
+
+                       vq = sve_vq_from_vl(vl);
+               }
 
                err = sigframe_alloc(user, &user->sve_offset,
                                     SVE_SIG_CONTEXT_SIZE(vq));
        return sigframe_alloc_end(user);
 }
 
-
 static int setup_sigframe(struct rt_sigframe_user_layout *user,
                          struct pt_regs *regs, sigset_t *set)
 {
        int err;
 
        init_user_layout(user);
-       err = setup_sigframe_layout(user);
+       err = setup_sigframe_layout(user, false);
        if (err)
                return err;
 
                thread_flags = READ_ONCE(current_thread_info()->flags);
        } while (thread_flags & _TIF_WORK_MASK);
 }
+
+unsigned long __ro_after_init signal_minsigstksz;
+
+/*
+ * Determine the stack space required for guaranteed signal devliery.
+ * This function is used to populate AT_MINSIGSTKSZ at process startup.
+ * cpufeatures setup is assumed to be complete.
+ */
+void __init minsigstksz_setup(void)
+{
+       struct rt_sigframe_user_layout user;
+
+       init_user_layout(&user);
+
+       /*
+        * If this fails, SIGFRAME_MAXSZ needs to be enlarged.  It won't
+        * be big enough, but it's our best guess:
+        */
+       if (WARN_ON(setup_sigframe_layout(&user, true)))
+               return;
+
+       signal_minsigstksz = sigframe_size(&user) +
+               round_up(sizeof(struct frame_record), 16) +
+               16; /* max alignment padding */
+}