[S390] 64-bit register support for 31-bit processes
Heiko Carstens [Tue, 6 Oct 2009 08:34:13 +0000 (10:34 +0200)]
From: Heiko Carstens <heiko.carstens@de.ibm.com>
From: Martin Schwidefsky <schwidefsky@de.ibm.com>

Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

arch/s390/include/asm/elf.h
arch/s390/include/asm/ptrace.h
arch/s390/include/asm/ucontext.h
arch/s390/kernel/compat_signal.c
arch/s390/kernel/ptrace.c
arch/s390/kernel/setup.c
include/linux/elf.h

index 74d0bbb..e885442 100644 (file)
 /* Keep this the last entry.  */
 #define R_390_NUM      61
 
+/* Bits present in AT_HWCAP. */
+#define HWCAP_S390_ESAN3       1
+#define HWCAP_S390_ZARCH       2
+#define HWCAP_S390_STFLE       4
+#define HWCAP_S390_MSA         8
+#define HWCAP_S390_LDISP       16
+#define HWCAP_S390_EIMM                32
+#define HWCAP_S390_DFP         64
+#define HWCAP_S390_HPAGE       128
+#define HWCAP_S390_ETF3EH      256
+#define HWCAP_S390_HIGH_GPRS   512
+
 /*
  * These are used to set parameters in the core dumps.
  */
index 539263f..95dcf18 100644 (file)
@@ -311,6 +311,10 @@ typedef struct
        __u32           orig_gpr2;
 } s390_compat_regs;
 
+typedef struct
+{
+       __u32           gprs_high[NUM_GPRS];
+} s390_compat_regs_high;
 
 #ifdef __KERNEL__
 
index d69bec0..cfb874e 100644 (file)
@@ -9,6 +9,21 @@
 #ifndef _ASM_S390_UCONTEXT_H
 #define _ASM_S390_UCONTEXT_H
 
+#define UC_EXTENDED    0x00000001
+
+#ifndef __s390x__
+
+struct ucontext_extended {
+       unsigned long     uc_flags;
+       struct ucontext  *uc_link;
+       stack_t           uc_stack;
+       _sigregs          uc_mcontext;
+       unsigned long     uc_sigmask[2];
+       unsigned long     uc_gprs_high[16];
+};
+
+#endif
+
 struct ucontext {
        unsigned long     uc_flags;
        struct ucontext  *uc_link;
index b537cb0..eee9998 100644 (file)
@@ -39,6 +39,7 @@ typedef struct
        struct sigcontext32 sc;
        _sigregs32 sregs;
        int signo;
+       __u32 gprs_high[NUM_GPRS];
        __u8 retcode[S390_SYSCALL_SIZE];
 } sigframe32;
 
@@ -48,6 +49,7 @@ typedef struct
        __u8 retcode[S390_SYSCALL_SIZE];
        compat_siginfo_t info;
        struct ucontext32 uc;
+       __u32 gprs_high[NUM_GPRS];
 } rt_sigframe32;
 
 int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)
@@ -344,6 +346,30 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs)
        return 0;
 }
 
