You've already forked linux-rockchip
mirror of
https://github.com/armbian/linux-rockchip.git
synced 2026-01-06 11:08:10 -08:00
parisc: Add vDSO support
Add minimal vDSO support, which provides the signal trampoline helpers, but none of the userspace syscall helpers like time wrappers. The big benefit of this vDSO implementation is, that we now don't need an executeable stack any longer. PA-RISC is one of the last architectures where an executeable stack was needed in oder to implement the signal trampolines by putting assembly instructions on the stack which then gets executed. Instead the kernel will provide the relevant code in the vDSO page and only put the pointers to the signal information on the stack. By dropping the need for executable stacks we avoid running into issues with applications which want non executable stacks for security reasons. Additionally, alternative stacks on memory areas without exec permissions are supported too. This code is based on an initial implementation by Randolph Chung from 2006: https://lore.kernel.org/linux-parisc/4544A34A.6080700@tausq.org/ I did the porting and lifted the code to current code base. Dave fixed the unwind code so that gdb and glibc are able to backtrace through the code. An additional patch to gdb will be pushed upstream by Dave. Signed-off-by: Helge Deller <deller@gmx.de> Signed-off-by: Dave Anglin <dave.anglin@bell.net> Cc: Randolph Chung <randolph@tausq.org> Signed-off-by: Helge Deller <deller@gmx.de>
This commit is contained in:
@@ -10,6 +10,7 @@ config PARISC
|
||||
select ARCH_HAS_ELF_RANDOMIZE
|
||||
select ARCH_HAS_STRICT_KERNEL_RWX
|
||||
select ARCH_HAS_UBSAN_SANITIZE_ALL
|
||||
select ARCH_HAS_PTE_SPECIAL
|
||||
select ARCH_NO_SG_CHAIN
|
||||
select ARCH_SUPPORTS_HUGETLBFS if PA20
|
||||
select ARCH_SUPPORTS_MEMORY_FAILURE
|
||||
|
||||
@@ -44,6 +44,16 @@ endif
|
||||
|
||||
export LD_BFD
|
||||
|
||||
# Set default 32 bits cross compilers for vdso
|
||||
CC_ARCHES_32 = hppa hppa2.0 hppa1.1
|
||||
CC_SUFFIXES = linux linux-gnu unknown-linux-gnu
|
||||
CROSS32_COMPILE := $(call cc-cross-prefix, \
|
||||
$(foreach a,$(CC_ARCHES_32), \
|
||||
$(foreach s,$(CC_SUFFIXES),$(a)-$(s)-)))
|
||||
CROSS32CC := $(CROSS32_COMPILE)gcc
|
||||
export CROSS32CC
|
||||
|
||||
# Set default cross compiler for kernel build
|
||||
ifdef cross_compiling
|
||||
ifeq ($(CROSS_COMPILE),)
|
||||
CC_SUFFIXES = linux linux-gnu unknown-linux-gnu
|
||||
@@ -163,6 +173,26 @@ vmlinuz: vmlinux
|
||||
@$(KGZIP) -cf -9 $< > $@
|
||||
endif
|
||||
|
||||
ifeq ($(KBUILD_EXTMOD),)
|
||||
# We need to generate vdso-offsets.h before compiling certain files in kernel/.
|
||||
# In order to do that, we should use the archprepare target, but we can't since
|
||||
# asm-offsets.h is included in some files used to generate vdso-offsets.h, and
|
||||
# asm-offsets.h is built in prepare0, for which archprepare is a dependency.
|
||||
# Therefore we need to generate the header after prepare0 has been made, hence
|
||||
# this hack.
|
||||
prepare: vdso_prepare
|
||||
vdso_prepare: prepare0
|
||||
$(if $(CONFIG_64BIT),$(Q)$(MAKE) \
|
||||
$(build)=arch/parisc/kernel/vdso64 include/generated/vdso64-offsets.h)
|
||||
$(Q)$(MAKE) $(build)=arch/parisc/kernel/vdso32 include/generated/vdso32-offsets.h
|
||||
endif
|
||||
|
||||
PHONY += vdso_install
|
||||
|
||||
vdso_install:
|
||||
$(Q)$(MAKE) $(build)=arch/parisc/kernel/vdso $@
|
||||
$(if $(CONFIG_COMPAT_VDSO), \
|
||||
$(Q)$(MAKE) $(build)=arch/parisc/kernel/vdso32 $@)
|
||||
install:
|
||||
$(CONFIG_SHELL) $(srctree)/arch/parisc/install.sh \
|
||||
$(KERNELRELEASE) vmlinux System.map "$(INSTALL_PATH)"
|
||||
|
||||
@@ -359,4 +359,19 @@ struct mm_struct;
|
||||
extern unsigned long arch_randomize_brk(struct mm_struct *);
|
||||
#define arch_randomize_brk arch_randomize_brk
|
||||
|
||||
|
||||
#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1
|
||||
struct linux_binprm;
|
||||
extern int arch_setup_additional_pages(struct linux_binprm *bprm,
|
||||
int executable_stack);
|
||||
#define VDSO_AUX_ENT(a, b) NEW_AUX_ENT(a, b)
|
||||
#define VDSO_CURRENT_BASE current->mm->context.vdso_base
|
||||
|
||||
#define ARCH_DLINFO \
|
||||
do { \
|
||||
if (VDSO_CURRENT_BASE) { \
|
||||
NEW_AUX_ENT(AT_SYSINFO_EHDR, VDSO_CURRENT_BASE);\
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
#ifndef _PARISC_MMU_H_
|
||||
#define _PARISC_MMU_H_
|
||||
|
||||
/* On parisc, we store the space id here */
|
||||
typedef unsigned long mm_context_t;
|
||||
typedef struct {
|
||||
unsigned long space_id;
|
||||
unsigned long vdso_base;
|
||||
} mm_context_t;
|
||||
|
||||
#endif /* _PARISC_MMU_H_ */
|
||||
|
||||
@@ -20,7 +20,7 @@ init_new_context(struct task_struct *tsk, struct mm_struct *mm)
|
||||
{
|
||||
BUG_ON(atomic_read(&mm->mm_users) != 1);
|
||||
|
||||
mm->context = alloc_sid();
|
||||
mm->context.space_id = alloc_sid();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -28,22 +28,22 @@ init_new_context(struct task_struct *tsk, struct mm_struct *mm)
|
||||
static inline void
|
||||
destroy_context(struct mm_struct *mm)
|
||||
{
|
||||
free_sid(mm->context);
|
||||
mm->context = 0;
|
||||
free_sid(mm->context.space_id);
|
||||
mm->context.space_id = 0;
|
||||
}
|
||||
|
||||
static inline unsigned long __space_to_prot(mm_context_t context)
|
||||
{
|
||||
#if SPACEID_SHIFT == 0
|
||||
return context << 1;
|
||||
return context.space_id << 1;
|
||||
#else
|
||||
return context >> (SPACEID_SHIFT - 1);
|
||||
return context.space_id >> (SPACEID_SHIFT - 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void load_context(mm_context_t context)
|
||||
{
|
||||
mtsp(context, 3);
|
||||
mtsp(context.space_id, 3);
|
||||
mtctl(__space_to_prot(context), 8);
|
||||
}
|
||||
|
||||
@@ -89,8 +89,8 @@ static inline void activate_mm(struct mm_struct *prev, struct mm_struct *next)
|
||||
|
||||
BUG_ON(next == &init_mm); /* Should never happen */
|
||||
|
||||
if (next->context == 0)
|
||||
next->context = alloc_sid();
|
||||
if (next->context.space_id == 0)
|
||||
next->context.space_id = alloc_sid();
|
||||
|
||||
switch_mm(prev,next,current);
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ static inline void purge_tlb_entries(struct mm_struct *mm, unsigned long addr)
|
||||
unsigned long flags;
|
||||
|
||||
purge_tlb_start(flags);
|
||||
mtsp(mm->context, 1);
|
||||
mtsp(mm->context.space_id, 1);
|
||||
pdtlb(addr);
|
||||
pitlb(addr);
|
||||
purge_tlb_end(flags);
|
||||
@@ -219,9 +219,10 @@ extern void __update_cache(pte_t pte);
|
||||
#define _PAGE_PRESENT (1 << xlate_pabit(_PAGE_PRESENT_BIT))
|
||||
#define _PAGE_HUGE (1 << xlate_pabit(_PAGE_HPAGE_BIT))
|
||||
#define _PAGE_USER (1 << xlate_pabit(_PAGE_USER_BIT))
|
||||
#define _PAGE_SPECIAL (_PAGE_DMB)
|
||||
|
||||
#define _PAGE_TABLE (_PAGE_PRESENT | _PAGE_READ | _PAGE_WRITE | _PAGE_DIRTY | _PAGE_ACCESSED)
|
||||
#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY)
|
||||
#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_SPECIAL)
|
||||
#define _PAGE_KERNEL_RO (_PAGE_PRESENT | _PAGE_READ | _PAGE_DIRTY | _PAGE_ACCESSED)
|
||||
#define _PAGE_KERNEL_EXEC (_PAGE_KERNEL_RO | _PAGE_EXEC)
|
||||
#define _PAGE_KERNEL_RWX (_PAGE_KERNEL_EXEC | _PAGE_WRITE)
|
||||
@@ -348,6 +349,7 @@ static inline void pud_clear(pud_t *pud) {
|
||||
static inline int pte_dirty(pte_t pte) { return pte_val(pte) & _PAGE_DIRTY; }
|
||||
static inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_ACCESSED; }
|
||||
static inline int pte_write(pte_t pte) { return pte_val(pte) & _PAGE_WRITE; }
|
||||
static inline int pte_special(pte_t pte) { return pte_val(pte) & _PAGE_SPECIAL; }
|
||||
|
||||
static inline pte_t pte_mkclean(pte_t pte) { pte_val(pte) &= ~_PAGE_DIRTY; return pte; }
|
||||
static inline pte_t pte_mkold(pte_t pte) { pte_val(pte) &= ~_PAGE_ACCESSED; return pte; }
|
||||
@@ -355,6 +357,7 @@ static inline pte_t pte_wrprotect(pte_t pte) { pte_val(pte) &= ~_PAGE_WRITE; ret
|
||||
static inline pte_t pte_mkdirty(pte_t pte) { pte_val(pte) |= _PAGE_DIRTY; return pte; }
|
||||
static inline pte_t pte_mkyoung(pte_t pte) { pte_val(pte) |= _PAGE_ACCESSED; return pte; }
|
||||
static inline pte_t pte_mkwrite(pte_t pte) { pte_val(pte) |= _PAGE_WRITE; return pte; }
|
||||
static inline pte_t pte_mkspecial(pte_t pte) { pte_val(pte) |= _PAGE_SPECIAL; return pte; }
|
||||
|
||||
/*
|
||||
* Huge pte definitions.
|
||||
|
||||
@@ -236,7 +236,7 @@ on downward growing arches, it looks like this:
|
||||
|
||||
#define start_thread(regs, new_pc, new_sp) do { \
|
||||
elf_addr_t *sp = (elf_addr_t *)new_sp; \
|
||||
__u32 spaceid = (__u32)current->mm->context; \
|
||||
__u32 spaceid = (__u32)current->mm->context.space_id; \
|
||||
elf_addr_t pc = (elf_addr_t)new_pc | 3; \
|
||||
elf_caddr_t *argv = (elf_caddr_t *)bprm->exec + 1; \
|
||||
\
|
||||
|
||||
@@ -2,16 +2,8 @@
|
||||
#ifndef _ASM_PARISC_RT_SIGFRAME_H
|
||||
#define _ASM_PARISC_RT_SIGFRAME_H
|
||||
|
||||
#define SIGRETURN_TRAMP 4
|
||||
#define SIGRESTARTBLOCK_TRAMP 5
|
||||
#define TRAMP_SIZE (SIGRETURN_TRAMP + SIGRESTARTBLOCK_TRAMP)
|
||||
|
||||
struct rt_sigframe {
|
||||
/* XXX: Must match trampoline size in arch/parisc/kernel/signal.c
|
||||
Secondary to that it must protect the ERESTART_RESTARTBLOCK
|
||||
trampoline we left on the stack (we were bad and didn't
|
||||
change sp so we could run really fast.) */
|
||||
unsigned int tramp[TRAMP_SIZE];
|
||||
unsigned int tramp[2]; /* holds original return address */
|
||||
struct siginfo info;
|
||||
struct ucontext uc;
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@ int __flush_tlb_range(unsigned long sid,
|
||||
unsigned long start, unsigned long end);
|
||||
|
||||
#define flush_tlb_range(vma, start, end) \
|
||||
__flush_tlb_range((vma)->vm_mm->context, start, end)
|
||||
__flush_tlb_range((vma)->vm_mm->context.space_id, start, end)
|
||||
|
||||
#define flush_tlb_kernel_range(start, end) \
|
||||
__flush_tlb_range(0, start, end)
|
||||
|
||||
@@ -63,10 +63,6 @@
|
||||
); \
|
||||
__sys_res = (long)__res; \
|
||||
} \
|
||||
if ( (unsigned long)__sys_res >= (unsigned long)-4095 ){ \
|
||||
errno = -__sys_res; \
|
||||
__sys_res = -1; \
|
||||
} \
|
||||
__sys_res; \
|
||||
})
|
||||
|
||||
|
||||
24
arch/parisc/include/asm/vdso.h
Normal file
24
arch/parisc/include/asm/vdso.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __PARISC_VDSO_H__
|
||||
#define __PARISC_VDSO_H__
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
#include <generated/vdso64-offsets.h>
|
||||
#endif
|
||||
#include <generated/vdso32-offsets.h>
|
||||
|
||||
#define VDSO64_SYMBOL(tsk, name) ((tsk)->mm->context.vdso_base + (vdso64_offset_##name))
|
||||
#define VDSO32_SYMBOL(tsk, name) ((tsk)->mm->context.vdso_base + (vdso32_offset_##name))
|
||||
|
||||
extern struct vdso_data *vdso_data;
|
||||
|
||||
#endif /* __ASSEMBLY __ */
|
||||
|
||||
/* Default link addresses for the vDSOs */
|
||||
#define VDSO_LBASE 0
|
||||
|
||||
#define VDSO_VERSION_STRING LINUX_5.18
|
||||
|
||||
#endif /* __PARISC_VDSO_H__ */
|
||||
8
arch/parisc/include/uapi/asm/auxvec.h
Normal file
8
arch/parisc/include/uapi/asm/auxvec.h
Normal file
@@ -0,0 +1,8 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
#ifndef _UAPI_PARISC_AUXVEC_H
|
||||
#define _UAPI_PARISC_AUXVEC_H
|
||||
|
||||
/* The vDSO location. */
|
||||
#define AT_SYSINFO_EHDR 33
|
||||
|
||||
#endif /* _UAPI_PARISC_AUXVEC_H */
|
||||
@@ -39,3 +39,8 @@ obj-$(CONFIG_KGDB) += kgdb.o
|
||||
obj-$(CONFIG_KPROBES) += kprobes.o
|
||||
obj-$(CONFIG_KEXEC_CORE) += kexec.o relocate_kernel.o
|
||||
obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
|
||||
|
||||
# vdso
|
||||
obj-y += vdso.o
|
||||
obj-$(CONFIG_64BIT) += vdso64/
|
||||
obj-y += vdso32/
|
||||
|
||||
@@ -26,7 +26,11 @@
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/pdc.h>
|
||||
#include <uapi/asm/sigcontext.h>
|
||||
#include <asm/ucontext.h>
|
||||
#include <asm/rt_sigframe.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include "signal32.h"
|
||||
|
||||
/* Add FRAME_SIZE to the size x and align it to y. All definitions
|
||||
* that use align_frame will include space for a frame.
|
||||
@@ -218,6 +222,11 @@ int main(void)
|
||||
DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
|
||||
DEFINE(TI_PRE_COUNT, offsetof(struct task_struct, thread_info.preempt_count));
|
||||
BLANK();
|
||||
DEFINE(ASM_SIGFRAME_SIZE, PARISC_RT_SIGFRAME_SIZE);
|
||||
DEFINE(SIGFRAME_CONTEXT_REGS, offsetof(struct rt_sigframe, uc.uc_mcontext) - PARISC_RT_SIGFRAME_SIZE);
|
||||
DEFINE(ASM_SIGFRAME_SIZE32, PARISC_RT_SIGFRAME_SIZE32);
|
||||
DEFINE(SIGFRAME_CONTEXT_REGS32, offsetof(struct compat_rt_sigframe, uc.uc_mcontext) - PARISC_RT_SIGFRAME_SIZE32);
|
||||
BLANK();
|
||||
DEFINE(ICACHE_BASE, offsetof(struct pdc_cache_info, ic_base));
|
||||
DEFINE(ICACHE_STRIDE, offsetof(struct pdc_cache_info, ic_stride));
|
||||
DEFINE(ICACHE_COUNT, offsetof(struct pdc_cache_info, ic_count));
|
||||
|
||||
@@ -566,7 +566,7 @@ void flush_cache_mm(struct mm_struct *mm)
|
||||
rp3440, etc. So, avoid it if the mm isn't too big. */
|
||||
if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
|
||||
mm_total_size(mm) >= parisc_cache_flush_threshold) {
|
||||
if (mm->context)
|
||||
if (mm->context.space_id)
|
||||
flush_tlb_all();
|
||||
flush_cache_all();
|
||||
return;
|
||||
@@ -581,7 +581,7 @@ void flush_cache_range(struct vm_area_struct *vma,
|
||||
{
|
||||
if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
|
||||
end - start >= parisc_cache_flush_threshold) {
|
||||
if (vma->vm_mm->context)
|
||||
if (vma->vm_mm->context.space_id)
|
||||
flush_tlb_range(vma, start, end);
|
||||
flush_cache_all();
|
||||
return;
|
||||
@@ -594,7 +594,7 @@ void
|
||||
flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr, unsigned long pfn)
|
||||
{
|
||||
if (pfn_valid(pfn)) {
|
||||
if (likely(vma->vm_mm->context)) {
|
||||
if (likely(vma->vm_mm->context.space_id)) {
|
||||
flush_tlb_page(vma, vmaddr);
|
||||
__flush_cache_page(vma, vmaddr, PFN_PHYS(pfn));
|
||||
} else {
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* linux/arch/parisc/kernel/signal.c: Architecture-specific signal
|
||||
* handling support.
|
||||
* PA-RISC architecture-specific signal handling support.
|
||||
*
|
||||
* Copyright (C) 2000 David Huggins-Daines <dhd@debian.org>
|
||||
* Copyright (C) 2000 Linuxcare, Inc.
|
||||
* Copyright (C) 2000-2022 Helge Deller <deller@gmx.de>
|
||||
* Copyright (C) 2022 John David Anglin <dave.anglin@bell.net>
|
||||
*
|
||||
* Based on the ia64, i386, and alpha versions.
|
||||
*
|
||||
* Like the IA-64, we are a recent enough port (we are *starting*
|
||||
* with glibc2.2) that we do not need to support the old non-realtime
|
||||
* Linux signals. Therefore we don't.
|
||||
*/
|
||||
|
||||
#include <linux/sched.h>
|
||||
@@ -32,6 +29,7 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/vdso.h>
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
#include "signal32.h"
|
||||
@@ -59,14 +57,6 @@
|
||||
* Do a signal return - restore sigcontext.
|
||||
*/
|
||||
|
||||
/* Trampoline for calling rt_sigreturn() */
|
||||
#define INSN_LDI_R25_0 0x34190000 /* ldi 0,%r25 (in_syscall=0) */
|
||||
#define INSN_LDI_R25_1 0x34190002 /* ldi 1,%r25 (in_syscall=1) */
|
||||
#define INSN_LDI_R20 0x3414015a /* ldi __NR_rt_sigreturn,%r20 */
|
||||
#define INSN_BLE_SR2_R0 0xe4008200 /* be,l 0x100(%sr2,%r0),%sr0,%r31 */
|
||||
/* For debugging */
|
||||
#define INSN_DIE_HORRIBLY 0x68000ccc /* stw %r0,0x666(%sr0,%r0) */
|
||||
|
||||
static long
|
||||
restore_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs)
|
||||
{
|
||||
@@ -77,9 +67,9 @@ restore_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs)
|
||||
err |= __copy_from_user(regs->iaoq, sc->sc_iaoq, sizeof(regs->iaoq));
|
||||
err |= __copy_from_user(regs->iasq, sc->sc_iasq, sizeof(regs->iasq));
|
||||
err |= __get_user(regs->sar, &sc->sc_sar);
|
||||
DBG(2,"restore_sigcontext: iaoq is %#lx / %#lx\n",
|
||||
regs->iaoq[0],regs->iaoq[1]);
|
||||
DBG(2,"restore_sigcontext: r28 is %ld\n", regs->gr[28]);
|
||||
DBG(2, "%s: iaoq is %#lx / %#lx\n",
|
||||
__func__, regs->iaoq[0], regs->iaoq[1]);
|
||||
DBG(2, "%s: r28 is %ld\n", __func__, regs->gr[28]);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -102,7 +92,7 @@ sys_rt_sigreturn(struct pt_regs *regs, int in_syscall)
|
||||
/* Unwind the user stack to get the rt_sigframe structure. */
|
||||
frame = (struct rt_sigframe __user *)
|
||||
(usp - sigframe_size);
|
||||
DBG(2,"sys_rt_sigreturn: frame is %p\n", frame);
|
||||
DBG(2, "%s: frame is %p pid %d\n", __func__, frame, task_pid_nr(current));
|
||||
|
||||
regs->orig_r28 = 1; /* no restarts for sigreturn */
|
||||
|
||||
@@ -110,7 +100,6 @@ sys_rt_sigreturn(struct pt_regs *regs, int in_syscall)
|
||||
compat_frame = (struct compat_rt_sigframe __user *)frame;
|
||||
|
||||
if (is_compat_task()) {
|
||||
DBG(2,"sys_rt_sigreturn: ELF32 process.\n");
|
||||
if (get_compat_sigset(&set, &compat_frame->uc.uc_sigmask))
|
||||
goto give_sigsegv;
|
||||
} else
|
||||
@@ -125,25 +114,25 @@ sys_rt_sigreturn(struct pt_regs *regs, int in_syscall)
|
||||
/* Good thing we saved the old gr[30], eh? */
|
||||
#ifdef CONFIG_64BIT
|
||||
if (is_compat_task()) {
|
||||
DBG(1,"sys_rt_sigreturn: compat_frame->uc.uc_mcontext 0x%p\n",
|
||||
&compat_frame->uc.uc_mcontext);
|
||||
DBG(1, "%s: compat_frame->uc.uc_mcontext 0x%p\n",
|
||||
__func__, &compat_frame->uc.uc_mcontext);
|
||||
// FIXME: Load upper half from register file
|
||||
if (restore_sigcontext32(&compat_frame->uc.uc_mcontext,
|
||||
&compat_frame->regs, regs))
|
||||
goto give_sigsegv;
|
||||
DBG(1,"sys_rt_sigreturn: usp %#08lx stack 0x%p\n",
|
||||
usp, &compat_frame->uc.uc_stack);
|
||||
DBG(1, "%s: usp %#08lx stack 0x%p\n",
|
||||
__func__, usp, &compat_frame->uc.uc_stack);
|
||||
if (compat_restore_altstack(&compat_frame->uc.uc_stack))
|
||||
goto give_sigsegv;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
DBG(1,"sys_rt_sigreturn: frame->uc.uc_mcontext 0x%p\n",
|
||||
&frame->uc.uc_mcontext);
|
||||
DBG(1, "%s: frame->uc.uc_mcontext 0x%p\n",
|
||||
__func__, &frame->uc.uc_mcontext);
|
||||
if (restore_sigcontext(&frame->uc.uc_mcontext, regs))
|
||||
goto give_sigsegv;
|
||||
DBG(1,"sys_rt_sigreturn: usp %#08lx stack 0x%p\n",
|
||||
usp, &frame->uc.uc_stack);
|
||||
DBG(1, "%s: usp %#08lx stack 0x%p\n",
|
||||
__func__, usp, &frame->uc.uc_stack);
|
||||
if (restore_altstack(&frame->uc.uc_stack))
|
||||
goto give_sigsegv;
|
||||
}
|
||||
@@ -155,14 +144,11 @@ sys_rt_sigreturn(struct pt_regs *regs, int in_syscall)
|
||||
*/
|
||||
if (in_syscall)
|
||||
regs->gr[31] = regs->iaoq[0];
|
||||
#if DEBUG_SIG
|
||||
DBG(1,"sys_rt_sigreturn: returning to %#lx, DUMPING REGS:\n", regs->iaoq[0]);
|
||||
show_regs(regs);
|
||||
#endif
|
||||
|
||||
return;
|
||||
|
||||
give_sigsegv:
|
||||
DBG(1,"sys_rt_sigreturn: Sending SIGSEGV\n");
|
||||
DBG(1, "%s: Sending SIGSEGV\n", __func__);
|
||||
force_sig(SIGSEGV);
|
||||
return;
|
||||
}
|
||||
@@ -177,15 +163,15 @@ get_sigframe(struct k_sigaction *ka, unsigned long sp, size_t frame_size)
|
||||
/*FIXME: ELF32 vs. ELF64 has different frame_size, but since we
|
||||
don't use the parameter it doesn't matter */
|
||||
|
||||
DBG(1,"get_sigframe: ka = %#lx, sp = %#lx, frame_size = %#lx\n",
|
||||
(unsigned long)ka, sp, frame_size);
|
||||
DBG(1, "%s: ka = %#lx, sp = %#lx, frame_size = %zu\n",
|
||||
__func__, (unsigned long)ka, sp, frame_size);
|
||||
|
||||
/* Align alternate stack and reserve 64 bytes for the signal
|
||||
handler's frame marker. */
|
||||
if ((ka->sa.sa_flags & SA_ONSTACK) != 0 && ! sas_ss_flags(sp))
|
||||
sp = (current->sas_ss_sp + 0x7f) & ~0x3f; /* Stacks grow up! */
|
||||
|
||||
DBG(1,"get_sigframe: Returning sp = %#lx\n", (unsigned long)sp);
|
||||
DBG(1, "%s: Returning sp = %#lx\n", __func__, (unsigned long)sp);
|
||||
return (void __user *) sp; /* Stacks grow up. Fun. */
|
||||
}
|
||||
|
||||
@@ -205,20 +191,20 @@ setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, int in_sysc
|
||||
err |= __put_user(regs->gr[31]+4, &sc->sc_iaoq[1]);
|
||||
err |= __put_user(regs->sr[3], &sc->sc_iasq[0]);
|
||||
err |= __put_user(regs->sr[3], &sc->sc_iasq[1]);
|
||||
DBG(1,"setup_sigcontext: iaoq %#lx / %#lx (in syscall)\n",
|
||||
regs->gr[31], regs->gr[31]+4);
|
||||
DBG(1, "%s: iaoq %#lx / %#lx (in syscall)\n",
|
||||
__func__, regs->gr[31], regs->gr[31]+4);
|
||||
} else {
|
||||
err |= __copy_to_user(sc->sc_iaoq, regs->iaoq, sizeof(regs->iaoq));
|
||||
err |= __copy_to_user(sc->sc_iasq, regs->iasq, sizeof(regs->iasq));
|
||||
DBG(1,"setup_sigcontext: iaoq %#lx / %#lx (not in syscall)\n",
|
||||
regs->iaoq[0], regs->iaoq[1]);
|
||||
DBG(1, "%s: iaoq %#lx / %#lx (not in syscall)\n",
|
||||
__func__, regs->iaoq[0], regs->iaoq[1]);
|
||||
}
|
||||
|
||||
err |= __put_user(flags, &sc->sc_flags);
|
||||
err |= __copy_to_user(sc->sc_gr, regs->gr, sizeof(regs->gr));
|
||||
err |= __copy_to_user(sc->sc_fr, regs->fr, sizeof(regs->fr));
|
||||
err |= __put_user(regs->sar, &sc->sc_sar);
|
||||
DBG(1,"setup_sigcontext: r28 is %ld\n", regs->gr[28]);
|
||||
DBG(1, "%s: r28 is %ld\n", __func__, regs->gr[28]);
|
||||
|
||||
return err;
|
||||
}
|
||||
@@ -230,7 +216,7 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs,
|
||||
struct rt_sigframe __user *frame;
|
||||
unsigned long rp, usp;
|
||||
unsigned long haddr, sigframe_size;
|
||||
unsigned long start, end;
|
||||
unsigned long start;
|
||||
int err = 0;
|
||||
#ifdef CONFIG_64BIT
|
||||
struct compat_rt_sigframe __user * compat_frame;
|
||||
@@ -247,8 +233,7 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs,
|
||||
#endif
|
||||
frame = get_sigframe(&ksig->ka, usp, sigframe_size);
|
||||
|
||||
DBG(1,"SETUP_RT_FRAME: START\n");
|
||||
DBG(1,"setup_rt_frame: frame %p info %p\n", frame, ksig->info);
|
||||
DBG(1, "%s: frame %p info %p\n", __func__, frame, &ksig->info);
|
||||
|
||||
start = (unsigned long) frame;
|
||||
if (start >= user_addr_max() - sigframe_size)
|
||||
@@ -259,11 +244,12 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs,
|
||||
compat_frame = (struct compat_rt_sigframe __user *)frame;
|
||||
|
||||
if (is_compat_task()) {
|
||||
DBG(1,"setup_rt_frame: frame->info = 0x%p\n", &compat_frame->info);
|
||||
DBG(1, "%s: frame->info = 0x%p\n", __func__, &compat_frame->info);
|
||||
err |= copy_siginfo_to_user32(&compat_frame->info, &ksig->info);
|
||||
err |= __compat_save_altstack( &compat_frame->uc.uc_stack, regs->gr[30]);
|
||||
DBG(1,"setup_rt_frame: frame->uc = 0x%p\n", &compat_frame->uc);
|
||||
DBG(1,"setup_rt_frame: frame->uc.uc_mcontext = 0x%p\n", &compat_frame->uc.uc_mcontext);
|
||||
DBG(1, "%s: frame->uc = 0x%p\n", __func__, &compat_frame->uc);
|
||||
DBG(1, "%s: frame->uc.uc_mcontext = 0x%p\n",
|
||||
__func__, &compat_frame->uc.uc_mcontext);
|
||||
err |= setup_sigcontext32(&compat_frame->uc.uc_mcontext,
|
||||
&compat_frame->regs, regs, in_syscall);
|
||||
err |= put_compat_sigset(&compat_frame->uc.uc_sigmask, set,
|
||||
@@ -271,11 +257,12 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs,
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
DBG(1,"setup_rt_frame: frame->info = 0x%p\n", &frame->info);
|
||||
DBG(1, "%s: frame->info = 0x%p\n", __func__, &frame->info);
|
||||
err |= copy_siginfo_to_user(&frame->info, &ksig->info);
|
||||
err |= __save_altstack(&frame->uc.uc_stack, regs->gr[30]);
|
||||
DBG(1,"setup_rt_frame: frame->uc = 0x%p\n", &frame->uc);
|
||||
DBG(1,"setup_rt_frame: frame->uc.uc_mcontext = 0x%p\n", &frame->uc.uc_mcontext);
|
||||
DBG(1, "%s: frame->uc = 0x%p\n", __func__, &frame->uc);
|
||||
DBG(1, "%s: frame->uc.uc_mcontext = 0x%p\n",
|
||||
__func__, &frame->uc.uc_mcontext);
|
||||
err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, in_syscall);
|
||||
/* FIXME: Should probably be converted as well for the compat case */
|
||||
err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
|
||||
@@ -284,32 +271,15 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs,
|
||||
if (err)
|
||||
return -EFAULT;
|
||||
|
||||
/* Set up to return from userspace. If provided, use a stub
|
||||
already in userspace. The first words of tramp are used to
|
||||
save the previous sigrestartblock trampoline that might be
|
||||
on the stack. We start the sigreturn trampoline at
|
||||
SIGRESTARTBLOCK_TRAMP+X. */
|
||||
err |= __put_user(in_syscall ? INSN_LDI_R25_1 : INSN_LDI_R25_0,
|
||||
&frame->tramp[SIGRESTARTBLOCK_TRAMP+0]);
|
||||
err |= __put_user(INSN_LDI_R20,
|
||||
&frame->tramp[SIGRESTARTBLOCK_TRAMP+1]);
|
||||
err |= __put_user(INSN_BLE_SR2_R0,
|
||||
&frame->tramp[SIGRESTARTBLOCK_TRAMP+2]);
|
||||
err |= __put_user(INSN_NOP, &frame->tramp[SIGRESTARTBLOCK_TRAMP+3]);
|
||||
#ifdef CONFIG_64BIT
|
||||
if (!is_compat_task())
|
||||
rp = VDSO64_SYMBOL(current, sigtramp_rt);
|
||||
else
|
||||
#endif
|
||||
rp = VDSO32_SYMBOL(current, sigtramp_rt);
|
||||
|
||||
start = (unsigned long) &frame->tramp[0];
|
||||
end = (unsigned long) &frame->tramp[TRAMP_SIZE];
|
||||
flush_user_dcache_range_asm(start, end);
|
||||
flush_user_icache_range_asm(start, end);
|
||||
|
||||
/* TRAMP Words 0-4, Length 5 = SIGRESTARTBLOCK_TRAMP
|
||||
* TRAMP Words 5-9, Length 4 = SIGRETURN_TRAMP
|
||||
* So the SIGRETURN_TRAMP is at the end of SIGRESTARTBLOCK_TRAMP
|
||||
*/
|
||||
rp = (unsigned long) &frame->tramp[SIGRESTARTBLOCK_TRAMP];
|
||||
|
||||
if (err)
|
||||
return -EFAULT;
|
||||
if (in_syscall)
|
||||
rp += 4*4; /* skip 4 instructions and start at ldi 1,%r25 */
|
||||
|
||||
haddr = A(ksig->ka.sa.sa_handler);
|
||||
/* The sa_handler may be a pointer to a function descriptor */
|
||||
@@ -340,8 +310,8 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs,
|
||||
|
||||
haddr = fdesc.addr;
|
||||
regs->gr[19] = fdesc.gp;
|
||||
DBG(1,"setup_rt_frame: 64 bit signal, exe=%#lx, r19=%#lx, in_syscall=%d\n",
|
||||
haddr, regs->gr[19], in_syscall);
|
||||
DBG(1, "%s: 64 bit signal, exe=%#lx, r19=%#lx, in_syscall=%d\n",
|
||||
__func__, haddr, regs->gr[19], in_syscall);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -351,7 +321,7 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs,
|
||||
regs->gr[31] = haddr;
|
||||
#ifdef CONFIG_64BIT
|
||||
if (!test_thread_flag(TIF_32BIT))
|
||||
sigframe_size |= 1;
|
||||
sigframe_size |= 1; /* XXX ???? */
|
||||
#endif
|
||||
} else {
|
||||
unsigned long psw = USER_PSW;
|
||||
@@ -373,11 +343,11 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs,
|
||||
}
|
||||
|
||||
regs->gr[0] = psw;
|
||||
regs->iaoq[0] = haddr | 3;
|
||||
regs->iaoq[0] = haddr | PRIV_USER;
|
||||
regs->iaoq[1] = regs->iaoq[0] + 4;
|
||||
}
|
||||
|
||||
regs->gr[2] = rp; /* userland return pointer */
|
||||
regs->gr[2] = rp; /* userland return pointer */
|
||||
regs->gr[26] = ksig->sig; /* signal number */
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
@@ -391,15 +361,15 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs,
|
||||
regs->gr[24] = A(&frame->uc); /* ucontext pointer */
|
||||
}
|
||||
|
||||
DBG(1,"setup_rt_frame: making sigreturn frame: %#lx + %#lx = %#lx\n",
|
||||
DBG(1, "%s: making sigreturn frame: %#lx + %#lx = %#lx\n", __func__,
|
||||
regs->gr[30], sigframe_size,
|
||||
regs->gr[30] + sigframe_size);
|
||||
/* Raise the user stack pointer to make a proper call frame. */
|
||||
regs->gr[30] = (A(frame) + sigframe_size);
|
||||
|
||||
|
||||
DBG(1,"setup_rt_frame: sig deliver (%s,%d) frame=0x%p sp=%#lx iaoq=%#lx/%#lx rp=%#lx\n",
|
||||
current->comm, current->pid, frame, regs->gr[30],
|
||||
DBG(1, "%s: sig deliver (%s,%d) frame=0x%p sp=%#lx iaoq=%#lx/%#lx rp=%#lx\n",
|
||||
__func__, current->comm, current->pid, frame, regs->gr[30],
|
||||
regs->iaoq[0], regs->iaoq[1], rp);
|
||||
|
||||
return 0;
|
||||
@@ -415,8 +385,8 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs, int in_syscall)
|
||||
int ret;
|
||||
sigset_t *oldset = sigmask_to_save();
|
||||
|
||||
DBG(1,"handle_signal: sig=%ld, ka=%p, info=%p, oldset=%p, regs=%p\n",
|
||||
ksig->sig, ksig->ka, ksig->info, oldset, regs);
|
||||
DBG(1, "%s: sig=%d, ka=%p, info=%p, oldset=%p, regs=%p\n",
|
||||
__func__, ksig->sig, &ksig->ka, &ksig->info, oldset, regs);
|
||||
|
||||
/* Set up the stack frame */
|
||||
ret = setup_rt_frame(ksig, oldset, regs, in_syscall);
|
||||
@@ -424,8 +394,8 @@ handle_signal(struct ksignal *ksig, struct pt_regs *regs, int in_syscall)
|
||||
signal_setup_done(ret, ksig, test_thread_flag(TIF_SINGLESTEP) ||
|
||||
test_thread_flag(TIF_BLOCKSTEP));
|
||||
|
||||
DBG(1,KERN_DEBUG "do_signal: Exit (success), regs->gr[28] = %ld\n",
|
||||
regs->gr[28]);
|
||||
DBG(1, "%s: Exit (success), regs->gr[28] = %ld\n",
|
||||
__func__, regs->gr[28]);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -483,21 +453,27 @@ syscall_restart(struct pt_regs *regs, struct k_sigaction *ka)
|
||||
if (regs->orig_r28)
|
||||
return;
|
||||
regs->orig_r28 = 1; /* no more restarts */
|
||||
|
||||
DBG(1, "%s: orig_r28 = %ld pid %d r20 %ld\n",
|
||||
__func__, regs->orig_r28, task_pid_nr(current), regs->gr[20]);
|
||||
|
||||
/* Check the return code */
|
||||
switch (regs->gr[28]) {
|
||||
case -ERESTART_RESTARTBLOCK:
|
||||
case -ERESTARTNOHAND:
|
||||
DBG(1,"ERESTARTNOHAND: returning -EINTR\n");
|
||||
DBG(1, "%s: ERESTARTNOHAND: returning -EINTR\n", __func__);
|
||||
regs->gr[28] = -EINTR;
|
||||
break;
|
||||
case -ERESTARTSYS:
|
||||
if (!(ka->sa.sa_flags & SA_RESTART)) {
|
||||
DBG(1,"ERESTARTSYS: putting -EINTR\n");
|
||||
DBG(1, "%s: ERESTARTSYS: putting -EINTR pid %d\n",
|
||||
__func__, task_pid_nr(current));
|
||||
regs->gr[28] = -EINTR;
|
||||
break;
|
||||
}
|
||||
fallthrough;
|
||||
case -ERESTARTNOINTR:
|
||||
DBG(1, "%s: %ld\n", __func__, regs->gr[28]);
|
||||
check_syscallno_in_delay_branch(regs);
|
||||
break;
|
||||
}
|
||||
@@ -509,50 +485,52 @@ insert_restart_trampoline(struct pt_regs *regs)
|
||||
if (regs->orig_r28)
|
||||
return;
|
||||
regs->orig_r28 = 1; /* no more restarts */
|
||||
switch(regs->gr[28]) {
|
||||
|
||||
DBG(2, "%s: gr28 = %ld pid %d\n",
|
||||
__func__, regs->gr[28], task_pid_nr(current));
|
||||
|
||||
switch (regs->gr[28]) {
|
||||
case -ERESTART_RESTARTBLOCK: {
|
||||
/* Restart the system call - no handlers present */
|
||||
unsigned int *usp = (unsigned int *)regs->gr[30];
|
||||
unsigned long start = (unsigned long) &usp[2];
|
||||
unsigned long end = (unsigned long) &usp[5];
|
||||
unsigned long rp;
|
||||
long err = 0;
|
||||
|
||||
/* check that we don't exceed the stack */
|
||||
if (A(&usp[0]) >= user_addr_max() - 5 * sizeof(int))
|
||||
return;
|
||||
|
||||
/* Setup a trampoline to restart the syscall
|
||||
* with __NR_restart_syscall
|
||||
/* Call trampoline in vdso to restart the syscall
|
||||
* with __NR_restart_syscall.
|
||||
* Original return addresses are on stack like this:
|
||||
*
|
||||
* 0: <return address (orig r31)>
|
||||
* 4: <2nd half for 64-bit>
|
||||
* 8: ldw 0(%sp), %r31
|
||||
* 12: be 0x100(%sr2, %r0)
|
||||
* 16: ldi __NR_restart_syscall, %r20
|
||||
*/
|
||||
#ifdef CONFIG_64BIT
|
||||
err |= put_user(regs->gr[31] >> 32, &usp[0]);
|
||||
err |= put_user(regs->gr[31] & 0xffffffff, &usp[1]);
|
||||
err |= put_user(0x0fc010df, &usp[2]);
|
||||
#else
|
||||
err |= put_user(regs->gr[31], &usp[0]);
|
||||
err |= put_user(0x0fc0109f, &usp[2]);
|
||||
if (!is_compat_task()) {
|
||||
err |= put_user(regs->gr[31] >> 32, &usp[0]);
|
||||
err |= put_user(regs->gr[31] & 0xffffffff, &usp[1]);
|
||||
rp = VDSO64_SYMBOL(current, restart_syscall);
|
||||
} else
|
||||
#endif
|
||||
err |= put_user(0xe0008200, &usp[3]);
|
||||
err |= put_user(0x34140000, &usp[4]);
|
||||
|
||||
{
|
||||
err |= put_user(regs->gr[31], &usp[0]);
|
||||
rp = VDSO32_SYMBOL(current, restart_syscall);
|
||||
}
|
||||
WARN_ON(err);
|
||||
|
||||
/* flush data/instruction cache for new insns */
|
||||
flush_user_dcache_range_asm(start, end);
|
||||
flush_user_icache_range_asm(start, end);
|
||||
|
||||
regs->gr[31] = regs->gr[30] + 8;
|
||||
regs->gr[31] = rp;
|
||||
DBG(1, "%s: ERESTART_RESTARTBLOCK\n", __func__);
|
||||
return;
|
||||
}
|
||||
case -EINTR:
|
||||
/* ok, was handled before and should be returned. */
|
||||
break;
|
||||
case -ERESTARTNOHAND:
|
||||
case -ERESTARTSYS:
|
||||
case -ERESTARTNOINTR:
|
||||
DBG(1, "%s: Type %ld\n", __func__, regs->gr[28]);
|
||||
check_syscallno_in_delay_branch(regs);
|
||||
return;
|
||||
default:
|
||||
@@ -567,30 +545,35 @@ insert_restart_trampoline(struct pt_regs *regs)
|
||||
* registers). As noted below, the syscall number gets restored for
|
||||
* us due to the magic of delayed branching.
|
||||
*/
|
||||
asmlinkage void
|
||||
do_signal(struct pt_regs *regs, long in_syscall)
|
||||
static void do_signal(struct pt_regs *regs, long in_syscall)
|
||||
{
|
||||
struct ksignal ksig;
|
||||
int restart_syscall;
|
||||
bool has_handler;
|
||||
|
||||
DBG(1,"\ndo_signal: regs=0x%p, sr7 %#lx, in_syscall=%d\n",
|
||||
regs, regs->sr[7], in_syscall);
|
||||
has_handler = get_signal(&ksig);
|
||||
|
||||
if (get_signal(&ksig)) {
|
||||
DBG(3,"do_signal: signr = %d, regs->gr[28] = %ld\n", signr, regs->gr[28]);
|
||||
restart_syscall = 0;
|
||||
if (in_syscall)
|
||||
restart_syscall = 1;
|
||||
|
||||
if (has_handler) {
|
||||
/* Restart a system call if necessary. */
|
||||
if (in_syscall)
|
||||
if (restart_syscall)
|
||||
syscall_restart(regs, &ksig.ka);
|
||||
|
||||
handle_signal(&ksig, regs, in_syscall);
|
||||
DBG(1, "%s: Handled signal pid %d\n",
|
||||
__func__, task_pid_nr(current));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Did we come from a system call? */
|
||||
if (in_syscall)
|
||||
/* Do we need to restart the system call? */
|
||||
if (restart_syscall)
|
||||
insert_restart_trampoline(regs);
|
||||
|
||||
DBG(1,"do_signal: Exit (not delivered), regs->gr[28] = %ld\n",
|
||||
regs->gr[28]);
|
||||
DBG(1, "%s: Exit (not delivered), regs->gr[28] = %ld orig_r28 = %ld pid %d\n",
|
||||
__func__, regs->gr[28], regs->orig_r28, task_pid_nr(current));
|
||||
|
||||
restore_saved_sigmask();
|
||||
}
|
||||
|
||||
@@ -36,21 +36,12 @@ struct compat_regfile {
|
||||
compat_int_t rf_sar;
|
||||
};
|
||||
|
||||
#define COMPAT_SIGRETURN_TRAMP 4
|
||||
#define COMPAT_SIGRESTARTBLOCK_TRAMP 5
|
||||
#define COMPAT_TRAMP_SIZE (COMPAT_SIGRETURN_TRAMP + \
|
||||
COMPAT_SIGRESTARTBLOCK_TRAMP)
|
||||
|
||||
struct compat_rt_sigframe {
|
||||
/* XXX: Must match trampoline size in arch/parisc/kernel/signal.c
|
||||
Secondary to that it must protect the ERESTART_RESTARTBLOCK
|
||||
trampoline we left on the stack (we were bad and didn't
|
||||
change sp so we could run really fast.) */
|
||||
compat_uint_t tramp[COMPAT_TRAMP_SIZE];
|
||||
compat_siginfo_t info;
|
||||
struct compat_ucontext uc;
|
||||
/* Hidden location of truncated registers, *must* be last. */
|
||||
struct compat_regfile regs;
|
||||
unsigned int tramp[2]; /* holds original return address */
|
||||
compat_siginfo_t info;
|
||||
struct compat_ucontext uc;
|
||||
/* Hidden location of truncated registers, *must* be last. */
|
||||
struct compat_regfile regs;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
122
arch/parisc/kernel/vdso.c
Normal file
122
arch/parisc/kernel/vdso.c
Normal file
@@ -0,0 +1,122 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2022 Helge Deller <deller@gmx.de>
|
||||
*
|
||||
* based on arch/s390/kernel/vdso.c which is
|
||||
* Copyright IBM Corp. 2008
|
||||
* Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/elf.h>
|
||||
#include <linux/timekeeper_internal.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/nsproxy.h>
|
||||
#include <linux/time_namespace.h>
|
||||
#include <linux/random.h>
|
||||
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/vdso.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
extern char vdso32_start, vdso32_end;
|
||||
extern char vdso64_start, vdso64_end;
|
||||
|
||||
static int vdso_mremap(const struct vm_special_mapping *sm,
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
current->mm->context.vdso_base = vma->vm_start;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
static struct vm_special_mapping vdso64_mapping = {
|
||||
.name = "[vdso]",
|
||||
.mremap = vdso_mremap,
|
||||
};
|
||||
#endif
|
||||
|
||||
static struct vm_special_mapping vdso32_mapping = {
|
||||
.name = "[vdso]",
|
||||
.mremap = vdso_mremap,
|
||||
};
|
||||
|
||||
/*
|
||||
* This is called from binfmt_elf, we create the special vma for the
|
||||
* vDSO and insert it into the mm struct tree
|
||||
*/
|
||||
int arch_setup_additional_pages(struct linux_binprm *bprm,
|
||||
int executable_stack)
|
||||
{
|
||||
|
||||
unsigned long vdso_text_start, vdso_text_len, map_base;
|
||||
struct vm_special_mapping *vdso_mapping;
|
||||
struct mm_struct *mm = current->mm;
|
||||
struct vm_area_struct *vma;
|
||||
int rc;
|
||||
|
||||
if (mmap_write_lock_killable(mm))
|
||||
return -EINTR;
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
if (!is_compat_task()) {
|
||||
vdso_text_len = &vdso64_end - &vdso64_start;
|
||||
vdso_mapping = &vdso64_mapping;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
vdso_text_len = &vdso32_end - &vdso32_start;
|
||||
vdso_mapping = &vdso32_mapping;
|
||||
}
|
||||
|
||||
map_base = mm->mmap_base;
|
||||
if (current->flags & PF_RANDOMIZE)
|
||||
map_base -= (get_random_int() & 0x1f) * PAGE_SIZE;
|
||||
|
||||
vdso_text_start = get_unmapped_area(NULL, map_base, vdso_text_len, 0, 0);
|
||||
|
||||
/* VM_MAYWRITE for COW so gdb can set breakpoints */
|
||||
vma = _install_special_mapping(mm, vdso_text_start, vdso_text_len,
|
||||
VM_READ|VM_EXEC|
|
||||
VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
|
||||
vdso_mapping);
|
||||
if (IS_ERR(vma)) {
|
||||
do_munmap(mm, vdso_text_start, PAGE_SIZE, NULL);
|
||||
rc = PTR_ERR(vma);
|
||||
} else {
|
||||
current->mm->context.vdso_base = vdso_text_start;
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
mmap_write_unlock(mm);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct page ** __init vdso_setup_pages(void *start, void *end)
|
||||
{
|
||||
int pages = (end - start) >> PAGE_SHIFT;
|
||||
struct page **pagelist;
|
||||
int i;
|
||||
|
||||
pagelist = kcalloc(pages + 1, sizeof(struct page *), GFP_KERNEL);
|
||||
if (!pagelist)
|
||||
panic("%s: Cannot allocate page list for VDSO", __func__);
|
||||
for (i = 0; i < pages; i++)
|
||||
pagelist[i] = virt_to_page(start + i * PAGE_SIZE);
|
||||
return pagelist;
|
||||
}
|
||||
|
||||
static int __init vdso_init(void)
|
||||
{
|
||||
#ifdef CONFIG_64BIT
|
||||
vdso64_mapping.pages = vdso_setup_pages(&vdso64_start, &vdso64_end);
|
||||
#endif
|
||||
if (IS_ENABLED(CONFIG_COMPAT) || !IS_ENABLED(CONFIG_64BIT))
|
||||
vdso32_mapping.pages = vdso_setup_pages(&vdso32_start, &vdso32_end);
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(vdso_init);
|
||||
53
arch/parisc/kernel/vdso32/Makefile
Normal file
53
arch/parisc/kernel/vdso32/Makefile
Normal file
@@ -0,0 +1,53 @@
|
||||
# List of files in the vdso, has to be asm only for now
|
||||
|
||||
obj-vdso32 = note.o sigtramp.o restart_syscall.o
|
||||
|
||||
# Build rules
|
||||
|
||||
targets := $(obj-vdso32) vdso32.so
|
||||
obj-vdso32 := $(addprefix $(obj)/, $(obj-vdso32))
|
||||
|
||||
ccflags-y := -shared -fno-common -fbuiltin -mno-fast-indirect-calls -O2 -mno-long-calls
|
||||
# -march=1.1 -mschedule=7100LC
|
||||
ccflags-y += -nostdlib -Wl,-soname=linux-vdso32.so.1 \
|
||||
$(call ld-option, -Wl$(comma)--hash-style=sysv)
|
||||
asflags-y := -D__VDSO32__ -s
|
||||
|
||||
KBUILD_AFLAGS += -DBUILD_VDSO
|
||||
KBUILD_CFLAGS += -DBUILD_VDSO -DDISABLE_BRANCH_PROFILING
|
||||
|
||||
VDSO_LIBGCC := $(shell $(CROSS32CC) -print-libgcc-file-name)
|
||||
|
||||
obj-y += vdso32_wrapper.o
|
||||
extra-y += vdso32.lds
|
||||
CPPFLAGS_vdso32.lds += -P -C # -U$(ARCH)
|
||||
|
||||
$(obj)/vdso32_wrapper.o : $(obj)/vdso32.so FORCE
|
||||
|
||||
# Force dependency (incbin is bad)
|
||||
# link rule for the .so file, .lds has to be first
|
||||
$(obj)/vdso32.so: $(src)/vdso32.lds $(obj-vdso32) $(obj-cvdso32) $(VDSO_LIBGCC)
|
||||
$(call if_changed,vdso32ld)
|
||||
|
||||
# assembly rules for the .S files
|
||||
$(obj-vdso32): %.o: %.S FORCE
|
||||
$(call if_changed_dep,vdso32as)
|
||||
|
||||
$(obj-cvdso32): %.o: %.c FORCE
|
||||
$(call if_changed_dep,vdso32cc)
|
||||
|
||||
# actual build commands
|
||||
quiet_cmd_vdso32ld = VDSO32L $@
|
||||
cmd_vdso32ld = $(CROSS32CC) $(c_flags) -Wl,-T $^ -o $@
|
||||
quiet_cmd_vdso32as = VDSO32A $@
|
||||
cmd_vdso32as = $(CROSS32CC) $(a_flags) -c -o $@ $<
|
||||
quiet_cmd_vdso32cc = VDSO32C $@
|
||||
cmd_vdso32cc = $(CROSS32CC) $(c_flags) -c -fPIC -mno-fast-indirect-calls -o $@ $<
|
||||
|
||||
# Generate VDSO offsets using helper script
|
||||
gen-vdsosym := $(srctree)/$(src)/gen_vdso_offsets.sh
|
||||
quiet_cmd_vdsosym = VDSOSYM $@
|
||||
cmd_vdsosym = $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@
|
||||
|
||||
include/generated/vdso32-offsets.h: $(obj)/vdso32.so FORCE
|
||||
$(call if_changed,vdsosym)
|
||||
15
arch/parisc/kernel/vdso32/gen_vdso_offsets.sh
Executable file
15
arch/parisc/kernel/vdso32/gen_vdso_offsets.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#
|
||||
# Match symbols in the DSO that look like VDSO_*; produce a header file
|
||||
# of constant offsets into the shared object.
|
||||
#
|
||||
# Doing this inside the Makefile will break the $(filter-out) function,
|
||||
# causing Kbuild to rebuild the vdso-offsets header file every time.
|
||||
#
|
||||
# Inspired by arm64 version.
|
||||
#
|
||||
|
||||
LC_ALL=C
|
||||
sed -n 's/\([0-9a-f]*\) . __kernel_\(.*\)/\#define vdso32_offset_\2\t0x\1/p'
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user