diff options
author | H. Peter Anvin <hpa@zytor.com> | 2012-02-19 12:41:09 -0500 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2012-02-20 15:52:05 -0500 |
commit | c5a373942bbc41698724fc948c74f959f73407e5 (patch) | |
tree | ba475366552b7d7bc143c0e8fd31f8e73ae2b6ce | |
parent | a96d692e9a559980f269f81c9b0b094220382186 (diff) |
x32: Signal-related system calls
x32 uses the 64-bit signal frame format, obviously, but there are some
structures which mixes that with pointers or sizeof(long) types, as
such we have to create a handful of system calls specific to x32. By
and large these are a mixture of the 64-bit and the compat system
calls.
Originally-by: H. J. Lu <hjl.tools@gmail.com>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r-- | arch/x86/kernel/entry_64.S | 19 | ||||
-rw-r--r-- | arch/x86/kernel/signal.c | 118 |
2 files changed, 136 insertions, 1 deletions
diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index a17b34216971..53dc821f0a62 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S | |||
@@ -746,6 +746,25 @@ ENTRY(stub_rt_sigreturn) | |||
746 | CFI_ENDPROC | 746 | CFI_ENDPROC |
747 | END(stub_rt_sigreturn) | 747 | END(stub_rt_sigreturn) |
748 | 748 | ||
749 | #ifdef CONFIG_X86_X32_ABI | ||
750 | PTREGSCALL stub_x32_sigaltstack, sys32_sigaltstack, %rdx | ||
751 | |||
752 | ENTRY(stub_x32_rt_sigreturn) | ||
753 | CFI_STARTPROC | ||
754 | addq $8, %rsp | ||
755 | PARTIAL_FRAME 0 | ||
756 | SAVE_REST | ||
757 | movq %rsp,%rdi | ||
758 | FIXUP_TOP_OF_STACK %r11 | ||
759 | call sys32_x32_rt_sigreturn | ||
760 | movq %rax,RAX(%rsp) # fixme, this could be done at the higher layer | ||
761 | RESTORE_REST | ||
762 | jmp int_ret_from_sys_call | ||
763 | CFI_ENDPROC | ||
764 | END(stub_x32_rt_sigreturn) | ||
765 | |||
766 | #endif | ||
767 | |||
749 | /* | 768 | /* |
750 | * Build the entry stubs and pointer table with some assembler magic. | 769 | * Build the entry stubs and pointer table with some assembler magic. |
751 | * We pack 7 stubs into a single 32-byte chunk, which will fit in a | 770 | * We pack 7 stubs into a single 32-byte chunk, which will fit in a |
diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c index 450fb255f877..c3846b6fb726 100644 --- a/arch/x86/kernel/signal.c +++ b/arch/x86/kernel/signal.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #ifdef CONFIG_X86_64 | 29 | #ifdef CONFIG_X86_64 |
30 | #include <asm/proto.h> | 30 | #include <asm/proto.h> |
31 | #include <asm/ia32_unistd.h> | 31 | #include <asm/ia32_unistd.h> |
32 | #include <asm/sys_ia32.h> | ||
32 | #endif /* CONFIG_X86_64 */ | 33 | #endif /* CONFIG_X86_64 */ |
33 | 34 | ||
34 | #include <asm/syscall.h> | 35 | #include <asm/syscall.h> |
@@ -632,6 +633,16 @@ static int signr_convert(int sig) | |||
632 | #define is_ia32 0 | 633 | #define is_ia32 0 |
633 | #endif /* CONFIG_IA32_EMULATION */ | 634 | #endif /* CONFIG_IA32_EMULATION */ |
634 | 635 | ||
636 | #ifdef CONFIG_X86_X32_ABI | ||
637 | #define is_x32 test_thread_flag(TIF_X32) | ||
638 | |||
639 | static int x32_setup_rt_frame(int sig, struct k_sigaction *ka, | ||
640 | siginfo_t *info, compat_sigset_t *set, | ||
641 | struct pt_regs *regs); | ||
642 | #else /* !CONFIG_X86_X32_ABI */ | ||
643 | #define is_x32 0 | ||
644 | #endif /* CONFIG_X86_X32_ABI */ | ||
645 | |||
635 | int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | 646 | int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, |
636 | sigset_t *set, struct pt_regs *regs); | 647 | sigset_t *set, struct pt_regs *regs); |
637 | int ia32_setup_frame(int sig, struct k_sigaction *ka, | 648 | int ia32_setup_frame(int sig, struct k_sigaction *ka, |
@@ -656,8 +667,14 @@ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | |||
656 | ret = ia32_setup_rt_frame(usig, ka, info, set, regs); | 667 | ret = ia32_setup_rt_frame(usig, ka, info, set, regs); |
657 | else | 668 | else |
658 | ret = ia32_setup_frame(usig, ka, set, regs); | 669 | ret = ia32_setup_frame(usig, ka, set, regs); |
659 | } else | 670 | #ifdef CONFIG_X86_X32_ABI |
671 | } else if (is_x32) { | ||
672 | ret = x32_setup_rt_frame(usig, ka, info, | ||
673 | (compat_sigset_t *)set, regs); | ||
674 | #endif | ||
675 | } else { | ||
660 | ret = __setup_rt_frame(sig, ka, info, set, regs); | 676 | ret = __setup_rt_frame(sig, ka, info, set, regs); |
677 | } | ||
661 | 678 | ||
662 | if (ret) { | 679 | if (ret) { |
663 | force_sigsegv(sig, current); | 680 | force_sigsegv(sig, current); |
@@ -840,3 +857,102 @@ void signal_fault(struct pt_regs *regs, void __user *frame, char *where) | |||
840 | 857 | ||
841 | force_sig(SIGSEGV, me); | 858 | force_sig(SIGSEGV, me); |
842 | } | 859 | } |
860 | |||
861 | #ifdef CONFIG_X86_X32_ABI | ||
862 | static int x32_setup_rt_frame(int sig, struct k_sigaction *ka, | ||
863 | siginfo_t *info, compat_sigset_t *set, | ||
864 | struct pt_regs *regs) | ||
865 | { | ||
866 | struct rt_sigframe_x32 __user *frame; | ||
867 | void __user *restorer; | ||
868 | int err = 0; | ||
869 | void __user *fpstate = NULL; | ||
870 | |||
871 | frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate); | ||
872 | |||
873 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | ||
874 | return -EFAULT; | ||
875 | |||
876 | if (ka->sa.sa_flags & SA_SIGINFO) { | ||
877 | if (copy_siginfo_to_user32(&frame->info, info)) | ||
878 | return -EFAULT; | ||
879 | } | ||
880 | |||
881 | put_user_try { | ||
882 | /* Create the ucontext. */ | ||
883 | if (cpu_has_xsave) | ||
884 | put_user_ex(UC_FP_XSTATE, &frame->uc.uc_flags); | ||
885 | else | ||
886 | put_user_ex(0, &frame->uc.uc_flags); | ||
887 | put_user_ex(0, &frame->uc.uc_link); | ||
888 | put_user_ex(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); | ||
889 | put_user_ex(sas_ss_flags(regs->sp), | ||
890 | &frame->uc.uc_stack.ss_flags); | ||
891 | put_user_ex(current->sas_ss_size, &frame->uc.uc_stack.ss_size); | ||
892 | put_user_ex(0, &frame->uc.uc__pad0); | ||
893 | err |= setup_sigcontext(&frame->uc.uc_mcontext, fpstate, | ||
894 | regs, set->sig[0]); | ||
895 | err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); | ||
896 | |||
897 | if (ka->sa.sa_flags & SA_RESTORER) { | ||
898 | restorer = ka->sa.sa_restorer; | ||
899 | } else { | ||
900 | /* could use a vstub here */ | ||
901 | restorer = NULL; | ||
902 | err |= -EFAULT; | ||
903 | } | ||
904 | put_user_ex(restorer, &frame->pretcode); | ||
905 | } put_user_catch(err); | ||
906 | |||
907 | if (err) | ||
908 | return -EFAULT; | ||
909 | |||
910 | /* Set up registers for signal handler */ | ||
911 | regs->sp = (unsigned long) frame; | ||
912 | regs->ip = (unsigned long) ka->sa.sa_handler; | ||
913 | |||
914 | /* We use the x32 calling convention here... */ | ||
915 | regs->di = sig; | ||
916 | regs->si = (unsigned long) &frame->info; | ||
917 | regs->dx = (unsigned long) &frame->uc; | ||
918 | |||
919 | loadsegment(ds, __USER_DS); | ||
920 | loadsegment(es, __USER_DS); | ||
921 | |||
922 | regs->cs = __USER_CS; | ||
923 | regs->ss = __USER_DS; | ||
924 | |||
925 | return 0; | ||
926 | } | ||
927 | |||
928 | asmlinkage long sys32_x32_rt_sigreturn(struct pt_regs *regs) | ||
929 | { | ||
930 | struct rt_sigframe_x32 __user *frame; | ||
931 | sigset_t set; | ||
932 | unsigned long ax; | ||
933 | struct pt_regs tregs; | ||
934 | |||
935 | frame = (struct rt_sigframe_x32 __user *)(regs->sp - 8); | ||
936 | |||
937 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | ||
938 | goto badframe; | ||
939 | if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) | ||
940 | goto badframe; | ||
941 | |||
942 | sigdelsetmask(&set, ~_BLOCKABLE); | ||
943 | set_current_blocked(&set); | ||
944 | |||
945 | if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &ax)) | ||
946 | goto badframe; | ||
947 | |||
948 | tregs = *regs; | ||
949 | if (sys32_sigaltstack(&frame->uc.uc_stack, NULL, &tregs) == -EFAULT) | ||
950 | goto badframe; | ||
951 | |||
952 | return ax; | ||
953 | |||
954 | badframe: | ||
955 | signal_fault(regs, frame, "x32 rt_sigreturn"); | ||
956 | return 0; | ||
957 | } | ||
958 | #endif | ||