parisc architecture updates for kernel v5.18-rc1
- add vDSO support (allows us to use read-only stacks) - many TLB and cache flush optimizations (by Dave Anglin) - fix handling of probe non-access faults (by Dave Anglin) - fix invalidate/flush vmap routines (by Dave Anglin) - avoid using hardware single-step in kprobes - enable ARCH_HAS_DEBUG_VM_PGTABLE - many cleanups in unaligned handlers, e.g. rewrite of existing assembly code - always use the self-extracting kernel feature - big refacturing and code reductions regarding space-register usage in get_user() and put_user() - add fillrect() support to stifb graphics driver -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQS86RI+GtKfB8BJu973ErUQojoPXwUCYjixdQAKCRD3ErUQojoP XwfXAP9asNqSpXVtNNkSJS6RW9X/dw3VoBteXkytq7Cqpl7DMwD/TXwXgHd7rGzo lQZBBTUsNdDxbZqnXYnXRf6XkHUfMQY= =pFju -----END PGP SIGNATURE----- Merge tag 'for-5.18/parisc-1' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/parisc-linux Pull parisc architecture updates from Helge Deller: - add vDSO support (allows us to use non-executable stacks) - many TLB and cache flush optimizations (by Dave Anglin) - fix handling of probe non-access faults (by Dave Anglin) - fix invalidate/flush vmap routines (by Dave Anglin) - avoid using hardware single-step in kprobes - enable ARCH_HAS_DEBUG_VM_PGTABLE - many cleanups in unaligned handlers, e.g. rewrite of existing assembly code - always use the self-extracting kernel feature - big refacturing and code reductions regarding space-register usage in get_user() and put_user() - add fillrect() support to stifb graphics driver * tag 'for-5.18/parisc-1' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/parisc-linux: (23 commits) parisc: Fix invalidate/flush vmap routines parisc: Avoid flushing cache on cache-less machines parisc: Avoid using hardware single-step in kprobes parisc: Improve CPU socket and core bootup info text parisc: Enable ARCH_HAS_DEBUG_VM_PGTABLE parisc: Avoid calling SMP cache flush functions on cache-less machines parisc: Increase parisc_cache_flush_threshold setting parisc/unaligned: Enhance user-space visible output parisc/unaligned: Rewrite 32-bit inline assembly of emulate_sth() parisc/unaligned: Rewrite 32-bit inline assembly of emulate_ldd() parisc/unaligned: Rewrite inline assembly of emulate_ldw() parisc/unaligned: Rewrite inline assembly of emulate_ldh() parisc/unaligned: Use EFAULT fixup handler in unaligned handlers parisc: Reduce code size by optimizing get_current() function calls parisc: Use constants to encode the space registers like SR_KERNEL parisc: Use SR_USER and SR_KERNEL in get_user() and put_user() parisc: Add defines for various space register parisc: Always use the self-extracting kernel feature video/fbdev/stifb: Implement the stifb_fillrect() function parisc: Add vDSO support ...
This commit is contained in:
commit
a04b1bf574
@ -10,10 +10,12 @@ 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
|
||||
select ARCH_STACKWALK
|
||||
select ARCH_HAS_DEBUG_VM_PGTABLE
|
||||
select HAVE_RELIABLE_STACKTRACE
|
||||
select DMA_OPS
|
||||
select RTC_CLASS
|
||||
@ -259,18 +261,6 @@ config PARISC_PAGE_SIZE_64KB
|
||||
|
||||
endchoice
|
||||
|
||||
config PARISC_SELF_EXTRACT
|
||||
bool "Build kernel as self-extracting executable"
|
||||
default y
|
||||
help
|
||||
Say Y if you want to build the parisc kernel as a kind of
|
||||
self-extracting executable.
|
||||
|
||||
If you say N here, the kernel will be compressed with gzip
|
||||
which can be loaded by the palo bootloader directly too.
|
||||
|
||||
If you don't know what to do here, say Y.
|
||||
|
||||
config SMP
|
||||
bool "Symmetric multi-processing support"
|
||||
help
|
||||
|
@ -15,12 +15,8 @@
|
||||
# Mike Shaver, Helge Deller and Martin K. Petersen
|
||||
#
|
||||
|
||||
ifdef CONFIG_PARISC_SELF_EXTRACT
|
||||
boot := arch/parisc/boot
|
||||
KBUILD_IMAGE := $(boot)/bzImage
|
||||
else
|
||||
KBUILD_IMAGE := vmlinuz
|
||||
endif
|
||||
|
||||
NM = sh $(srctree)/arch/parisc/nm
|
||||
CHECKFLAGS += -D__hppa__=1
|
||||
@ -44,6 +40,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
|
||||
@ -155,14 +161,29 @@ Image: vmlinux
|
||||
bzImage: vmlinux
|
||||
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
|
||||
|
||||
ifdef CONFIG_PARISC_SELF_EXTRACT
|
||||
vmlinuz: bzImage
|
||||
$(OBJCOPY) $(boot)/bzImage $@
|
||||
else
|
||||
vmlinuz: vmlinux
|
||||
@$(KGZIP) -cf -9 $< > $@
|
||||
|
||||
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)"
|
||||
|
@ -47,6 +47,12 @@
|
||||
#define PRIV_USER 3
|
||||
#define PRIV_KERNEL 0
|
||||
|
||||
/* Space register used inside kernel */
|
||||
#define SR_KERNEL 0
|
||||
#define SR_TEMP1 1
|
||||
#define SR_TEMP2 2
|
||||
#define SR_USER 3
|
||||
|
||||
#ifdef __ASSEMBLY__
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
|
@ -39,16 +39,13 @@ extern int icache_stride;
|
||||
extern struct pdc_cache_info cache_info;
|
||||
void parisc_setup_cache_timing(void);
|
||||
|
||||
#define pdtlb(addr) asm volatile("pdtlb 0(%%sr1,%0)" \
|
||||
#define pdtlb(sr, addr) asm volatile("pdtlb 0(%%sr%0,%1)" \
|
||||
ALTERNATIVE(ALT_COND_NO_SMP, INSN_PxTLB) \
|
||||
: : "r" (addr) : "memory")
|
||||
#define pitlb(addr) asm volatile("pitlb 0(%%sr1,%0)" \
|
||||
: : "i"(sr), "r" (addr) : "memory")
|
||||
#define pitlb(sr, addr) asm volatile("pitlb 0(%%sr%0,%1)" \
|
||||
ALTERNATIVE(ALT_COND_NO_SMP, INSN_PxTLB) \
|
||||
ALTERNATIVE(ALT_COND_NO_SPLIT_TLB, INSN_NOP) \
|
||||
: : "r" (addr) : "memory")
|
||||
#define pdtlb_kernel(addr) asm volatile("pdtlb 0(%0)" \
|
||||
ALTERNATIVE(ALT_COND_NO_SMP, INSN_PxTLB) \
|
||||
: : "r" (addr) : "memory")
|
||||
: : "i"(sr), "r" (addr) : "memory")
|
||||
|
||||
#define asm_io_fdc(addr) asm volatile("fdc %%r0(%0)" \
|
||||
ALTERNATIVE(ALT_COND_NO_DCACHE, INSN_NOP) \
|
||||
|
@ -9,16 +9,11 @@
|
||||
/* The usual comment is "Caches aren't brain-dead on the <architecture>".
|
||||
* Unfortunately, that doesn't apply to PA-RISC. */
|
||||
|
||||
/* Internal implementation */
|
||||
void flush_data_cache_local(void *); /* flushes local data-cache only */
|
||||
void flush_instruction_cache_local(void *); /* flushes local code-cache only */
|
||||
#ifdef CONFIG_SMP
|
||||
void flush_data_cache(void); /* flushes data-cache only (all processors) */
|
||||
void flush_instruction_cache(void); /* flushes i-cache only (all processors) */
|
||||
#else
|
||||
#define flush_data_cache() flush_data_cache_local(NULL)
|
||||
#define flush_instruction_cache() flush_instruction_cache_local(NULL)
|
||||
#endif
|
||||
#include <linux/jump_label.h>
|
||||
|
||||
DECLARE_STATIC_KEY_TRUE(parisc_has_cache);
|
||||
DECLARE_STATIC_KEY_TRUE(parisc_has_dcache);
|
||||
DECLARE_STATIC_KEY_TRUE(parisc_has_icache);
|
||||
|
||||
#define flush_cache_dup_mm(mm) flush_cache_mm(mm)
|
||||
|
||||
|
@ -2,14 +2,16 @@
|
||||
#ifndef _ASM_PARISC_CURRENT_H
|
||||
#define _ASM_PARISC_CURRENT_H
|
||||
|
||||
#include <asm/special_insns.h>
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
struct task_struct;
|
||||
|
||||
static __always_inline struct task_struct *get_current(void)
|
||||
{
|
||||
return (struct task_struct *) mfctl(30);
|
||||
struct task_struct *ts;
|
||||
|
||||
/* do not use mfctl() macro as it is marked volatile */
|
||||
asm( "mfctl %%cr30,%0" : "=r" (ts) );
|
||||
return ts;
|
||||
}
|
||||
|
||||
#define current get_current()
|
||||
|
@ -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
|
||||
|
@ -18,8 +18,9 @@
|
||||
#include <linux/notifier.h>
|
||||
|
||||
#define PARISC_KPROBES_BREAK_INSN 0x3ff801f
|
||||
#define PARISC_KPROBES_BREAK_INSN2 0x3ff801e
|
||||
#define __ARCH_WANT_KPROBES_INSN_SLOT
|
||||
#define MAX_INSN_SIZE 1
|
||||
#define MAX_INSN_SIZE 2
|
||||
|
||||
typedef u32 kprobe_opcode_t;
|
||||
struct kprobe;
|
||||
@ -29,7 +30,7 @@ void arch_remove_kprobe(struct kprobe *p);
|
||||
#define flush_insn_slot(p) \
|
||||
flush_icache_range((unsigned long)&(p)->ainsn.insn[0], \
|
||||
(unsigned long)&(p)->ainsn.insn[0] + \
|
||||
sizeof(kprobe_opcode_t))
|
||||
MAX_INSN_SIZE*sizeof(kprobe_opcode_t))
|
||||
|
||||
#define kretprobe_blacklist_size 0
|
||||
|
||||
|
@ -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, SR_USER);
|
||||
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,9 +70,9 @@ static inline void purge_tlb_entries(struct mm_struct *mm, unsigned long addr)
|
||||
unsigned long flags;
|
||||
|
||||
purge_tlb_start(flags);
|
||||
mtsp(mm->context, 1);
|
||||
pdtlb(addr);
|
||||
pitlb(addr);
|
||||
mtsp(mm->context.space_id, SR_TEMP1);
|
||||
pdtlb(SR_TEMP1, addr);
|
||||
pitlb(SR_TEMP1, 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;
|
||||
};
|
||||
|
@ -55,8 +55,8 @@ static inline void set_eiem(unsigned long val)
|
||||
#define mfsp(reg) ({ \
|
||||
unsigned long cr; \
|
||||
__asm__ __volatile__( \
|
||||
"mfsp " #reg ",%0" : \
|
||||
"=r" (cr) \
|
||||
"mfsp %%sr%1,%0" \
|
||||
: "=r" (cr) : "i"(reg) \
|
||||
); \
|
||||
cr; \
|
||||
})
|
||||
|
@ -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)
|
||||
|
@ -18,6 +18,7 @@ unsigned long parisc_acctyp(unsigned long code, unsigned int inst);
|
||||
const char *trap_name(unsigned long code);
|
||||
void do_page_fault(struct pt_regs *regs, unsigned long code,
|
||||
unsigned long address);
|
||||
int handle_nadtlb_fault(struct pt_regs *regs);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -79,18 +79,18 @@ struct exception_table_entry {
|
||||
|
||||
#define __get_user(val, ptr) \
|
||||
({ \
|
||||
__get_user_internal("%%sr3,", val, ptr); \
|
||||
__get_user_internal(SR_USER, val, ptr); \
|
||||
})
|
||||
|
||||
#define __get_user_asm(sr, val, ldx, ptr) \
|
||||
{ \
|
||||
register long __gu_val; \
|
||||
\
|
||||
__asm__("1: " ldx " 0(" sr "%2),%0\n" \
|
||||
__asm__("1: " ldx " 0(%%sr%2,%3),%0\n" \
|
||||
"9:\n" \
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b) \
|
||||
: "=r"(__gu_val), "+r"(__gu_err) \
|
||||
: "r"(ptr)); \
|
||||
: "i"(sr), "r"(ptr)); \
|
||||
\
|
||||
(val) = (__force __typeof__(*(ptr))) __gu_val; \
|
||||
}
|
||||
@ -100,7 +100,7 @@ struct exception_table_entry {
|
||||
{ \
|
||||
type __z; \
|
||||
long __err; \
|
||||
__err = __get_user_internal("%%sr0,", __z, (type *)(src)); \
|
||||
__err = __get_user_internal(SR_KERNEL, __z, (type *)(src)); \
|
||||
if (unlikely(__err)) \
|
||||
goto err_label; \
|
||||
else \
|
||||
@ -118,13 +118,13 @@ struct exception_table_entry {
|
||||
} __gu_tmp; \
|
||||
\
|
||||
__asm__(" copy %%r0,%R0\n" \
|
||||
"1: ldw 0(" sr "%2),%0\n" \
|
||||
"2: ldw 4(" sr "%2),%R0\n" \
|
||||
"1: ldw 0(%%sr%2,%3),%0\n" \
|
||||
"2: ldw 4(%%sr%2,%3),%R0\n" \
|
||||
"9:\n" \
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b) \
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 9b) \
|
||||
: "=&r"(__gu_tmp.l), "+r"(__gu_err) \
|
||||
: "r"(ptr)); \
|
||||
: "i"(sr), "r"(ptr)); \
|
||||
\
|
||||
(val) = __gu_tmp.t; \
|
||||
}
|
||||
@ -151,14 +151,14 @@ struct exception_table_entry {
|
||||
({ \
|
||||
__typeof__(&*(ptr)) __ptr = ptr; \
|
||||
__typeof__(*(__ptr)) __x = (__typeof__(*(__ptr)))(x); \
|
||||
__put_user_internal("%%sr3,", __x, __ptr); \
|
||||
__put_user_internal(SR_USER, __x, __ptr); \
|
||||
})
|
||||
|
||||
#define __put_kernel_nofault(dst, src, type, err_label) \
|
||||
{ \
|
||||
type __z = *(type *)(src); \
|
||||
long __err; \
|
||||
__err = __put_user_internal("%%sr0,", __z, (type *)(dst)); \
|
||||
__err = __put_user_internal(SR_KERNEL, __z, (type *)(dst)); \
|
||||
if (unlikely(__err)) \
|
||||
goto err_label; \
|
||||
}
|
||||
@ -178,24 +178,24 @@ struct exception_table_entry {
|
||||
|
||||
#define __put_user_asm(sr, stx, x, ptr) \
|
||||
__asm__ __volatile__ ( \
|
||||
"1: " stx " %2,0(" sr "%1)\n" \
|
||||
"1: " stx " %1,0(%%sr%2,%3)\n" \
|
||||
"9:\n" \
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b) \
|
||||
: "+r"(__pu_err) \
|
||||
: "r"(ptr), "r"(x))
|
||||
: "r"(x), "i"(sr), "r"(ptr))
|
||||
|
||||
|
||||
#if !defined(CONFIG_64BIT)
|
||||
|
||||
#define __put_user_asm64(sr, __val, ptr) do { \
|
||||
__asm__ __volatile__ ( \
|
||||
"1: stw %2,0(" sr "%1)\n" \
|
||||
"2: stw %R2,4(" sr "%1)\n" \
|
||||
"1: stw %1,0(%%sr%2,%3)\n" \
|
||||
"2: stw %R1,4(%%sr%2,%3)\n" \
|
||||
"9:\n" \
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 9b) \
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 9b) \
|
||||
: "+r"(__pu_err) \
|
||||
: "r"(ptr), "r"(__val)); \
|
||||
: "r"(__val), "i"(sr), "r"(ptr)); \
|
||||
} while (0)
|
||||
|
||||
#endif /* !defined(CONFIG_64BIT) */
|
||||
|
@ -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/
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <asm/processor.h>
|
||||
#include <asm/sections.h>
|
||||
#include <asm/alternative.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
@ -107,5 +108,14 @@ void __init apply_alternatives_all(void)
|
||||
apply_alternatives((struct alt_instr *) &__alt_instructions,
|
||||
(struct alt_instr *) &__alt_instructions_end, NULL);
|
||||
|
||||
if (cache_info.dc_size == 0 && cache_info.ic_size == 0) {
|
||||
pr_info("alternatives: optimizing cache-flushes.\n");
|
||||
static_branch_disable(&parisc_has_cache);
|
||||
}
|
||||
if (cache_info.dc_size == 0)
|
||||
static_branch_disable(&parisc_has_dcache);
|
||||
if (cache_info.ic_size == 0)
|
||||
static_branch_disable(&parisc_has_icache);
|
||||
|
||||
set_kernel_text_rw(0);
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -38,6 +38,9 @@ EXPORT_SYMBOL(flush_dcache_page_asm);
|
||||
void purge_dcache_page_asm(unsigned long phys_addr, unsigned long vaddr);
|
||||
void flush_icache_page_asm(unsigned long phys_addr, unsigned long vaddr);
|
||||
|
||||
/* Internal implementation in arch/parisc/kernel/pacache.S */
|
||||
void flush_data_cache_local(void *); /* flushes local data-cache only */
|
||||
void flush_instruction_cache_local(void); /* flushes local code-cache only */
|
||||
|
||||
/* On some machines (i.e., ones with the Merced bus), there can be
|
||||
* only a single PxTLB broadcast at a time; this must be guaranteed
|
||||
@ -58,26 +61,35 @@ struct pdc_cache_info cache_info __ro_after_init;
|
||||
static struct pdc_btlb_info btlb_info __ro_after_init;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
void
|
||||
flush_data_cache(void)
|
||||
{
|
||||
on_each_cpu(flush_data_cache_local, NULL, 1);
|
||||
}
|
||||
void
|
||||
flush_instruction_cache(void)
|
||||
{
|
||||
on_each_cpu(flush_instruction_cache_local, NULL, 1);
|
||||
}
|
||||
#endif
|
||||
DEFINE_STATIC_KEY_TRUE(parisc_has_cache);
|
||||
DEFINE_STATIC_KEY_TRUE(parisc_has_dcache);
|
||||
DEFINE_STATIC_KEY_TRUE(parisc_has_icache);
|
||||
|
||||
void
|
||||
flush_cache_all_local(void)
|
||||
static void cache_flush_local_cpu(void *dummy)
|
||||
{
|
||||
flush_instruction_cache_local(NULL);
|
||||
flush_data_cache_local(NULL);
|
||||
if (static_branch_likely(&parisc_has_icache))
|
||||
flush_instruction_cache_local();
|
||||
if (static_branch_likely(&parisc_has_dcache))
|
||||
flush_data_cache_local(NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(flush_cache_all_local);
|
||||
|
||||
void flush_cache_all_local(void)
|
||||
{
|
||||
cache_flush_local_cpu(NULL);
|
||||
}
|
||||
|
||||
void flush_cache_all(void)
|
||||
{
|
||||
if (static_branch_likely(&parisc_has_cache))
|
||||
on_each_cpu(cache_flush_local_cpu, NULL, 1);
|
||||
}
|
||||
|
||||
static inline void flush_data_cache(void)
|
||||
{
|
||||
if (static_branch_likely(&parisc_has_dcache))
|
||||
on_each_cpu(flush_data_cache_local, NULL, 1);
|
||||
}
|
||||
|
||||
|
||||
/* Virtual address of pfn. */
|
||||
#define pfn_va(pfn) __va(PFN_PHYS(pfn))
|
||||
@ -303,6 +315,8 @@ static inline void
|
||||
__flush_cache_page(struct vm_area_struct *vma, unsigned long vmaddr,
|
||||
unsigned long physaddr)
|
||||
{
|
||||
if (!static_branch_likely(&parisc_has_cache))
|
||||
return;
|
||||
preempt_disable();
|
||||
flush_dcache_page_asm(physaddr, vmaddr);
|
||||
if (vma->vm_flags & VM_EXEC)
|
||||
@ -314,6 +328,8 @@ static inline void
|
||||
__purge_cache_page(struct vm_area_struct *vma, unsigned long vmaddr,
|
||||
unsigned long physaddr)
|
||||
{
|
||||
if (!static_branch_likely(&parisc_has_cache))
|
||||
return;
|
||||
preempt_disable();
|
||||
purge_dcache_page_asm(physaddr, vmaddr);
|
||||
if (vma->vm_flags & VM_EXEC)
|
||||
@ -375,7 +391,6 @@ EXPORT_SYMBOL(flush_dcache_page);
|
||||
|
||||
/* Defined in arch/parisc/kernel/pacache.S */
|
||||
EXPORT_SYMBOL(flush_kernel_dcache_range_asm);
|
||||
EXPORT_SYMBOL(flush_data_cache_local);
|
||||
EXPORT_SYMBOL(flush_kernel_icache_range_asm);
|
||||
|
||||
#define FLUSH_THRESHOLD 0x80000 /* 0.5MB */
|
||||
@ -388,7 +403,7 @@ void __init parisc_setup_cache_timing(void)
|
||||
{
|
||||
unsigned long rangetime, alltime;
|
||||
unsigned long size;
|
||||
unsigned long threshold;
|
||||
unsigned long threshold, threshold2;
|
||||
|
||||
alltime = mfctl(16);
|
||||
flush_data_cache();
|
||||
@ -403,8 +418,20 @@ void __init parisc_setup_cache_timing(void)
|
||||
alltime, size, rangetime);
|
||||
|
||||
threshold = L1_CACHE_ALIGN(size * alltime / rangetime);
|
||||
if (threshold > cache_info.dc_size)
|
||||
threshold = cache_info.dc_size;
|
||||
|
||||
/*
|
||||
* The threshold computed above isn't very reliable since the
|
||||
* flush times depend greatly on the percentage of dirty lines
|
||||
* in the flush range. Further, the whole cache time doesn't
|
||||
* include the time to refill lines that aren't in the mm/vma
|
||||
* being flushed. By timing glibc build and checks on mako cpus,
|
||||
* the following formula seems to work reasonably well. The
|
||||
* value from the timing calculation is too small, and increases
|
||||
* build and check times by almost a factor two.
|
||||
*/
|
||||
threshold2 = cache_info.dc_size * num_online_cpus();
|
||||
if (threshold2 > threshold)
|
||||
threshold = threshold2;
|
||||
if (threshold)
|
||||
parisc_cache_flush_threshold = threshold;
|
||||
printk(KERN_INFO "Cache flush threshold set to %lu KiB\n",
|
||||
@ -457,7 +484,7 @@ void flush_kernel_dcache_page_addr(void *addr)
|
||||
|
||||
flush_kernel_dcache_page_asm(addr);
|
||||
purge_tlb_start(flags);
|
||||
pdtlb_kernel(addr);
|
||||
pdtlb(SR_KERNEL, addr);
|
||||
purge_tlb_end(flags);
|
||||
}
|
||||
EXPORT_SYMBOL(flush_kernel_dcache_page_addr);
|
||||
@ -496,25 +523,15 @@ int __flush_tlb_range(unsigned long sid, unsigned long start,
|
||||
but cause a purge request to be broadcast to other TLBs. */
|
||||
while (start < end) {
|
||||
purge_tlb_start(flags);
|
||||
mtsp(sid, 1);
|
||||
pdtlb(start);
|
||||
pitlb(start);
|
||||
mtsp(sid, SR_TEMP1);
|
||||
pdtlb(SR_TEMP1, start);
|
||||
pitlb(SR_TEMP1, start);
|
||||
purge_tlb_end(flags);
|
||||
start += PAGE_SIZE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cacheflush_h_tmp_function(void *dummy)
|
||||
{
|
||||
flush_cache_all_local();
|
||||
}
|
||||
|
||||
void flush_cache_all(void)
|
||||
{
|
||||
on_each_cpu(cacheflush_h_tmp_function, NULL, 1);
|
||||
}
|
||||
|
||||
static inline unsigned long mm_total_size(struct mm_struct *mm)
|
||||
{
|
||||
struct vm_area_struct *vma;
|
||||
@ -558,15 +575,6 @@ static void flush_cache_pages(struct vm_area_struct *vma, struct mm_struct *mm,
|
||||
}
|
||||
}
|
||||
|
||||
static void flush_user_cache_tlb(struct vm_area_struct *vma,
|
||||
unsigned long start, unsigned long end)
|
||||
{
|
||||
flush_user_dcache_range_asm(start, end);
|
||||
if (vma->vm_flags & VM_EXEC)
|
||||
flush_user_icache_range_asm(start, end);
|
||||
flush_tlb_range(vma, start, end);
|
||||
}
|
||||
|
||||
void flush_cache_mm(struct mm_struct *mm)
|
||||
{
|
||||
struct vm_area_struct *vma;
|
||||
@ -575,23 +583,14 @@ 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;
|
||||
}
|
||||
|
||||
preempt_disable();
|
||||
if (mm->context == mfsp(3)) {
|
||||
for (vma = mm->mmap; vma; vma = vma->vm_next)
|
||||
flush_user_cache_tlb(vma, vma->vm_start, vma->vm_end);
|
||||
preempt_enable();
|
||||
return;
|
||||
}
|
||||
|
||||
for (vma = mm->mmap; vma; vma = vma->vm_next)
|
||||
flush_cache_pages(vma, mm, vma->vm_start, vma->vm_end);
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
void flush_cache_range(struct vm_area_struct *vma,
|
||||
@ -599,29 +598,21 @@ 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;
|
||||
}
|
||||
|
||||
preempt_disable();
|
||||
if (vma->vm_mm->context == mfsp(3)) {
|
||||
flush_user_cache_tlb(vma, start, end);
|
||||
preempt_enable();
|
||||
return;
|
||||
}
|
||||
|
||||
flush_cache_pages(vma, vma->vm_mm, vma->vm_start, vma->vm_end);
|
||||
preempt_enable();
|
||||
flush_cache_pages(vma, vma->vm_mm, start, end);
|
||||
}
|
||||
|
||||
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)) {
|
||||
flush_tlb_page(vma, vmaddr);
|
||||
flush_tlb_page(vma, vmaddr);
|
||||
if (likely(vma->vm_mm->context.space_id)) {
|
||||
__flush_cache_page(vma, vmaddr, PFN_PHYS(pfn));
|
||||
} else {
|
||||
__purge_cache_page(vma, vmaddr, PFN_PHYS(pfn));
|
||||
@ -633,6 +624,7 @@ void flush_kernel_vmap_range(void *vaddr, int size)
|
||||
{
|
||||
unsigned long start = (unsigned long)vaddr;
|
||||
unsigned long end = start + size;
|
||||
unsigned long flags, physaddr;
|
||||
|
||||
if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
|
||||
(unsigned long)size >= parisc_cache_flush_threshold) {
|
||||
@ -641,8 +633,14 @@ void flush_kernel_vmap_range(void *vaddr, int size)
|
||||
return;
|
||||
}
|
||||
|
||||
flush_kernel_dcache_range_asm(start, end);
|
||||
flush_tlb_kernel_range(start, end);
|
||||
while (start < end) {
|
||||
physaddr = lpa(start);
|
||||
purge_tlb_start(flags);
|
||||
pdtlb(SR_KERNEL, start);
|
||||
purge_tlb_end(flags);
|
||||
flush_dcache_page_asm(physaddr, start);
|
||||
start += PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(flush_kernel_vmap_range);
|
||||
|
||||
@ -650,6 +648,7 @@ void invalidate_kernel_vmap_range(void *vaddr, int size)
|
||||
{
|
||||
unsigned long start = (unsigned long)vaddr;
|
||||
unsigned long end = start + size;
|
||||
unsigned long flags, physaddr;
|
||||
|
||||
if ((!IS_ENABLED(CONFIG_SMP) || !arch_irqs_disabled()) &&
|
||||
(unsigned long)size >= parisc_cache_flush_threshold) {
|
||||
@ -658,7 +657,13 @@ void invalidate_kernel_vmap_range(void *vaddr, int size)
|
||||
return;
|
||||
}
|
||||
|
||||
purge_kernel_dcache_range_asm(start, end);
|
||||
flush_tlb_kernel_range(start, end);
|
||||
while (start < end) {
|
||||
physaddr = lpa(start);
|
||||
purge_tlb_start(flags);
|
||||
pdtlb(SR_KERNEL, start);
|
||||
purge_tlb_end(flags);
|
||||
purge_dcache_page_asm(physaddr, start);
|
||||
start += PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(invalidate_kernel_vmap_range);
|
||||
|
@ -1288,74 +1288,12 @@ nadtlb_check_alias_20:
|
||||
nadtlb_emulate:
|
||||
|
||||
/*
|
||||
* Non access misses can be caused by fdc,fic,pdc,lpa,probe and
|
||||
* probei instructions. We don't want to fault for these
|
||||
* instructions (not only does it not make sense, it can cause
|
||||
* deadlocks, since some flushes are done with the mmap
|
||||
* semaphore held). If the translation doesn't exist, we can't
|
||||
* insert a translation, so have to emulate the side effects
|
||||
* of the instruction. Since we don't insert a translation
|
||||
* we can get a lot of faults during a flush loop, so it makes
|
||||
* sense to try to do it here with minimum overhead. We only
|
||||
* emulate fdc,fic,pdc,probew,prober instructions whose base
|
||||
* and index registers are not shadowed. We defer everything
|
||||
* else to the "slow" path.
|
||||
* Non-access misses can be caused by fdc,fic,pdc,lpa,probe and
|
||||
* probei instructions. The kernel no longer faults doing flushes.
|
||||
* Use of lpa and probe instructions is rare. Given the issue
|
||||
* with shadow registers, we defer everything to the "slow" path.
|
||||
*/
|
||||
|
||||
mfctl %cr19,%r9 /* Get iir */
|
||||
|
||||
/* PA 2.0 Arch Ref. Book pg 382 has a good description of the insn bits.
|
||||
Checks for fdc,fdce,pdc,"fic,4f",prober,probeir,probew, probeiw */
|
||||
|
||||
/* Checks for fdc,fdce,pdc,"fic,4f" only */
|
||||
ldi 0x280,%r16
|
||||
and %r9,%r16,%r17
|
||||
cmpb,<>,n %r16,%r17,nadtlb_probe_check
|
||||
bb,>=,n %r9,26,nadtlb_nullify /* m bit not set, just nullify */
|
||||
BL get_register,%r25
|
||||
extrw,u %r9,15,5,%r8 /* Get index register # */
|
||||
cmpib,COND(=),n -1,%r1,nadtlb_fault /* have to use slow path */
|
||||
copy %r1,%r24
|
||||
BL get_register,%r25
|
||||
extrw,u %r9,10,5,%r8 /* Get base register # */
|
||||
cmpib,COND(=),n -1,%r1,nadtlb_fault /* have to use slow path */
|
||||
BL set_register,%r25
|
||||
add,l %r1,%r24,%r1 /* doesn't affect c/b bits */
|
||||
|
||||
nadtlb_nullify:
|
||||
mfctl %ipsw,%r8
|
||||
ldil L%PSW_N,%r9
|
||||
or %r8,%r9,%r8 /* Set PSW_N */
|
||||
mtctl %r8,%ipsw
|
||||
|
||||
rfir
|
||||
nop
|
||||
|
||||
/*
|
||||
When there is no translation for the probe address then we
|
||||
must nullify the insn and return zero in the target register.
|
||||
This will indicate to the calling code that it does not have
|
||||
write/read privileges to this address.
|
||||
|
||||
This should technically work for prober and probew in PA 1.1,
|
||||
and also probe,r and probe,w in PA 2.0
|
||||
|
||||
WARNING: USE ONLY NON-SHADOW REGISTERS WITH PROBE INSN!
|
||||
THE SLOW-PATH EMULATION HAS NOT BEEN WRITTEN YET.
|
||||
|
||||
*/
|
||||
nadtlb_probe_check:
|
||||
ldi 0x80,%r16
|
||||
and %r9,%r16,%r17
|
||||
cmpb,<>,n %r16,%r17,nadtlb_fault /* Must be probe,[rw]*/
|
||||
BL get_register,%r25 /* Find the target register */
|
||||
extrw,u %r9,31,5,%r8 /* Get target register */
|
||||
cmpib,COND(=),n -1,%r1,nadtlb_fault /* have to use slow path */
|
||||
BL set_register,%r25
|
||||
copy %r0,%r1 /* Write zero to target register */
|
||||
b nadtlb_nullify /* Nullify return insn */
|
||||
nop
|
||||
|
||||
b,n nadtlb_fault
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
itlb_miss_20w:
|
||||
|
@ -5,6 +5,7 @@
|
||||
* PA-RISC kprobes implementation
|
||||
*
|
||||
* Copyright (c) 2019 Sven Schnelle <svens@stackframe.org>
|
||||
* Copyright (c) 2022 Helge Deller <deller@gmx.de>
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
@ -25,9 +26,14 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p)
|
||||
if (!p->ainsn.insn)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(p->ainsn.insn, p->addr,
|
||||
MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
|
||||
/*
|
||||
* Set up new instructions. Second break instruction will
|
||||
* trigger call of parisc_kprobe_ss_handler().
|
||||
*/
|
||||
p->opcode = *p->addr;
|
||||
p->ainsn.insn[0] = p->opcode;
|
||||
p->ainsn.insn[1] = PARISC_KPROBES_BREAK_INSN2;
|
||||
|
||||
flush_insn_slot(p);
|
||||
return 0;
|
||||
}
|
||||
@ -73,9 +79,7 @@ static void __kprobes setup_singlestep(struct kprobe *p,
|
||||
{
|
||||
kcb->iaoq[0] = regs->iaoq[0];
|
||||
kcb->iaoq[1] = regs->iaoq[1];
|
||||
regs->iaoq[0] = (unsigned long)p->ainsn.insn;
|
||||
mtctl(0, 0);
|
||||
regs->gr[0] |= PSW_R;
|
||||
instruction_pointer_set(regs, (unsigned long)p->ainsn.insn);
|
||||
}
|
||||
|
||||
int __kprobes parisc_kprobe_break_handler(struct pt_regs *regs)
|
||||
@ -165,9 +169,8 @@ int __kprobes parisc_kprobe_ss_handler(struct pt_regs *regs)
|
||||
regs->iaoq[0] = kcb->iaoq[1];
|
||||
break;
|
||||
default:
|
||||
regs->iaoq[1] = kcb->iaoq[0];
|
||||
regs->iaoq[1] += (regs->iaoq[1] - regs->iaoq[0]) + 4;
|
||||
regs->iaoq[0] = kcb->iaoq[1];
|
||||
regs->iaoq[1] = regs->iaoq[0] + 4;
|
||||
break;
|
||||
}
|
||||
kcb->kprobe_status = KPROBE_HIT_SSDONE;
|
||||
@ -191,14 +194,17 @@ static struct kprobe trampoline_p = {
|
||||
static int __kprobes trampoline_probe_handler(struct kprobe *p,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
unsigned long orig_ret_address;
|
||||
|
||||
orig_ret_address = __kretprobe_trampoline_handler(regs, NULL);
|
||||
instruction_pointer_set(regs, orig_ret_address);
|
||||
__kretprobe_trampoline_handler(regs, NULL);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void arch_kretprobe_fixup_return(struct pt_regs *regs,
|
||||
kprobe_opcode_t *correct_ret_addr)
|
||||
{
|
||||
regs->gr[2] = (unsigned long)correct_ret_addr;
|
||||
}
|
||||
|
||||
void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
|
@ -91,7 +91,7 @@ static inline int map_pte_uncached(pte_t * pte,
|
||||
printk(KERN_ERR "map_pte_uncached: page already exists\n");
|
||||
purge_tlb_start(flags);
|
||||
set_pte(pte, __mk_pte(*paddr_ptr, PAGE_KERNEL_UNC));
|
||||
pdtlb_kernel(orig_vaddr);
|
||||
pdtlb(SR_KERNEL, orig_vaddr);
|
||||
purge_tlb_end(flags);
|
||||
vaddr += PAGE_SIZE;
|
||||
orig_vaddr += PAGE_SIZE;
|
||||
@ -175,7 +175,7 @@ static inline void unmap_uncached_pte(pmd_t * pmd, unsigned long vaddr,
|
||||
|
||||
pte_clear(&init_mm, vaddr, pte);
|
||||
purge_tlb_start(flags);
|
||||
pdtlb_kernel(orig_vaddr);
|
||||
pdtlb(SR_KERNEL, orig_vaddr);
|
||||
purge_tlb_end(flags);
|
||||
vaddr += PAGE_SIZE;
|
||||
orig_vaddr += PAGE_SIZE;
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -101,8 +101,8 @@ void __init store_cpu_topology(unsigned int cpuid)
|
||||
|
||||
update_siblings_masks(cpuid);
|
||||
|
||||
pr_info("CPU%u: thread %d, cpu %d, socket %d\n",
|
||||
cpuid, cpu_topology[cpuid].thread_id,
|
||||
pr_info("CPU%u: cpu core %d of socket %d\n",
|
||||
cpuid,
|
||||
cpu_topology[cpuid].core_id,
|
||||
cpu_topology[cpuid].socket_id);
|
||||
}
|
||||
|
@ -302,7 +302,10 @@ static void handle_break(struct pt_regs *regs)
|
||||
parisc_kprobe_break_handler(regs);
|
||||
return;
|
||||
}
|
||||
|
||||
if (unlikely(iir == PARISC_KPROBES_BREAK_INSN2)) {
|
||||
parisc_kprobe_ss_handler(regs);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KGDB
|
||||
@ -539,11 +542,6 @@ void notrace handle_interruption(int code, struct pt_regs *regs)
|
||||
/* Recovery counter trap */
|
||||
regs->gr[0] &= ~PSW_R;
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
if (parisc_kprobe_ss_handler(regs))
|
||||
return;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KGDB
|
||||
if (kgdb_single_step) {
|
||||
kgdb_handle_exception(0, SIGTRAP, 0, regs);
|
||||
@ -662,6 +660,8 @@ void notrace handle_interruption(int code, struct pt_regs *regs)
|
||||
by hand. Technically we need to emulate:
|
||||
fdc,fdce,pdc,"fic,4f",prober,probeir,probew, probeiw
|
||||
*/
|
||||
if (code == 17 && handle_nadtlb_fault(regs))
|
||||
return;
|
||||
fault_address = regs->ior;
|
||||
fault_space = regs->isr;
|
||||
break;
|
||||
|
@ -3,14 +3,11 @@
|
||||
* Unaligned memory access handler
|
||||
*
|
||||
* Copyright (C) 2001 Randolph Chung <tausq@debian.org>
|
||||
* Copyright (C) 2022 Helge Deller <deller@gmx.de>
|
||||
* Significantly tweaked by LaMont Jones <lamont@debian.org>
|
||||
*/
|
||||
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/sched/debug.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include <linux/uaccess.h>
|
||||
@ -25,18 +22,7 @@
|
||||
#define DPRINTF(fmt, args...)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
#define RFMT "%016lx"
|
||||
#else
|
||||
#define RFMT "%08lx"
|
||||
#endif
|
||||
|
||||
#define FIXUP_BRANCH(lbl) \
|
||||
"\tldil L%%" #lbl ", %%r1\n" \
|
||||
"\tldo R%%" #lbl "(%%r1), %%r1\n" \
|
||||
"\tbv,n %%r0(%%r1)\n"
|
||||
/* If you use FIXUP_BRANCH, then you must list this clobber */
|
||||
#define FIXUP_BRANCH_CLOBBER "r1"
|
||||
#define RFMT "%#08lx"
|
||||
|
||||
/* 1111 1100 0000 0000 0001 0011 1100 0000 */
|
||||
#define OPCODE1(a,b,c) ((a)<<26|(b)<<12|(c)<<6)
|
||||
@ -114,37 +100,30 @@
|
||||
#define IM14(i) IM((i),14)
|
||||
|
||||
#define ERR_NOTHANDLED -1
|
||||
#define ERR_PAGEFAULT -2
|
||||
|
||||
int unaligned_enabled __read_mostly = 1;
|
||||
|
||||
static int emulate_ldh(struct pt_regs *regs, int toreg)
|
||||
{
|
||||
unsigned long saddr = regs->ior;
|
||||
unsigned long val = 0;
|
||||
int ret;
|
||||
unsigned long val = 0, temp1;
|
||||
ASM_EXCEPTIONTABLE_VAR(ret);
|
||||
|
||||
DPRINTF("load " RFMT ":" RFMT " to r%d for 2 bytes\n",
|
||||
regs->isr, regs->ior, toreg);
|
||||
|
||||
__asm__ __volatile__ (
|
||||
" mtsp %4, %%sr1\n"
|
||||
"1: ldbs 0(%%sr1,%3), %%r20\n"
|
||||
"1: ldbs 0(%%sr1,%3), %2\n"
|
||||
"2: ldbs 1(%%sr1,%3), %0\n"
|
||||
" depw %%r20, 23, 24, %0\n"
|
||||
" copy %%r0, %1\n"
|
||||
" depw %2, 23, 24, %0\n"
|
||||
"3: \n"
|
||||
" .section .fixup,\"ax\"\n"
|
||||
"4: ldi -2, %1\n"
|
||||
FIXUP_BRANCH(3b)
|
||||
" .previous\n"
|
||||
ASM_EXCEPTIONTABLE_ENTRY(1b, 4b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY(2b, 4b)
|
||||
: "=r" (val), "=r" (ret)
|
||||
: "0" (val), "r" (saddr), "r" (regs->isr)
|
||||
: "r20", FIXUP_BRANCH_CLOBBER );
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b)
|
||||
: "+r" (val), "+r" (ret), "=&r" (temp1)
|
||||
: "r" (saddr), "r" (regs->isr) );
|
||||
|
||||
DPRINTF("val = 0x" RFMT "\n", val);
|
||||
DPRINTF("val = " RFMT "\n", val);
|
||||
|
||||
if (toreg)
|
||||
regs->gr[toreg] = val;
|
||||
@ -155,34 +134,28 @@ static int emulate_ldh(struct pt_regs *regs, int toreg)
|
||||
static int emulate_ldw(struct pt_regs *regs, int toreg, int flop)
|
||||
{
|
||||
unsigned long saddr = regs->ior;
|
||||
unsigned long val = 0;
|
||||
int ret;
|
||||
unsigned long val = 0, temp1, temp2;
|
||||
ASM_EXCEPTIONTABLE_VAR(ret);
|
||||
|
||||
DPRINTF("load " RFMT ":" RFMT " to r%d for 4 bytes\n",
|
||||
regs->isr, regs->ior, toreg);
|
||||
|
||||
__asm__ __volatile__ (
|
||||
" zdep %3,28,2,%%r19\n" /* r19=(ofs&3)*8 */
|
||||
" mtsp %4, %%sr1\n"
|
||||
" depw %%r0,31,2,%3\n"
|
||||
"1: ldw 0(%%sr1,%3),%0\n"
|
||||
"2: ldw 4(%%sr1,%3),%%r20\n"
|
||||
" subi 32,%%r19,%%r19\n"
|
||||
" mtctl %%r19,11\n"
|
||||
" vshd %0,%%r20,%0\n"
|
||||
" copy %%r0, %1\n"
|
||||
" zdep %4,28,2,%2\n" /* r19=(ofs&3)*8 */
|
||||
" mtsp %5, %%sr1\n"
|
||||
" depw %%r0,31,2,%4\n"
|
||||
"1: ldw 0(%%sr1,%4),%0\n"
|
||||
"2: ldw 4(%%sr1,%4),%3\n"
|
||||
" subi 32,%4,%2\n"
|
||||
" mtctl %2,11\n"
|
||||
" vshd %0,%3,%0\n"
|
||||
"3: \n"
|
||||
" .section .fixup,\"ax\"\n"
|
||||
"4: ldi -2, %1\n"
|
||||
FIXUP_BRANCH(3b)
|
||||
" .previous\n"
|
||||
ASM_EXCEPTIONTABLE_ENTRY(1b, 4b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY(2b, 4b)
|
||||
: "=r" (val), "=r" (ret)
|
||||
: "0" (val), "r" (saddr), "r" (regs->isr)
|
||||
: "r19", "r20", FIXUP_BRANCH_CLOBBER );
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b)
|
||||
: "+r" (val), "+r" (ret), "=&r" (temp1), "=&r" (temp2)
|
||||
: "r" (saddr), "r" (regs->isr) );
|
||||
|
||||
DPRINTF("val = 0x" RFMT "\n", val);
|
||||
DPRINTF("val = " RFMT "\n", val);
|
||||
|
||||
if (flop)
|
||||
((__u32*)(regs->fr))[toreg] = val;
|
||||
@ -195,16 +168,15 @@ static int emulate_ldd(struct pt_regs *regs, int toreg, int flop)
|
||||
{
|
||||
unsigned long saddr = regs->ior;
|
||||
__u64 val = 0;
|
||||
int ret;
|
||||
ASM_EXCEPTIONTABLE_VAR(ret);
|
||||
|
||||
DPRINTF("load " RFMT ":" RFMT " to r%d for 8 bytes\n",
|
||||
regs->isr, regs->ior, toreg);
|
||||
#ifdef CONFIG_PA20
|
||||
|
||||
#ifndef CONFIG_64BIT
|
||||
if (!flop)
|
||||
return -1;
|
||||
#endif
|
||||
if (!IS_ENABLED(CONFIG_64BIT) && !flop)
|
||||
return ERR_NOTHANDLED;
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
__asm__ __volatile__ (
|
||||
" depd,z %3,60,3,%%r19\n" /* r19=(ofs&7)*8 */
|
||||
" mtsp %4, %%sr1\n"
|
||||
@ -214,44 +186,32 @@ static int emulate_ldd(struct pt_regs *regs, int toreg, int flop)
|
||||
" subi 64,%%r19,%%r19\n"
|
||||
" mtsar %%r19\n"
|
||||
" shrpd %0,%%r20,%%sar,%0\n"
|
||||
" copy %%r0, %1\n"
|
||||
"3: \n"
|
||||
" .section .fixup,\"ax\"\n"
|
||||
"4: ldi -2, %1\n"
|
||||
FIXUP_BRANCH(3b)
|
||||
" .previous\n"
|
||||
ASM_EXCEPTIONTABLE_ENTRY(1b,4b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY(2b,4b)
|
||||
: "=r" (val), "=r" (ret)
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b)
|
||||
: "=r" (val), "+r" (ret)
|
||||
: "0" (val), "r" (saddr), "r" (regs->isr)
|
||||
: "r19", "r20", FIXUP_BRANCH_CLOBBER );
|
||||
: "r19", "r20" );
|
||||
#else
|
||||
{
|
||||
unsigned long valh=0,vall=0;
|
||||
unsigned long shift, temp1;
|
||||
__asm__ __volatile__ (
|
||||
" zdep %5,29,2,%%r19\n" /* r19=(ofs&3)*8 */
|
||||
" mtsp %6, %%sr1\n"
|
||||
" dep %%r0,31,2,%5\n"
|
||||
"1: ldw 0(%%sr1,%5),%0\n"
|
||||
"2: ldw 4(%%sr1,%5),%1\n"
|
||||
"3: ldw 8(%%sr1,%5),%%r20\n"
|
||||
" subi 32,%%r19,%%r19\n"
|
||||
" mtsar %%r19\n"
|
||||
" vshd %0,%1,%0\n"
|
||||
" vshd %1,%%r20,%1\n"
|
||||
" copy %%r0, %2\n"
|
||||
" zdep %2,29,2,%3\n" /* r19=(ofs&3)*8 */
|
||||
" mtsp %5, %%sr1\n"
|
||||
" dep %%r0,31,2,%2\n"
|
||||
"1: ldw 0(%%sr1,%2),%0\n"
|
||||
"2: ldw 4(%%sr1,%2),%R0\n"
|
||||
"3: ldw 8(%%sr1,%2),%4\n"
|
||||
" subi 32,%3,%3\n"
|
||||
" mtsar %3\n"
|
||||
" vshd %0,%R0,%0\n"
|
||||
" vshd %R0,%4,%R0\n"
|
||||
"4: \n"
|
||||
" .section .fixup,\"ax\"\n"
|
||||
"5: ldi -2, %2\n"
|
||||
FIXUP_BRANCH(4b)
|
||||
" .previous\n"
|
||||
ASM_EXCEPTIONTABLE_ENTRY(1b,5b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY(2b,5b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY(3b,5b)
|
||||
: "=r" (valh), "=r" (vall), "=r" (ret)
|
||||
: "0" (valh), "1" (vall), "r" (saddr), "r" (regs->isr)
|
||||
: "r19", "r20", FIXUP_BRANCH_CLOBBER );
|
||||
val=((__u64)valh<<32)|(__u64)vall;
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 4b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 4b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(3b, 4b)
|
||||
: "+r" (val), "+r" (ret), "+r" (saddr), "=&r" (shift), "=&r" (temp1)
|
||||
: "r" (regs->isr) );
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -267,31 +227,25 @@ static int emulate_ldd(struct pt_regs *regs, int toreg, int flop)
|
||||
|
||||
static int emulate_sth(struct pt_regs *regs, int frreg)
|
||||
{
|
||||
unsigned long val = regs->gr[frreg];
|
||||
int ret;
|
||||
unsigned long val = regs->gr[frreg], temp1;
|
||||
ASM_EXCEPTIONTABLE_VAR(ret);
|
||||
|
||||
if (!frreg)
|
||||
val = 0;
|
||||
|
||||
DPRINTF("store r%d (0x" RFMT ") to " RFMT ":" RFMT " for 2 bytes\n", frreg,
|
||||
DPRINTF("store r%d (" RFMT ") to " RFMT ":" RFMT " for 2 bytes\n", frreg,
|
||||
val, regs->isr, regs->ior);
|
||||
|
||||
__asm__ __volatile__ (
|
||||
" mtsp %3, %%sr1\n"
|
||||
" extrw,u %1, 23, 8, %%r19\n"
|
||||
"1: stb %1, 1(%%sr1, %2)\n"
|
||||
"2: stb %%r19, 0(%%sr1, %2)\n"
|
||||
" copy %%r0, %0\n"
|
||||
" mtsp %4, %%sr1\n"
|
||||
" extrw,u %2, 23, 8, %1\n"
|
||||
"1: stb %1, 0(%%sr1, %3)\n"
|
||||
"2: stb %2, 1(%%sr1, %3)\n"
|
||||
"3: \n"
|
||||
" .section .fixup,\"ax\"\n"
|
||||
"4: ldi -2, %0\n"
|
||||
FIXUP_BRANCH(3b)
|
||||
" .previous\n"
|
||||
ASM_EXCEPTIONTABLE_ENTRY(1b,4b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY(2b,4b)
|
||||
: "=r" (ret)
|
||||
: "r" (val), "r" (regs->ior), "r" (regs->isr)
|
||||
: "r19", FIXUP_BRANCH_CLOBBER );
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b)
|
||||
: "+r" (ret), "=&r" (temp1)
|
||||
: "r" (val), "r" (regs->ior), "r" (regs->isr) );
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -299,7 +253,7 @@ static int emulate_sth(struct pt_regs *regs, int frreg)
|
||||
static int emulate_stw(struct pt_regs *regs, int frreg, int flop)
|
||||
{
|
||||
unsigned long val;
|
||||
int ret;
|
||||
ASM_EXCEPTIONTABLE_VAR(ret);
|
||||
|
||||
if (flop)
|
||||
val = ((__u32*)(regs->fr))[frreg];
|
||||
@ -308,7 +262,7 @@ static int emulate_stw(struct pt_regs *regs, int frreg, int flop)
|
||||
else
|
||||
val = 0;
|
||||
|
||||
DPRINTF("store r%d (0x" RFMT ") to " RFMT ":" RFMT " for 4 bytes\n", frreg,
|
||||
DPRINTF("store r%d (" RFMT ") to " RFMT ":" RFMT " for 4 bytes\n", frreg,
|
||||
val, regs->isr, regs->ior);
|
||||
|
||||
|
||||
@ -328,24 +282,19 @@ static int emulate_stw(struct pt_regs *regs, int frreg, int flop)
|
||||
" or %%r1, %%r21, %%r21\n"
|
||||
" stw %%r20,0(%%sr1,%2)\n"
|
||||
" stw %%r21,4(%%sr1,%2)\n"
|
||||
" copy %%r0, %0\n"
|
||||
"3: \n"
|
||||
" .section .fixup,\"ax\"\n"
|
||||
"4: ldi -2, %0\n"
|
||||
FIXUP_BRANCH(3b)
|
||||
" .previous\n"
|
||||
ASM_EXCEPTIONTABLE_ENTRY(1b,4b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY(2b,4b)
|
||||
: "=r" (ret)
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 3b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 3b)
|
||||
: "+r" (ret)
|
||||
: "r" (val), "r" (regs->ior), "r" (regs->isr)
|
||||
: "r19", "r20", "r21", "r22", "r1", FIXUP_BRANCH_CLOBBER );
|
||||
: "r19", "r20", "r21", "r22", "r1" );
|
||||
|
||||
return ret;
|
||||
}
|
||||
static int emulate_std(struct pt_regs *regs, int frreg, int flop)
|
||||
{
|
||||
__u64 val;
|
||||
int ret;
|
||||
ASM_EXCEPTIONTABLE_VAR(ret);
|
||||
|
||||
if (flop)
|
||||
val = regs->fr[frreg];
|
||||
@ -357,11 +306,10 @@ static int emulate_std(struct pt_regs *regs, int frreg, int flop)
|
||||
DPRINTF("store r%d (0x%016llx) to " RFMT ":" RFMT " for 8 bytes\n", frreg,
|
||||
val, regs->isr, regs->ior);
|
||||
|
||||
#ifdef CONFIG_PA20
|
||||
#ifndef CONFIG_64BIT
|
||||
if (!flop)
|
||||
return -1;
|
||||
#endif
|
||||
if (!IS_ENABLED(CONFIG_64BIT) && !flop)
|
||||
return ERR_NOTHANDLED;
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
__asm__ __volatile__ (
|
||||
" mtsp %3, %%sr1\n"
|
||||
" depd,z %2, 60, 3, %%r19\n"
|
||||
@ -378,19 +326,14 @@ static int emulate_std(struct pt_regs *regs, int frreg, int flop)
|
||||
" or %%r1, %%r21, %%r21\n"
|
||||
"3: std %%r20,0(%%sr1,%2)\n"
|
||||
"4: std %%r21,8(%%sr1,%2)\n"
|
||||
" copy %%r0, %0\n"
|
||||
"5: \n"
|
||||
" .section .fixup,\"ax\"\n"
|
||||
"6: ldi -2, %0\n"
|
||||
FIXUP_BRANCH(5b)
|
||||
" .previous\n"
|
||||
ASM_EXCEPTIONTABLE_ENTRY(1b,6b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY(2b,6b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY(3b,6b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY(4b,6b)
|
||||
: "=r" (ret)
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 5b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 5b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(3b, 5b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(4b, 5b)
|
||||
: "+r" (ret)
|
||||
: "r" (val), "r" (regs->ior), "r" (regs->isr)
|
||||
: "r19", "r20", "r21", "r22", "r1", FIXUP_BRANCH_CLOBBER );
|
||||
: "r19", "r20", "r21", "r22", "r1" );
|
||||
#else
|
||||
{
|
||||
unsigned long valh=(val>>32),vall=(val&0xffffffffl);
|
||||
@ -412,20 +355,15 @@ static int emulate_std(struct pt_regs *regs, int frreg, int flop)
|
||||
"3: stw %1,0(%%sr1,%3)\n"
|
||||
"4: stw %%r1,4(%%sr1,%3)\n"
|
||||
"5: stw %2,8(%%sr1,%3)\n"
|
||||
" copy %%r0, %0\n"
|
||||
"6: \n"
|
||||
" .section .fixup,\"ax\"\n"
|
||||
"7: ldi -2, %0\n"
|
||||
FIXUP_BRANCH(6b)
|
||||
" .previous\n"
|
||||
ASM_EXCEPTIONTABLE_ENTRY(1b,7b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY(2b,7b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY(3b,7b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY(4b,7b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY(5b,7b)
|
||||
: "=r" (ret)
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(1b, 6b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(2b, 6b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(3b, 6b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(4b, 6b)
|
||||
ASM_EXCEPTIONTABLE_ENTRY_EFAULT(5b, 6b)
|
||||
: "+r" (ret)
|
||||
: "r" (valh), "r" (vall), "r" (regs->ior), "r" (regs->isr)
|
||||
: "r19", "r20", "r21", "r1", FIXUP_BRANCH_CLOBBER );
|
||||
: "r19", "r20", "r21", "r1" );
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -438,7 +376,6 @@ void handle_unaligned(struct pt_regs *regs)
|
||||
unsigned long newbase = R1(regs->iir)?regs->gr[R1(regs->iir)]:0;
|
||||
int modify = 0;
|
||||
int ret = ERR_NOTHANDLED;
|
||||
register int flop=0; /* true if this is a flop */
|
||||
|
||||
__inc_irq_stat(irq_unaligned_count);
|
||||
|
||||
@ -450,10 +387,10 @@ void handle_unaligned(struct pt_regs *regs)
|
||||
|
||||
if (!(current->thread.flags & PARISC_UAC_NOPRINT) &&
|
||||
__ratelimit(&ratelimit)) {
|
||||
char buf[256];
|
||||
sprintf(buf, "%s(%d): unaligned access to 0x" RFMT " at ip=0x" RFMT "\n",
|
||||
current->comm, task_pid_nr(current), regs->ior, regs->iaoq[0]);
|
||||
printk(KERN_WARNING "%s", buf);
|
||||
printk(KERN_WARNING "%s(%d): unaligned access to " RFMT
|
||||
" at ip " RFMT " (iir " RFMT ")\n",
|
||||
current->comm, task_pid_nr(current), regs->ior,
|
||||
regs->iaoq[0], regs->iir);
|
||||
#ifdef DEBUG_UNALIGNED
|
||||
show_regs(regs);
|
||||
#endif
|
||||
@ -547,7 +484,7 @@ void handle_unaligned(struct pt_regs *regs)
|
||||
ret = emulate_stw(regs, R2(regs->iir),0);
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_PA20
|
||||
#ifdef CONFIG_64BIT
|
||||
case OPCODE_LDD_I:
|
||||
case OPCODE_LDDA_I:
|
||||
case OPCODE_LDD_S:
|
||||
@ -565,13 +502,11 @@ void handle_unaligned(struct pt_regs *regs)
|
||||
case OPCODE_FLDWS:
|
||||
case OPCODE_FLDWXR:
|
||||
case OPCODE_FLDWSR:
|
||||
flop=1;
|
||||
ret = emulate_ldw(regs,FR3(regs->iir),1);
|
||||
break;
|
||||
|
||||
case OPCODE_FLDDX:
|
||||
case OPCODE_FLDDS:
|
||||
flop=1;
|
||||
ret = emulate_ldd(regs,R3(regs->iir),1);
|
||||
break;
|
||||
|
||||
@ -579,13 +514,11 @@ void handle_unaligned(struct pt_regs *regs)
|
||||
case OPCODE_FSTWS:
|
||||
case OPCODE_FSTWXR:
|
||||
case OPCODE_FSTWSR:
|
||||
flop=1;
|
||||
ret = emulate_stw(regs,FR3(regs->iir),1);
|
||||
break;
|
||||
|
||||
case OPCODE_FSTDX:
|
||||
case OPCODE_FSTDS:
|
||||
flop=1;
|
||||
ret = emulate_std(regs,R3(regs->iir),1);
|
||||
break;
|
||||
|
||||
@ -599,14 +532,12 @@ void handle_unaligned(struct pt_regs *regs)
|
||||
switch (regs->iir & OPCODE2_MASK)
|
||||
{
|
||||
case OPCODE_FLDD_L:
|
||||
flop=1;
|
||||
ret = emulate_ldd(regs,R2(regs->iir),1);
|
||||
break;
|
||||
case OPCODE_FSTD_L:
|
||||
flop=1;
|
||||
ret = emulate_std(regs, R2(regs->iir),1);
|
||||
break;
|
||||
#ifdef CONFIG_PA20
|
||||
#ifdef CONFIG_64BIT
|
||||
case OPCODE_LDD_L:
|
||||
ret = emulate_ldd(regs, R2(regs->iir),0);
|
||||
break;
|
||||
@ -618,7 +549,6 @@ void handle_unaligned(struct pt_regs *regs)
|
||||
switch (regs->iir & OPCODE3_MASK)
|
||||
{
|
||||
case OPCODE_FLDW_L:
|
||||
flop=1;
|
||||
ret = emulate_ldw(regs, R2(regs->iir), 1);
|
||||
break;
|
||||
case OPCODE_LDW_M:
|
||||
@ -626,7 +556,6 @@ void handle_unaligned(struct pt_regs *regs)
|
||||
break;
|
||||
|
||||
case OPCODE_FSTW_L:
|
||||
flop=1;
|
||||
ret = emulate_stw(regs, R2(regs->iir),1);
|
||||
break;
|
||||
case OPCODE_STW_M:
|
||||
@ -673,7 +602,7 @@ void handle_unaligned(struct pt_regs *regs)
|
||||
printk(KERN_CRIT "Unaligned handler failed, ret = %d\n", ret);
|
||||
die_if_kernel("Unaligned data reference", regs, 28);
|
||||
|
||||
if (ret == ERR_PAGEFAULT)
|
||||
if (ret == -EFAULT)
|
||||
{
|
||||
force_sig_fault(SIGSEGV, SEGV_MAPERR,
|
||||
(void __user *)regs->ior);
|
||||
|
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'
|
26
arch/parisc/kernel/vdso32/note.S
Normal file
26
arch/parisc/kernel/vdso32/note.S
Normal file
@ -0,0 +1,26 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* This supplies .note.* sections to go into the PT_NOTE inside the vDSO text.
|
||||
* Here we can supply some information useful to userland.
|
||||
*/
|
||||
|
||||
#include <linux/uts.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#define ASM_ELF_NOTE_BEGIN(name, flags, vendor, type) \
|
||||
.section name, flags; \
|
||||
.balign 4; \
|
||||
.long 1f - 0f; /* name length */ \
|
||||
.long 3f - 2f; /* data length */ \
|
||||
.long type; /* note type */ \
|
||||
0: .asciz vendor; /* vendor name */ \
|
||||
1: .balign 4; \
|
||||
2:
|
||||
|
||||
#define ASM_ELF_NOTE_END \
|
||||
3: .balign 4; /* pad out section */ \
|
||||
.previous
|
||||
|
||||
ASM_ELF_NOTE_BEGIN(".note.kernel-version", "a", UTS_SYSNAME, 0)
|
||||
.long LINUX_VERSION_CODE
|
||||
ASM_ELF_NOTE_END
|
32
arch/parisc/kernel/vdso32/restart_syscall.S
Normal file
32
arch/parisc/kernel/vdso32/restart_syscall.S
Normal file
@ -0,0 +1,32 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Syscall restart trampoline for 32 and 64 bits processes.
|
||||
*
|
||||
* Copyright (C) 2018-2022 Helge Deller <deller@gmx.de>
|
||||
* Copyright (C) 2022 John David Anglin <dave.anglin@bell.net>
|
||||
*/
|
||||
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/vdso.h>
|
||||
|
||||
#include <linux/linkage.h>
|
||||
|
||||
.text
|
||||
|
||||
ENTRY_CFI(__kernel_restart_syscall)
|
||||
/*
|
||||
* Setup a trampoline to restart the syscall
|
||||
* with __NR_restart_syscall
|
||||
*/
|
||||
|
||||
/* load return pointer */
|
||||
#if defined(__VDSO64__)
|
||||
ldd 0(%sp), %r31
|
||||
#elif defined(__VDSO32__)
|
||||
ldw 0(%sp), %r31
|
||||
#endif
|
||||
|
||||
be 0x100(%sr2, %r0)
|
||||
ldi __NR_restart_syscall, %r20
|
||||
|
||||
ENDPROC_CFI(__kernel_restart_syscall)
|
195
arch/parisc/kernel/vdso32/sigtramp.S
Normal file
195
arch/parisc/kernel/vdso32/sigtramp.S
Normal file
@ -0,0 +1,195 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Signal trampolines for 32 bit processes.
|
||||
*
|
||||
* Copyright (C) 2006 Randolph Chung <tausq@debian.org>
|
||||
* Copyright (C) 2018-2022 Helge Deller <deller@gmx.de>
|
||||
* Copyright (C) 2022 John David Anglin <dave.anglin@bell.net>
|
||||
*/
|
||||
#include <asm/unistd.h>
|
||||
#include <linux/linkage.h>
|
||||
#include <generated/asm-offsets.h>
|
||||
|
||||
.text
|
||||
|
||||
/* Gdb expects the trampoline is on the stack and the pc is offset from
|
||||
a 64-byte boundary by 0, 4 or 5 instructions. Since the vdso trampoline
|
||||
is not on the stack, we need a new variant with different offsets and
|
||||
data to tell gdb where to find the signal context on the stack.
|
||||
|
||||
Here we put the offset to the context data at the start of the trampoline
|
||||
region and offset the first trampoline by 2 instructions. Please do
|
||||
not change the trampoline as the code in gdb depends on the following
|
||||
instruction sequence exactly.
|
||||
*/
|
||||
.align 64
|
||||
.word SIGFRAME_CONTEXT_REGS32
|
||||
|
||||
/* The nop here is a hack. The dwarf2 unwind routines subtract 1 from
|
||||
the return address to get an address in the middle of the presumed
|
||||
call instruction. Since we don't have a call here, we artifically
|
||||
extend the range covered by the unwind info by adding a nop before
|
||||
the real start.
|
||||
*/
|
||||
nop
|
||||
|
||||
.globl __kernel_sigtramp_rt
|
||||
.type __kernel_sigtramp_rt, @function
|
||||
__kernel_sigtramp_rt:
|
||||
.proc
|
||||
.callinfo FRAME=ASM_SIGFRAME_SIZE32,CALLS,SAVE_RP
|
||||
.entry
|
||||
|
||||
.Lsigrt_start = . - 4
|
||||
0: ldi 0, %r25 /* (in_syscall=0) */
|
||||
ldi __NR_rt_sigreturn, %r20
|
||||
ble 0x100(%sr2, %r0)
|
||||
nop
|
||||
|
||||
1: ldi 1, %r25 /* (in_syscall=1) */
|
||||
ldi __NR_rt_sigreturn, %r20
|
||||
ble 0x100(%sr2, %r0)
|
||||
nop
|
||||
.Lsigrt_end:
|
||||
.exit
|
||||
.procend
|
||||
.size __kernel_sigtramp_rt,.-__kernel_sigtramp_rt
|
||||
|
||||
|
||||
.section .eh_frame,"a",@progbits
|
||||
|
||||
/* This is where the mcontext_t struct can be found on the stack. */
|
||||
#define PTREGS SIGFRAME_CONTEXT_REGS32 /* 32-bit process offset is -672 */
|
||||
|
||||
/* Register REGNO can be found at offset OFS of the mcontext_t structure. */
|
||||
.macro rsave regno,ofs
|
||||
.byte 0x05 /* DW_CFA_offset_extended */
|
||||
.uleb128 \regno; /* regno */
|
||||
.uleb128 \ofs /* factored offset */
|
||||
.endm
|
||||
|
||||
.Lcie:
|
||||
.long .Lcie_end - .Lcie_start
|
||||
.Lcie_start:
|
||||
.long 0 /* CIE ID */
|
||||
.byte 1 /* Version number */
|
||||
.stringz "zRS" /* NUL-terminated augmentation string */
|
||||
.uleb128 4 /* Code alignment factor */
|
||||
.sleb128 4 /* Data alignment factor */
|
||||
.byte 89 /* Return address register column, iaoq[0] */
|
||||
.uleb128 1 /* Augmentation value length */
|
||||
.byte 0x1b /* DW_EH_PE_pcrel | DW_EH_PE_sdata4. */
|
||||
.byte 0x0f /* DW_CFA_def_cfa_expresion */
|
||||
.uleb128 9f - 1f /* length */
|
||||
1:
|
||||
.byte 0x8e /* DW_OP_breg30 */
|
||||
.sleb128 PTREGS
|
||||
9:
|
||||
.balign 4
|
||||
.Lcie_end:
|
||||
|
||||
.long .Lfde0_end - .Lfde0_start
|
||||
.Lfde0_start:
|
||||
.long .Lfde0_start - .Lcie /* CIE pointer. */
|
||||
.long .Lsigrt_start - . /* PC start, length */
|
||||
.long .Lsigrt_end - .Lsigrt_start
|
||||
.uleb128 0 /* Augmentation */
|
||||
|
||||
/* General registers */
|
||||
rsave 1, 2
|
||||
rsave 2, 3
|
||||
rsave 3, 4
|
||||
rsave 4, 5
|
||||
rsave 5, 6
|
||||
rsave 6, 7
|
||||
rsave 7, 8
|
||||
rsave 8, 9
|
||||
rsave 9, 10
|
||||
rsave 10, 11
|
||||
rsave 11, 12
|
||||
rsave 12, 13
|
||||
rsave 13, 14
|
||||
rsave 14, 15
|
||||
rsave 15, 16
|
||||
rsave 16, 17
|
||||
rsave 17, 18
|
||||
rsave 18, 19
|
||||
rsave 19, 20
|
||||
rsave 20, 21
|
||||
rsave 21, 22
|
||||
rsave 22, 23
|
||||
rsave 23, 24
|
||||
rsave 24, 25
|
||||
rsave 25, 26
|
||||
rsave 26, 27
|
||||
rsave 27, 28
|
||||
rsave 28, 29
|
||||
rsave 29, 30
|
||||
rsave 30, 31
|
||||
rsave 31, 32
|
||||
|
||||
/* Floating-point registers */
|
||||
rsave 32, 42
|
||||
rsave 33, 43
|
||||
rsave 34, 44
|
||||
rsave 35, 45
|
||||
rsave 36, 46
|
||||
rsave 37, 47
|
||||
rsave 38, 48
|
||||
rsave 39, 49
|
||||
rsave 40, 50
|
||||
rsave 41, 51
|
||||
rsave 42, 52
|
||||
rsave 43, 53
|
||||
rsave 44, 54
|
||||
rsave 45, 55
|
||||
rsave 46, 56
|
||||
rsave 47, 57
|
||||
rsave 48, 58
|
||||
rsave 49, 59
|
||||
rsave 50, 60
|
||||
rsave 51, 61
|
||||
rsave 52, 62
|
||||
rsave 53, 63
|
||||
rsave 54, 64
|
||||
rsave 55, 65
|
||||
rsave 56, 66
|
||||
rsave 57, 67
|
||||
rsave 58, 68
|
||||
rsave 59, 69
|
||||
rsave 60, 70
|
||||
rsave 61, 71
|
||||
rsave 62, 72
|
||||
rsave 63, 73
|
||||
rsave 64, 74
|
||||
rsave 65, 75
|
||||
rsave 66, 76
|
||||
rsave 67, 77
|
||||
rsave 68, 78
|
||||
rsave 69, 79
|
||||
rsave 70, 80
|
||||
rsave 71, 81
|
||||
rsave 72, 82
|
||||
rsave 73, 83
|
||||
rsave 74, 84
|
||||
rsave 75, 85
|
||||
rsave 76, 86
|
||||
rsave 77, 87
|
||||
rsave 78, 88
|
||||
rsave 79, 89
|
||||
rsave 80, 90
|
||||
rsave 81, 91
|
||||
rsave 82, 92
|
||||
rsave 83, 93
|
||||
rsave 84, 94
|
||||
rsave 85, 95
|
||||
rsave 86, 96
|
||||
rsave 87, 97
|
||||
|
||||
/* SAR register */
|
||||
rsave 88, 102
|
||||
|
||||
/* iaoq[0] return address register */
|
||||
rsave 89, 100
|
||||
.balign 4
|
||||
.Lfde0_end:
|
111
arch/parisc/kernel/vdso32/vdso32.lds.S
Normal file
111
arch/parisc/kernel/vdso32/vdso32.lds.S
Normal file
@ -0,0 +1,111 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* This is the infamous ld script for the 32 bits vdso library
|
||||
*/
|
||||
#include <asm/vdso.h>
|
||||
#include <asm/page.h>
|
||||
|
||||
/* Default link addresses for the vDSOs */
|
||||
OUTPUT_FORMAT("elf32-hppa-linux")
|
||||
OUTPUT_ARCH(hppa)
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = VDSO_LBASE + SIZEOF_HEADERS;
|
||||
.hash : { *(.hash) } :text
|
||||
.gnu.hash : { *(.gnu.hash) }
|
||||
.dynsym : { *(.dynsym) }
|
||||
.dynstr : { *(.dynstr) }
|
||||
.gnu.version : { *(.gnu.version) }
|
||||
.gnu.version_d : { *(.gnu.version_d) }
|
||||
.gnu.version_r : { *(.gnu.version_r) }
|
||||
|
||||
.note : { *(.note.*) } :text :note
|
||||
|
||||
. = ALIGN (16);
|
||||
.text :
|
||||
{
|
||||
*(.text .stub .text.* .gnu.linkonce.t.*)
|
||||
}
|
||||
PROVIDE (__etext = .);
|
||||
PROVIDE (_etext = .);
|
||||
PROVIDE (etext = .);
|
||||
|
||||
/* Other stuff is appended to the text segment: */
|
||||
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
|
||||
.rodata1 : { *(.rodata1) }
|
||||
.rodata2 : { *(.data.rel.ro) }
|
||||
|
||||
.eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr
|
||||
.eh_frame : { KEEP (*(.eh_frame)) } :text
|
||||
.gcc_except_table : { *(.gcc_except_table) }
|
||||
.fixup : { *(.fixup) }
|
||||
|
||||
.dynamic : { *(.dynamic) } :text :dynamic
|
||||
.plt : { *(.plt) }
|
||||
.got : { *(.got) }
|
||||
|
||||
_end = .;
|
||||
__end = .;
|
||||
PROVIDE (end = .);
|
||||
|
||||
|
||||
/* Stabs debugging sections are here too
|
||||
*/
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
.comment 0 : { *(.comment) }
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
|
||||
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
|
||||
.debug_weaknames 0 : { *(.debug_weaknames) }
|
||||
.debug_funcnames 0 : { *(.debug_funcnames) }
|
||||
.debug_typenames 0 : { *(.debug_typenames) }
|
||||
.debug_varnames 0 : { *(.debug_varnames) }
|
||||
|
||||
/DISCARD/ : { *(.note.GNU-stack) }
|
||||
/DISCARD/ : { *(.data .data.* .gnu.linkonce.d.* .sdata*) }
|
||||
/DISCARD/ : { *(.bss .sbss .dynbss .dynsbss) }
|
||||
}
|
||||
|
||||
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD FILEHDR PHDRS FLAGS(5); /* PF_R|PF_X */
|
||||
note PT_NOTE FLAGS(4); /* PF_R */
|
||||
dynamic PT_DYNAMIC FLAGS(4); /* PF_R */
|
||||
eh_frame_hdr PT_GNU_EH_FRAME;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This controls what symbols we export from the DSO.
|
||||
*/
|
||||
VERSION
|
||||
{
|
||||
VDSO_VERSION_STRING {
|
||||
global:
|
||||
__kernel_sigtramp_rt32;
|
||||
__kernel_restart_syscall32;
|
||||
local: *;
|
||||
};
|
||||
}
|
14
arch/parisc/kernel/vdso32/vdso32_wrapper.S
Normal file
14
arch/parisc/kernel/vdso32/vdso32_wrapper.S
Normal file
@ -0,0 +1,14 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/page.h>
|
||||
|
||||
__PAGE_ALIGNED_DATA
|
||||
|
||||
.globl vdso32_start, vdso32_end
|
||||
.balign PAGE_SIZE
|
||||
vdso32_start:
|
||||
.incbin "arch/parisc/kernel/vdso32/vdso32.so"
|
||||
.balign PAGE_SIZE
|
||||
vdso32_end:
|
||||
|
||||
.previous
|
48
arch/parisc/kernel/vdso64/Makefile
Normal file
48
arch/parisc/kernel/vdso64/Makefile
Normal file
@ -0,0 +1,48 @@
|
||||
# List of files in the vdso, has to be asm only for now
|
||||
|
||||
obj-vdso64 = note.o sigtramp.o restart_syscall.o
|
||||
|
||||
# Build rules
|
||||
|
||||
targets := $(obj-vdso64) vdso64.so
|
||||
obj-vdso64 := $(addprefix $(obj)/, $(obj-vdso64))
|
||||
|
||||
|
||||
ccflags-y := -shared -fno-common -fno-builtin
|
||||
ccflags-y += -nostdlib -Wl,-soname=linux-vdso64.so.1 \
|
||||
$(call ld-option, -Wl$(comma)--hash-style=sysv)
|
||||
asflags-y := -D__VDSO64__ -s
|
||||
|
||||
KBUILD_AFLAGS += -DBUILD_VDSO
|
||||
KBUILD_CFLAGS += -DBUILD_VDSO -DDISABLE_BRANCH_PROFILING
|
||||
|
||||
VDSO_LIBGCC := $(shell $(CC) -print-libgcc-file-name)
|
||||
|
||||
obj-y += vdso64_wrapper.o
|
||||
extra-y += vdso64.lds
|
||||
CPPFLAGS_vdso64.lds += -P -C -U$(ARCH)
|
||||
|
||||
$(obj)/vdso64_wrapper.o : $(obj)/vdso64.so FORCE
|
||||
|
||||
# Force dependency (incbin is bad)
|
||||
# link rule for the .so file, .lds has to be first
|
||||
$(obj)/vdso64.so: $(src)/vdso64.lds $(obj-vdso64) $(VDSO_LIBGCC)
|
||||
$(call if_changed,vdso64ld)
|
||||
|
||||
# assembly rules for the .S files
|
||||
$(obj-vdso64): %.o: %.S FORCE
|
||||
$(call if_changed_dep,vdso64as)
|
||||
|
||||
# actual build commands
|
||||
quiet_cmd_vdso64ld = VDSO64L $@
|
||||
cmd_vdso64ld = $(CC) $(c_flags) -Wl,-T $^ -o $@
|
||||
quiet_cmd_vdso64as = VDSO64A $@
|
||||
cmd_vdso64as = $(CC) $(a_flags) -c -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/vdso64-offsets.h: $(obj)/vdso64.so FORCE
|
||||
$(call if_changed,vdsosym)
|
15
arch/parisc/kernel/vdso64/gen_vdso_offsets.sh
Executable file
15
arch/parisc/kernel/vdso64/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 vdso64_offset_\2\t0x\1/p'
|
2
arch/parisc/kernel/vdso64/note.S
Normal file
2
arch/parisc/kernel/vdso64/note.S
Normal file
@ -0,0 +1,2 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#include "../vdso32/note.S"
|
3
arch/parisc/kernel/vdso64/restart_syscall.S
Normal file
3
arch/parisc/kernel/vdso64/restart_syscall.S
Normal file
@ -0,0 +1,3 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
#include "../vdso32/restart_syscall.S"
|
166
arch/parisc/kernel/vdso64/sigtramp.S
Normal file
166
arch/parisc/kernel/vdso64/sigtramp.S
Normal file
@ -0,0 +1,166 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Signal trampolines for 64 bit processes.
|
||||
*
|
||||
* Copyright (C) 2006 Randolph Chung <tausq@debian.org>
|
||||
* Copyright (C) 2018-2022 Helge Deller <deller@gmx.de>
|
||||
* Copyright (C) 2022 John David Anglin <dave.anglin@bell.net>
|
||||
*/
|
||||
#include <asm/unistd.h>
|
||||
#include <linux/linkage.h>
|
||||
#include <generated/asm-offsets.h>
|
||||
|
||||
.text
|
||||
|
||||
/* Gdb expects the trampoline is on the stack and the pc is offset from
|
||||
a 64-byte boundary by 0, 4 or 5 instructions. Since the vdso trampoline
|
||||
is not on the stack, we need a new variant with different offsets and
|
||||
data to tell gdb where to find the signal context on the stack.
|
||||
|
||||
Here we put the offset to the context data at the start of the trampoline
|
||||
region and offset the first trampoline by 2 instructions. Please do
|
||||
not change the trampoline as the code in gdb depends on the following
|
||||
instruction sequence exactly.
|
||||
*/
|
||||
.align 64
|
||||
.word SIGFRAME_CONTEXT_REGS
|
||||
|
||||
/* The nop here is a hack. The dwarf2 unwind routines subtract 1 from
|
||||
the return address to get an address in the middle of the presumed
|
||||
call instruction. Since we don't have a call here, we artifically
|
||||
extend the range covered by the unwind info by adding a nop before
|
||||
the real start.
|
||||
*/
|
||||
nop
|
||||
|
||||
.globl __kernel_sigtramp_rt
|
||||
.type __kernel_sigtramp_rt, @function
|
||||
__kernel_sigtramp_rt:
|
||||
.proc
|
||||
.callinfo FRAME=ASM_SIGFRAME_SIZE,CALLS,SAVE_RP
|
||||
.entry
|
||||
|
||||
.Lsigrt_start = . - 4
|
||||
0: ldi 0, %r25 /* (in_syscall=0) */
|
||||
ldi __NR_rt_sigreturn, %r20
|
||||
ble 0x100(%sr2, %r0)
|
||||
nop
|
||||
|
||||
1: ldi 1, %r25 /* (in_syscall=1) */
|
||||
ldi __NR_rt_sigreturn, %r20
|
||||
ble 0x100(%sr2, %r0)
|
||||
nop
|
||||
.Lsigrt_end:
|
||||
.exit
|
||||
.procend
|
||||
.size __kernel_sigtramp_rt,.-__kernel_sigtramp_rt
|
||||
|
||||
.section .eh_frame,"a",@progbits
|
||||
|
||||
/* This is where the mcontext_t struct can be found on the stack. */
|
||||
#define PTREGS SIGFRAME_CONTEXT_REGS /* 64-bit process offset is -720 */
|
||||
|
||||
/* Register REGNO can be found at offset OFS of the mcontext_t structure. */
|
||||
.macro rsave regno,ofs
|
||||
.byte 0x05 /* DW_CFA_offset_extended */
|
||||
.uleb128 \regno; /* regno */
|
||||
.uleb128 \ofs /* factored offset */
|
||||
.endm
|
||||
|
||||
.Lcie:
|
||||
.long .Lcie_end - .Lcie_start
|
||||
.Lcie_start:
|
||||
.long 0 /* CIE ID */
|
||||
.byte 1 /* Version number */
|
||||
.stringz "zRS" /* NUL-terminated augmentation string */
|
||||
.uleb128 4 /* Code alignment factor */
|
||||
.sleb128 8 /* Data alignment factor */
|
||||
.byte 61 /* Return address register column, iaoq[0] */
|
||||
.uleb128 1 /* Augmentation value length */
|
||||
.byte 0x1b /* DW_EH_PE_pcrel | DW_EH_PE_sdata4. */
|
||||
.byte 0x0f /* DW_CFA_def_cfa_expresion */
|
||||
.uleb128 9f - 1f /* length */
|
||||
1:
|
||||
.byte 0x8e /* DW_OP_breg30 */
|
||||
.sleb128 PTREGS
|
||||
9:
|
||||
.balign 8
|
||||
.Lcie_end:
|
||||
|
||||
.long .Lfde0_end - .Lfde0_start
|
||||
.Lfde0_start:
|
||||
.long .Lfde0_start - .Lcie /* CIE pointer. */
|
||||
.long .Lsigrt_start - . /* PC start, length */
|
||||
.long .Lsigrt_end - .Lsigrt_start
|
||||
.uleb128 0 /* Augmentation */
|
||||
|
||||
/* General registers */
|
||||
rsave 1, 2
|
||||
rsave 2, 3
|
||||
rsave 3, 4
|
||||
rsave 4, 5
|
||||
rsave 5, 6
|
||||
rsave 6, 7
|
||||
rsave 7, 8
|
||||
rsave 8, 9
|
||||
rsave 9, 10
|
||||
rsave 10, 11
|
||||
rsave 11, 12
|
||||
rsave 12, 13
|
||||
rsave 13, 14
|
||||
rsave 14, 15
|
||||
rsave 15, 16
|
||||
rsave 16, 17
|
||||
rsave 17, 18
|
||||
rsave 18, 19
|
||||
rsave 19, 20
|
||||
rsave 20, 21
|
||||
rsave 21, 22
|
||||
rsave 22, 23
|
||||
rsave 23, 24
|
||||
rsave 24, 25
|
||||
rsave 25, 26
|
||||
rsave 26, 27
|
||||
rsave 27, 28
|
||||
rsave 28, 29
|
||||
rsave 29, 30
|
||||
rsave 30, 31
|
||||
rsave 31, 32
|
||||
|
||||
/* Floating-point registers */
|
||||
rsave 32, 36
|
||||
rsave 33, 37
|
||||
rsave 34, 38
|
||||
rsave 35, 39
|
||||
rsave 36, 40
|
||||
rsave 37, 41
|
||||
rsave 38, 42
|
||||
rsave 39, 43
|
||||
rsave 40, 44
|
||||
rsave 41, 45
|
||||
rsave 42, 46
|
||||
rsave 43, 47
|
||||
rsave 44, 48
|
||||
rsave 45, 49
|
||||
rsave 46, 50
|
||||
rsave 47, 51
|
||||
rsave 48, 52
|
||||
rsave 49, 53
|
||||
rsave 50, 54
|
||||
rsave 51, 55
|
||||
rsave 52, 56
|
||||
rsave 53, 57
|
||||
rsave 54, 58
|
||||
rsave 55, 59
|
||||
rsave 56, 60
|
||||
rsave 57, 61
|
||||
rsave 58, 62
|
||||
rsave 59, 63
|
||||
|
||||
/* SAR register */
|
||||
rsave 60, 67
|
||||
|
||||
/* iaoq[0] return address register */
|
||||
rsave 61, 65
|
||||
.balign 8
|
||||
.Lfde0_end:
|
109
arch/parisc/kernel/vdso64/vdso64.lds.S
Normal file
109
arch/parisc/kernel/vdso64/vdso64.lds.S
Normal file
@ -0,0 +1,109 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* This is the infamous ld script for the 64 bits vdso library
|
||||
*/
|
||||
#include <asm/vdso.h>
|
||||
|
||||
/* Default link addresses for the vDSOs */
|
||||
OUTPUT_FORMAT("elf64-hppa-linux")
|
||||
OUTPUT_ARCH(hppa:hppa2.0w)
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = VDSO_LBASE + SIZEOF_HEADERS;
|
||||
.hash : { *(.hash) } :text
|
||||
.gnu.hash : { *(.gnu.hash) }
|
||||
.dynsym : { *(.dynsym) }
|
||||
.dynstr : { *(.dynstr) }
|
||||
.gnu.version : { *(.gnu.version) }
|
||||
.gnu.version_d : { *(.gnu.version_d) }
|
||||
.gnu.version_r : { *(.gnu.version_r) }
|
||||
|
||||
.note : { *(.note.*) } :text :note
|
||||
|
||||
. = ALIGN (16);
|
||||
.text :
|
||||
{
|
||||
*(.text .stub .text.* .gnu.linkonce.t.*)
|
||||
}
|
||||
PROVIDE (__etext = .);
|
||||
PROVIDE (_etext = .);
|
||||
PROVIDE (etext = .);
|
||||
|
||||
/* Other stuff is appended to the text segment: */
|
||||
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
|
||||
.rodata1 : { *(.rodata1) }
|
||||
|
||||
.eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr
|
||||
.eh_frame : { KEEP (*(.eh_frame)) } :text
|
||||
.gcc_except_table : { *(.gcc_except_table) }
|
||||
.fixup : { *(.fixup) }
|
||||
|
||||
.dynamic : { *(.dynamic) } :text :dynamic
|
||||
.plt : { *(.plt) }
|
||||
.got : { *(.got) }
|
||||
|
||||
_end = .;
|
||||
__end = .;
|
||||
PROVIDE (end = .);
|
||||
|
||||
|
||||
/* Stabs debugging sections are here too
|
||||
*/
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
.comment 0 : { *(.comment) }
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
|
||||
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
|
||||
.debug_weaknames 0 : { *(.debug_weaknames) }
|
||||
.debug_funcnames 0 : { *(.debug_funcnames) }
|
||||
.debug_typenames 0 : { *(.debug_typenames) }
|
||||
.debug_varnames 0 : { *(.debug_varnames) }
|
||||
|
||||
/DISCARD/ : { *(.note.GNU-stack) }
|
||||
/DISCARD/ : { *(.data .data.* .gnu.linkonce.d.* .sdata*) }
|
||||
/DISCARD/ : { *(.bss .sbss .dynbss .dynsbss) }
|
||||
}
|
||||
|
||||
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD FILEHDR PHDRS FLAGS(5); /* PF_R|PF_X */
|
||||
note PT_NOTE FLAGS(4); /* PF_R */
|
||||
dynamic PT_DYNAMIC FLAGS(4); /* PF_R */
|
||||
eh_frame_hdr PT_GNU_EH_FRAME;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This controls what symbols we export from the DSO.
|
||||
*/
|
||||
VERSION
|
||||
{
|
||||
VDSO_VERSION_STRING {
|
||||
global:
|
||||
__kernel_sigtramp_rt64;
|
||||
__kernel_restart_syscall64;
|
||||
local: *;
|
||||
};
|
||||
}
|
14
arch/parisc/kernel/vdso64/vdso64_wrapper.S
Normal file
14
arch/parisc/kernel/vdso64/vdso64_wrapper.S
Normal file
@ -0,0 +1,14 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/page.h>
|
||||
|
||||
__PAGE_ALIGNED_DATA
|
||||
|
||||
.globl vdso64_start, vdso64_end
|
||||
.balign PAGE_SIZE
|
||||
vdso64_start:
|
||||
.incbin "arch/parisc/kernel/vdso64/vdso64.so"
|
||||
.balign PAGE_SIZE
|
||||
vdso64_end:
|
||||
|
||||
.previous
|
@ -13,8 +13,8 @@
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define get_user_space() (uaccess_kernel() ? 0 : mfsp(3))
|
||||
#define get_kernel_space() (0)
|
||||
#define get_user_space() mfsp(SR_USER)
|
||||
#define get_kernel_space() SR_KERNEL
|
||||
|
||||
/* Returns 0 for success, otherwise, returns number of bytes not transferred. */
|
||||
extern unsigned long pa_memcpy(void *dst, const void *src,
|
||||
@ -23,8 +23,8 @@ extern unsigned long pa_memcpy(void *dst, const void *src,
|
||||
unsigned long raw_copy_to_user(void __user *dst, const void *src,
|
||||
unsigned long len)
|
||||
{
|
||||
mtsp(get_kernel_space(), 1);
|
||||
mtsp(get_user_space(), 2);
|
||||
mtsp(get_kernel_space(), SR_TEMP1);
|
||||
mtsp(get_user_space(), SR_TEMP2);
|
||||
return pa_memcpy((void __force *)dst, src, len);
|
||||
}
|
||||
EXPORT_SYMBOL(raw_copy_to_user);
|
||||
@ -32,16 +32,16 @@ EXPORT_SYMBOL(raw_copy_to_user);
|
||||
unsigned long raw_copy_from_user(void *dst, const void __user *src,
|
||||
unsigned long len)
|
||||
{
|
||||
mtsp(get_user_space(), 1);
|
||||
mtsp(get_kernel_space(), 2);
|
||||
mtsp(get_user_space(), SR_TEMP1);
|
||||
mtsp(get_kernel_space(), SR_TEMP2);
|
||||
return pa_memcpy(dst, (void __force *)src, len);
|
||||
}
|
||||
EXPORT_SYMBOL(raw_copy_from_user);
|
||||
|
||||
void * memcpy(void * dst,const void *src, size_t count)
|
||||
{
|
||||
mtsp(get_kernel_space(), 1);
|
||||
mtsp(get_kernel_space(), 2);
|
||||
mtsp(get_kernel_space(), SR_TEMP1);
|
||||
mtsp(get_kernel_space(), SR_TEMP2);
|
||||
pa_memcpy(dst, src, count);
|
||||
return dst;
|
||||
}
|
||||
|
@ -425,3 +425,92 @@ out_of_memory:
|
||||
}
|
||||
pagefault_out_of_memory();
|
||||
}
|
||||
|
||||
/* Handle non-access data TLB miss faults.
|
||||
*
|
||||
* For probe instructions, accesses to userspace are considered allowed
|
||||
* if they lie in a valid VMA and the access type matches. We are not
|
||||
* allowed to handle MM faults here so there may be situations where an
|
||||
* actual access would fail even though a probe was successful.
|
||||
*/
|
||||
int
|
||||
handle_nadtlb_fault(struct pt_regs *regs)
|
||||
{
|
||||
unsigned long insn = regs->iir;
|
||||
int breg, treg, xreg, val = 0;
|
||||
struct vm_area_struct *vma, *prev_vma;
|
||||
struct task_struct *tsk;
|
||||
struct mm_struct *mm;
|
||||
unsigned long address;
|
||||
unsigned long acc_type;
|
||||
|
||||
switch (insn & 0x380) {
|
||||
case 0x280:
|
||||
/* FDC instruction */
|
||||
fallthrough;
|
||||
case 0x380:
|
||||
/* PDC and FIC instructions */
|
||||
if (printk_ratelimit()) {
|
||||
pr_warn("BUG: nullifying cache flush/purge instruction\n");
|
||||
show_regs(regs);
|
||||
}
|
||||
if (insn & 0x20) {
|
||||
/* Base modification */
|
||||
breg = (insn >> 21) & 0x1f;
|
||||
xreg = (insn >> 16) & 0x1f;
|
||||
if (breg && xreg)
|
||||
regs->gr[breg] += regs->gr[xreg];
|
||||
}
|
||||
regs->gr[0] |= PSW_N;
|
||||
return 1;
|
||||
|
||||
case 0x180:
|
||||
/* PROBE instruction */
|
||||
treg = insn & 0x1f;
|
||||
if (regs->isr) {
|
||||
tsk = current;
|
||||
mm = tsk->mm;
|
||||
if (mm) {
|
||||
/* Search for VMA */
|
||||
address = regs->ior;
|
||||
mmap_read_lock(mm);
|
||||
vma = find_vma_prev(mm, address, &prev_vma);
|
||||
mmap_read_unlock(mm);
|
||||
|
||||
/*
|
||||
* Check if access to the VMA is okay.
|
||||
* We don't allow for stack expansion.
|
||||
*/
|
||||
acc_type = (insn & 0x40) ? VM_WRITE : VM_READ;
|
||||
if (vma
|
||||
&& address >= vma->vm_start
|
||||
&& (vma->vm_flags & acc_type) == acc_type)
|
||||
val = 1;
|
||||
}
|
||||
}
|
||||
if (treg)
|
||||
regs->gr[treg] = val;
|
||||
regs->gr[0] |= PSW_N;
|
||||
return 1;
|
||||
|
||||
case 0x300:
|
||||
/* LPA instruction */
|
||||
if (insn & 0x20) {
|
||||
/* Base modification */
|
||||
breg = (insn >> 21) & 0x1f;
|
||||
xreg = (insn >> 16) & 0x1f;
|
||||
if (breg && xreg)
|
||||
regs->gr[breg] += regs->gr[xreg];
|
||||
}
|
||||
treg = insn & 0x1f;
|
||||
if (treg)
|
||||
regs->gr[treg] = 0;
|
||||
regs->gr[0] |= PSW_N;
|
||||
return 1;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1041,6 +1041,47 @@ stifb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
|
||||
SETUP_FB(fb);
|
||||
}
|
||||
|
||||
#define ARTIST_VRAM_SIZE 0x000804
|
||||
#define ARTIST_VRAM_SRC 0x000808
|
||||
#define ARTIST_VRAM_SIZE_TRIGGER_WINFILL 0x000a04
|
||||
#define ARTIST_VRAM_DEST_TRIGGER_BLOCKMOVE 0x000b00
|
||||
#define ARTIST_SRC_BM_ACCESS 0x018008
|
||||
#define ARTIST_FGCOLOR 0x018010
|
||||
#define ARTIST_BGCOLOR 0x018014
|
||||
#define ARTIST_BITMAP_OP 0x01801c
|
||||
|
||||
static void
|
||||
stifb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
|
||||
{
|
||||
struct stifb_info *fb = container_of(info, struct stifb_info, info);
|
||||
|
||||
if (rect->rop != ROP_COPY)
|
||||
return cfb_fillrect(info, rect);
|
||||
|
||||
SETUP_HW(fb);
|
||||
|
||||
if (fb->info.var.bits_per_pixel == 32) {
|
||||
WRITE_WORD(0xBBA0A000, fb, REG_10);
|
||||
|
||||
NGLE_REALLY_SET_IMAGE_PLANEMASK(fb, 0xffffffff);
|
||||
} else {
|
||||
WRITE_WORD(fb->id == S9000_ID_HCRX ? 0x13a02000 : 0x13a01000, fb, REG_10);
|
||||
|
||||
NGLE_REALLY_SET_IMAGE_PLANEMASK(fb, 0xff);
|
||||
}
|
||||
|
||||
WRITE_WORD(0x03000300, fb, ARTIST_BITMAP_OP);
|
||||
WRITE_WORD(0x2ea01000, fb, ARTIST_SRC_BM_ACCESS);
|
||||
NGLE_QUICK_SET_DST_BM_ACCESS(fb, 0x2ea01000);
|
||||
NGLE_REALLY_SET_IMAGE_FG_COLOR(fb, rect->color);
|
||||
WRITE_WORD(0, fb, ARTIST_BGCOLOR);
|
||||
|
||||
NGLE_SET_DSTXY(fb, (rect->dx << 16) | (rect->dy));
|
||||
SET_LENXY_START_RECFILL(fb, (rect->width << 16) | (rect->height));
|
||||
|
||||
SETUP_FB(fb);
|
||||
}
|
||||
|
||||
static void __init
|
||||
stifb_init_display(struct stifb_info *fb)
|
||||
{
|
||||
@ -1105,7 +1146,7 @@ static const struct fb_ops stifb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.fb_setcolreg = stifb_setcolreg,
|
||||
.fb_blank = stifb_blank,
|
||||
.fb_fillrect = cfb_fillrect,
|
||||
.fb_fillrect = stifb_fillrect,
|
||||
.fb_copyarea = stifb_copyarea,
|
||||
.fb_imageblit = cfb_imageblit,
|
||||
};
|
||||
@ -1297,7 +1338,7 @@ static int __init stifb_init_fb(struct sti_struct *sti, int bpp_pref)
|
||||
goto out_err0;
|
||||
}
|
||||
info->screen_size = fix->smem_len;
|
||||
info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA;
|
||||
info->flags = FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT;
|
||||
info->pseudo_palette = &fb->pseudo_palette;
|
||||
|
||||
/* This has to be done !!! */
|
||||
|
Loading…
x
Reference in New Issue
Block a user