+static int save_sigregs_gprs_high(struct pt_regs *regs, __u32 __user *uregs)
+{
+       __u32 gprs_high[NUM_GPRS];
+       int i;
+
+       for (i = 0; i < NUM_GPRS; i++)
+               gprs_high[i] = regs->gprs[i] >> 32;
+
+       return __copy_to_user(uregs, &gprs_high, sizeof(gprs_high));
+}
+
+static int restore_sigregs_gprs_high(struct pt_regs *regs, __u32 __user *uregs)
+{
+       __u32 gprs_high[NUM_GPRS];
+       int err, i;
+
+       err = __copy_from_user(&gprs_high, uregs, sizeof(gprs_high));
+       if (err)
+               return err;
+       for (i = 0; i < NUM_GPRS; i++)
+               *(__u32 *)&regs->gprs[i] = gprs_high[i];
+       return 0;
+}
+
 asmlinkage long sys32_sigreturn(void)
 {
        struct pt_regs *regs = task_pt_regs(current);
@@ -363,6 +389,8 @@ asmlinkage long sys32_sigreturn(void)
 
        if (restore_sigregs32(regs, &frame->sregs))
                goto badframe;
+       if (restore_sigregs_gprs_high(regs, frame->gprs_high))
+               goto badframe;
 
        return regs->gprs[2];
 
@@ -394,6 +422,8 @@ asmlinkage long sys32_rt_sigreturn(void)
 
        if (restore_sigregs32(regs, &frame->uc.uc_mcontext))
                goto badframe;
+       if (restore_sigregs_gprs_high(regs, frame->gprs_high))
+               goto badframe;
 
        err = __get_user(ss_sp, &frame->uc.uc_stack.ss_sp);
        st.ss_sp = compat_ptr(ss_sp);
@@ -474,6 +504,8 @@ static int setup_frame32(int sig, struct k_sigaction *ka,
 
        if (save_sigregs32(regs, &frame->sregs))
                goto give_sigsegv;
+       if (save_sigregs_gprs_high(regs, frame->gprs_high))
+               goto give_sigsegv;
        if (__put_user((unsigned long) &frame->sregs, &frame->sc.sregs))
                goto give_sigsegv;
 
@@ -529,13 +561,14 @@ static int setup_rt_frame32(int sig, struct k_sigaction *ka, siginfo_t *info,
                goto give_sigsegv;
 
        /* Create the ucontext.  */
-       err |= __put_user(0, &frame->uc.uc_flags);
+       err |= __put_user(UC_EXTENDED, &frame->uc.uc_flags);
        err |= __put_user(0, &frame->uc.uc_link);
        err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
        err |= __put_user(sas_ss_flags(regs->gprs[15]),
                          &frame->uc.uc_stack.ss_flags);
        err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
        err |= save_sigregs32(regs, &frame->uc.uc_mcontext);
+       err |= save_sigregs_gprs_high(regs, frame->gprs_high);
        err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
        if (err)
                goto give_sigsegv;
index a873867..653c6a1 100644 (file)
@@ -57,6 +57,7 @@
 enum s390_regset {
        REGSET_GENERAL,
        REGSET_FP,
+       REGSET_GENERAL_EXTENDED,
 };
 
 static void
@@ -879,6 +880,67 @@ static int s390_compat_regs_set(struct task_struct *target,
        return rc;
 }
 
+static int s390_compat_regs_high_get(struct task_struct *target,
+                                    const struct user_regset *regset,
+                                    unsigned int pos, unsigned int count,
+                                    void *kbuf, void __user *ubuf)
+{
+       compat_ulong_t *gprs_high;
+
+       gprs_high = (compat_ulong_t *)
+               &task_pt_regs(target)->gprs[pos / sizeof(compat_ulong_t)];
+       if (kbuf) {
+               compat_ulong_t *k = kbuf;
+               while (count > 0) {
+                       *k++ = *gprs_high;
+                       gprs_high += 2;
+                       count -= sizeof(*k);
+               }
+       } else {
+               compat_ulong_t __user *u = ubuf;
+               while (count > 0) {
+                       if (__put_user(*gprs_high, u++))
+                               return -EFAULT;
+                       gprs_high += 2;
+                       count -= sizeof(*u);
+               }
+       }
+       return 0;
+}
+
+static int s390_compat_regs_high_set(struct task_struct *target,
+                                    const struct user_regset *regset,
+                                    unsigned int pos, unsigned int count,
+                                    const void *kbuf, const void __user *ubuf)
+{
+       compat_ulong_t *gprs_high;
+       int rc = 0;
+
+       gprs_high = (compat_ulong_t *)
+               &task_pt_regs(target)->gprs[pos / sizeof(compat_ulong_t)];
+       if (kbuf) {
+               const compat_ulong_t *k = kbuf;
+               while (count > 0) {
+                       *gprs_high = *k++;
+                       *gprs_high += 2;
+                       count -= sizeof(*k);
+               }
+       } else {
+               const compat_ulong_t  __user *u = ubuf;
+               while (count > 0 && !rc) {
+                       unsigned long word;
+                       rc = __get_user(word, u++);
+                       if (rc)
+                               break;
+                       *gprs_high = word;
+                       *gprs_high += 2;
+                       count -= sizeof(*u);
+               }
+       }
+
+       return rc;
+}
+
 static const struct user_regset s390_compat_regsets[] = {
        [REGSET_GENERAL] = {
                .core_note_type = NT_PRSTATUS,
@@ -896,6 +958,14 @@ static const struct user_regset s390_compat_regsets[] = {
                .get = s390_fpregs_get,
                .set = s390_fpregs_set,
        },
+       [REGSET_GENERAL_EXTENDED] = {
+               .core_note_type = NT_PRXSTATUS,
+               .n = sizeof(s390_compat_regs_high) / sizeof(compat_long_t),
+               .size = sizeof(compat_long_t),
+               .align = sizeof(compat_long_t),
+               .get = s390_compat_regs_high_get,
+               .set = s390_compat_regs_high_set,
+       },
 };
 
 static const struct user_regset_view user_s390_compat_view = {
index 9ed13a1..061479f 100644 (file)
@@ -729,7 +729,7 @@ static void __init setup_hwcaps(void)
 
        if ((facility_list & (1UL << (31 - 22)))
            && (facility_list & (1UL << (31 - 30))))
-               elf_hwcap |= 1UL << 8;
+               elf_hwcap |= HWCAP_S390_ETF3EH;
 
        /*
         * Check for additional facilities with store-facility-list-extended.
@@ -748,11 +748,20 @@ static void __init setup_hwcaps(void)
            __stfle(&facility_list_extended, 1) > 0) {
                if ((facility_list_extended & (1ULL << (63 - 42)))
                    && (facility_list_extended & (1ULL << (63 - 44))))
-                       elf_hwcap |= 1UL << 6;
+                       elf_hwcap |= HWCAP_S390_DFP;
        }
 
+       /*
+        * Huge page support HWCAP_S390_HPAGE is bit 7.
+        */
        if (MACHINE_HAS_HPAGE)
-               elf_hwcap |= 1UL << 7;
+               elf_hwcap |= HWCAP_S390_HPAGE;
+
+       /*
+        * 64-bit register support for 31-bit processes
+        * HWCAP_S390_HIGH_GPRS is bit 9.
+        */
+       elf_hwcap |= HWCAP_S390_HIGH_GPRS;
 
        switch (S390_lowcore.cpu_id.machine) {
        case 0x9672:
index 45a937b..90a4ed0 100644 (file)
@@ -361,6 +361,7 @@ typedef struct elf64_shdr {
 #define NT_PPC_VSX     0x102           /* PowerPC VSX registers */
 #define NT_386_TLS     0x200           /* i386 TLS slots (struct user_desc) */
 #define NT_386_IOPERM  0x201           /* x86 io permission bitmap (1=deny) */
+#define NT_PRXSTATUS   0x300           /* s390 upper register halves */
 
 
 /* Note header in a PT_NOTE section */