Merge branch 'parisc-5.2-1' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/parisc-linux
Pull parisc updates from Helge Deller: "Many great new features, fixes and optimizations, including: - Convert page table updates to use per-pagetable spinlocks which overall improves performance on SMP machines a lot, by Mikulas Patocka - Kernel debugger (KGDB) support, by Sven Schnelle - KPROBES support, by Sven Schnelle - Lots of TLB lock/flush improvements, by Dave Anglin - Drop DISCONTIGMEM and switch to SPARSEMEM - Added JUMP_LABEL, branch runtime-patching support - Lots of other small speedups and cleanups, e.g. for QEMU, stack randomization, avoidance of name clashes, documentation updates, etc ..." * 'parisc-5.2-1' of git://git.kernel.org/pub/scm/linux/kernel/git/deller/parisc-linux: (28 commits) parisc: Add static branch and JUMP_LABEL feature parisc: Use PA_ASM_LEVEL in boot code parisc: Rename LEVEL to PA_ASM_LEVEL to avoid name clash with DRBD code parisc: Update huge TLB page support to use per-pagetable spinlock parisc: Use per-pagetable spinlock parisc: Allow live-patching of __meminit functions parisc: Add memory barrier to asm pdc and sync instructions parisc: Add memory clobber to TLB purges parisc: Use ldcw instruction for SMP spinlock release barrier parisc: Remove lock code to serialize TLB operations in pacache.S parisc: Switch from DISCONTIGMEM to SPARSEMEM parisc: enable wide mode early parisc: update feature lists parisc: Show n/a if product number not available parisc: remove unused flags parameter in __patch_text() doc: update kprobes supported architecture list parisc: Implement kretprobes parisc: remove kprobes.h from generic-y parisc: Implement kprobes parisc: add functions required by KPROBE_EVENTS ...
This commit is contained in:
@@ -9,7 +9,8 @@ obj-y := cache.o pacache.o setup.o pdt.o traps.o time.o irq.o \
|
||||
pa7300lc.o syscall.o entry.o sys_parisc.o firmware.o \
|
||||
ptrace.o hardware.o inventory.o drivers.o alternative.o \
|
||||
signal.o hpmc.o real2.o parisc_ksyms.o unaligned.o \
|
||||
process.o processor.o pdc_cons.o pdc_chassis.o unwind.o
|
||||
process.o processor.o pdc_cons.o pdc_chassis.o unwind.o \
|
||||
patch.o
|
||||
|
||||
ifdef CONFIG_FUNCTION_TRACER
|
||||
# Do not profile debug and lowlevel utilities
|
||||
@@ -32,3 +33,6 @@ obj-$(CONFIG_64BIT) += perf.o perf_asm.o $(obj64-y)
|
||||
obj-$(CONFIG_PARISC_CPU_TOPOLOGY) += topology.o
|
||||
obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o
|
||||
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
|
||||
obj-$(CONFIG_JUMP_LABEL) += jump_label.o
|
||||
obj-$(CONFIG_KGDB) += kgdb.o
|
||||
obj-$(CONFIG_KPROBES) += kprobes.o
|
||||
|
@@ -40,12 +40,19 @@ void purge_dcache_page_asm(unsigned long phys_addr, unsigned long vaddr);
|
||||
void flush_icache_page_asm(unsigned long phys_addr, unsigned long vaddr);
|
||||
|
||||
|
||||
/* On some machines (e.g. ones with the Merced bus), there can be
|
||||
/* 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
|
||||
* by software. We put a spinlock around all TLB flushes to
|
||||
* ensure this.
|
||||
* by software. We need a spinlock around all TLB flushes to ensure
|
||||
* this.
|
||||
*/
|
||||
DEFINE_SPINLOCK(pa_tlb_lock);
|
||||
DEFINE_SPINLOCK(pa_tlb_flush_lock);
|
||||
|
||||
/* Swapper page setup lock. */
|
||||
DEFINE_SPINLOCK(pa_swapper_pg_lock);
|
||||
|
||||
#if defined(CONFIG_64BIT) && defined(CONFIG_SMP)
|
||||
int pa_serialize_tlb_flushes __read_mostly;
|
||||
#endif
|
||||
|
||||
struct pdc_cache_info cache_info __read_mostly;
|
||||
#ifndef CONFIG_PA20
|
||||
|
@@ -38,6 +38,7 @@
|
||||
#include <asm/io.h>
|
||||
#include <asm/pdc.h>
|
||||
#include <asm/parisc-device.h>
|
||||
#include <asm/ropes.h>
|
||||
|
||||
/* See comments in include/asm-parisc/pci.h */
|
||||
const struct dma_map_ops *hppa_dma_ops __read_mostly;
|
||||
@@ -257,6 +258,30 @@ static struct parisc_device *find_device_by_addr(unsigned long hpa)
|
||||
return ret ? d.dev : NULL;
|
||||
}
|
||||
|
||||
static int __init is_IKE_device(struct device *dev, void *data)
|
||||
{
|
||||
struct parisc_device *pdev = to_parisc_device(dev);
|
||||
|
||||
if (!check_dev(dev))
|
||||
return 0;
|
||||
if (pdev->id.hw_type != HPHW_BCPORT)
|
||||
return 0;
|
||||
if (IS_IKE(pdev) ||
|
||||
(pdev->id.hversion == REO_MERCED_PORT) ||
|
||||
(pdev->id.hversion == REOG_MERCED_PORT)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init machine_has_merced_bus(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = for_each_padev(is_IKE_device, NULL);
|
||||
return ret ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* find_pa_parent_type - Find a parent of a specific type
|
||||
* @dev: The device to start searching from
|
||||
|
@@ -50,12 +50,8 @@
|
||||
|
||||
.import pa_tlb_lock,data
|
||||
.macro load_pa_tlb_lock reg
|
||||
#if __PA_LDCW_ALIGNMENT > 4
|
||||
load32 PA(pa_tlb_lock) + __PA_LDCW_ALIGNMENT-1, \reg
|
||||
depi 0,31,__PA_LDCW_ALIGN_ORDER, \reg
|
||||
#else
|
||||
load32 PA(pa_tlb_lock), \reg
|
||||
#endif
|
||||
mfctl %cr25,\reg
|
||||
addil L%(PAGE_SIZE << (PGD_ALLOC_ORDER - 1)),\reg
|
||||
.endm
|
||||
|
||||
/* space_to_prot macro creates a prot id from a space id */
|
||||
@@ -471,8 +467,9 @@
|
||||
nop
|
||||
LDREG 0(\ptp),\pte
|
||||
bb,<,n \pte,_PAGE_PRESENT_BIT,3f
|
||||
LDCW 0(\tmp),\tmp1
|
||||
b \fault
|
||||
stw,ma \spc,0(\tmp)
|
||||
stw \spc,0(\tmp)
|
||||
99: ALTERNATIVE(98b, 99b, ALT_COND_NO_SMP, INSN_NOP)
|
||||
#endif
|
||||
2: LDREG 0(\ptp),\pte
|
||||
@@ -481,20 +478,22 @@
|
||||
.endm
|
||||
|
||||
/* Release pa_tlb_lock lock without reloading lock address. */
|
||||
.macro tlb_unlock0 spc,tmp
|
||||
.macro tlb_unlock0 spc,tmp,tmp1
|
||||
#ifdef CONFIG_SMP
|
||||
98: or,COND(=) %r0,\spc,%r0
|
||||
stw,ma \spc,0(\tmp)
|
||||
LDCW 0(\tmp),\tmp1
|
||||
or,COND(=) %r0,\spc,%r0
|
||||
stw \spc,0(\tmp)
|
||||
99: ALTERNATIVE(98b, 99b, ALT_COND_NO_SMP, INSN_NOP)
|
||||
#endif
|
||||
.endm
|
||||
|
||||
/* Release pa_tlb_lock lock. */
|
||||
.macro tlb_unlock1 spc,tmp
|
||||
.macro tlb_unlock1 spc,tmp,tmp1
|
||||
#ifdef CONFIG_SMP
|
||||
98: load_pa_tlb_lock \tmp
|
||||
99: ALTERNATIVE(98b, 99b, ALT_COND_NO_SMP, INSN_NOP)
|
||||
tlb_unlock0 \spc,\tmp
|
||||
tlb_unlock0 \spc,\tmp,\tmp1
|
||||
#endif
|
||||
.endm
|
||||
|
||||
@@ -1177,7 +1176,7 @@ dtlb_miss_20w:
|
||||
|
||||
idtlbt pte,prot
|
||||
|
||||
tlb_unlock1 spc,t0
|
||||
tlb_unlock1 spc,t0,t1
|
||||
rfir
|
||||
nop
|
||||
|
||||
@@ -1203,7 +1202,7 @@ nadtlb_miss_20w:
|
||||
|
||||
idtlbt pte,prot
|
||||
|
||||
tlb_unlock1 spc,t0
|
||||
tlb_unlock1 spc,t0,t1
|
||||
rfir
|
||||
nop
|
||||
|
||||
@@ -1237,7 +1236,7 @@ dtlb_miss_11:
|
||||
|
||||
mtsp t1, %sr1 /* Restore sr1 */
|
||||
|
||||
tlb_unlock1 spc,t0
|
||||
tlb_unlock1 spc,t0,t1
|
||||
rfir
|
||||
nop
|
||||
|
||||
@@ -1270,7 +1269,7 @@ nadtlb_miss_11:
|
||||
|
||||
mtsp t1, %sr1 /* Restore sr1 */
|
||||
|
||||
tlb_unlock1 spc,t0
|
||||
tlb_unlock1 spc,t0,t1
|
||||
rfir
|
||||
nop
|
||||
|
||||
@@ -1299,7 +1298,7 @@ dtlb_miss_20:
|
||||
|
||||
idtlbt pte,prot
|
||||
|
||||
tlb_unlock1 spc,t0
|
||||
tlb_unlock1 spc,t0,t1
|
||||
rfir
|
||||
nop
|
||||
|
||||
@@ -1327,7 +1326,7 @@ nadtlb_miss_20:
|
||||
|
||||
idtlbt pte,prot
|
||||
|
||||
tlb_unlock1 spc,t0
|
||||
tlb_unlock1 spc,t0,t1
|
||||
rfir
|
||||
nop
|
||||
|
||||
@@ -1434,7 +1433,7 @@ itlb_miss_20w:
|
||||
|
||||
iitlbt pte,prot
|
||||
|
||||
tlb_unlock1 spc,t0
|
||||
tlb_unlock1 spc,t0,t1
|
||||
rfir
|
||||
nop
|
||||
|
||||
@@ -1458,7 +1457,7 @@ naitlb_miss_20w:
|
||||
|
||||
iitlbt pte,prot
|
||||
|
||||
tlb_unlock1 spc,t0
|
||||
tlb_unlock1 spc,t0,t1
|
||||
rfir
|
||||
nop
|
||||
|
||||
@@ -1492,7 +1491,7 @@ itlb_miss_11:
|
||||
|
||||
mtsp t1, %sr1 /* Restore sr1 */
|
||||
|
||||
tlb_unlock1 spc,t0
|
||||
tlb_unlock1 spc,t0,t1
|
||||
rfir
|
||||
nop
|
||||
|
||||
@@ -1516,7 +1515,7 @@ naitlb_miss_11:
|
||||
|
||||
mtsp t1, %sr1 /* Restore sr1 */
|
||||
|
||||
tlb_unlock1 spc,t0
|
||||
tlb_unlock1 spc,t0,t1
|
||||
rfir
|
||||
nop
|
||||
|
||||
@@ -1546,7 +1545,7 @@ itlb_miss_20:
|
||||
|
||||
iitlbt pte,prot
|
||||
|
||||
tlb_unlock1 spc,t0
|
||||
tlb_unlock1 spc,t0,t1
|
||||
rfir
|
||||
nop
|
||||
|
||||
@@ -1566,7 +1565,7 @@ naitlb_miss_20:
|
||||
|
||||
iitlbt pte,prot
|
||||
|
||||
tlb_unlock1 spc,t0
|
||||
tlb_unlock1 spc,t0,t1
|
||||
rfir
|
||||
nop
|
||||
|
||||
@@ -1596,7 +1595,7 @@ dbit_trap_20w:
|
||||
|
||||
idtlbt pte,prot
|
||||
|
||||
tlb_unlock0 spc,t0
|
||||
tlb_unlock0 spc,t0,t1
|
||||
rfir
|
||||
nop
|
||||
#else
|
||||
@@ -1622,7 +1621,7 @@ dbit_trap_11:
|
||||
|
||||
mtsp t1, %sr1 /* Restore sr1 */
|
||||
|
||||
tlb_unlock0 spc,t0
|
||||
tlb_unlock0 spc,t0,t1
|
||||
rfir
|
||||
nop
|
||||
|
||||
@@ -1642,7 +1641,7 @@ dbit_trap_20:
|
||||
|
||||
idtlbt pte,prot
|
||||
|
||||
tlb_unlock0 spc,t0
|
||||
tlb_unlock0 spc,t0,t1
|
||||
rfir
|
||||
nop
|
||||
#endif
|
||||
|
@@ -22,7 +22,7 @@
|
||||
#include <linux/linkage.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
.level LEVEL
|
||||
.level PA_ASM_LEVEL
|
||||
|
||||
__INITDATA
|
||||
ENTRY(boot_args)
|
||||
@@ -258,7 +258,7 @@ stext_pdc_ret:
|
||||
ldo R%PA(fault_vector_11)(%r10),%r10
|
||||
|
||||
$is_pa20:
|
||||
.level LEVEL /* restore 1.1 || 2.0w */
|
||||
.level PA_ASM_LEVEL /* restore 1.1 || 2.0w */
|
||||
#endif /*!CONFIG_64BIT*/
|
||||
load32 PA(fault_vector_20),%r10
|
||||
|
||||
@@ -329,6 +329,19 @@ smp_slave_stext:
|
||||
mtsp %r0,%sr6
|
||||
mtsp %r0,%sr7
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
/*
|
||||
* Enable Wide mode early, in case the task_struct for the idle
|
||||
* task in smp_init_current_idle_task was allocated above 4GB.
|
||||
*/
|
||||
1: mfia %rp /* clear upper part of pcoq */
|
||||
ldo 2f-1b(%rp),%rp
|
||||
depdi 0,31,32,%rp
|
||||
bv (%rp)
|
||||
ssm PSW_SM_W,%r0
|
||||
2:
|
||||
#endif
|
||||
|
||||
/* Initialize the SP - monarch sets up smp_init_current_idle_task */
|
||||
load32 PA(smp_init_current_idle_task),%sp
|
||||
LDREG 0(%sp),%sp /* load task address */
|
||||
|
@@ -31,6 +31,7 @@
|
||||
#include <asm/processor.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/parisc-device.h>
|
||||
#include <asm/tlbflush.h>
|
||||
|
||||
/*
|
||||
** Debug options
|
||||
@@ -638,4 +639,10 @@ void __init do_device_inventory(void)
|
||||
}
|
||||
printk(KERN_INFO "Found devices:\n");
|
||||
print_parisc_devices();
|
||||
|
||||
#if defined(CONFIG_64BIT) && defined(CONFIG_SMP)
|
||||
pa_serialize_tlb_flushes = machine_has_merced_bus();
|
||||
if (pa_serialize_tlb_flushes)
|
||||
pr_info("Merced bus found: Enable PxTLB serialization.\n");
|
||||
#endif
|
||||
}
|
||||
|
55
arch/parisc/kernel/jump_label.c
Normal file
55
arch/parisc/kernel/jump_label.c
Normal file
@@ -0,0 +1,55 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2019 Helge Deller <deller@gmx.de>
|
||||
*
|
||||
* Based on arch/arm64/kernel/jump_label.c
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/jump_label.h>
|
||||
#include <linux/bug.h>
|
||||
#include <asm/alternative.h>
|
||||
#include <asm/patch.h>
|
||||
|
||||
static inline int reassemble_17(int as17)
|
||||
{
|
||||
return (((as17 & 0x10000) >> 16) |
|
||||
((as17 & 0x0f800) << 5) |
|
||||
((as17 & 0x00400) >> 8) |
|
||||
((as17 & 0x003ff) << 3));
|
||||
}
|
||||
|
||||
void arch_jump_label_transform(struct jump_entry *entry,
|
||||
enum jump_label_type type)
|
||||
{
|
||||
void *addr = (void *)jump_entry_code(entry);
|
||||
u32 insn;
|
||||
|
||||
if (type == JUMP_LABEL_JMP) {
|
||||
void *target = (void *)jump_entry_target(entry);
|
||||
int distance = target - addr;
|
||||
/*
|
||||
* Encode the PA1.1 "b,n" instruction with a 17-bit
|
||||
* displacement. In case we hit the BUG(), we could use
|
||||
* another branch instruction with a 22-bit displacement on
|
||||
* 64-bit CPUs instead. But this seems sufficient for now.
|
||||
*/
|
||||
distance -= 8;
|
||||
BUG_ON(distance > 262143 || distance < -262144);
|
||||
insn = 0xe8000002 | reassemble_17(distance >> 2);
|
||||
} else {
|
||||
insn = INSN_NOP;
|
||||
}
|
||||
|
||||
patch_text(addr, insn);
|
||||
}
|
||||
|
||||
void arch_jump_label_transform_static(struct jump_entry *entry,
|
||||
enum jump_label_type type)
|
||||
{
|
||||
/*
|
||||
* We use the architected NOP in arch_static_branch, so there's no
|
||||
* need to patch an identical NOP over the top of it here. The core
|
||||
* will call arch_jump_label_transform from a module notifier if the
|
||||
* NOP needs to be replaced by a branch.
|
||||
*/
|
||||
}
|
209
arch/parisc/kernel/kgdb.c
Normal file
209
arch/parisc/kernel/kgdb.c
Normal file
@@ -0,0 +1,209 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* PA-RISC KGDB support
|
||||
*
|
||||
* Copyright (c) 2019 Sven Schnelle <svens@stackframe.org>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kgdb.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/kdebug.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/traps.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/patch.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
const struct kgdb_arch arch_kgdb_ops = {
|
||||
.gdb_bpt_instr = { 0x03, 0xff, 0xa0, 0x1f }
|
||||
};
|
||||
|
||||
static int __kgdb_notify(struct die_args *args, unsigned long cmd)
|
||||
{
|
||||
struct pt_regs *regs = args->regs;
|
||||
|
||||
if (kgdb_handle_exception(1, args->signr, cmd, regs))
|
||||
return NOTIFY_DONE;
|
||||
return NOTIFY_STOP;
|
||||
}
|
||||
|
||||
static int kgdb_notify(struct notifier_block *self,
|
||||
unsigned long cmd, void *ptr)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
local_irq_save(flags);
|
||||
ret = __kgdb_notify(ptr, cmd);
|
||||
local_irq_restore(flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct notifier_block kgdb_notifier = {
|
||||
.notifier_call = kgdb_notify,
|
||||
.priority = -INT_MAX,
|
||||
};
|
||||
|
||||
int kgdb_arch_init(void)
|
||||
{
|
||||
return register_die_notifier(&kgdb_notifier);
|
||||
}
|
||||
|
||||
void kgdb_arch_exit(void)
|
||||
{
|
||||
unregister_die_notifier(&kgdb_notifier);
|
||||
}
|
||||
|
||||
void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)
|
||||
{
|
||||
struct parisc_gdb_regs *gr = (struct parisc_gdb_regs *)gdb_regs;
|
||||
|
||||
memset(gr, 0, sizeof(struct parisc_gdb_regs));
|
||||
|
||||
memcpy(gr->gpr, regs->gr, sizeof(gr->gpr));
|
||||
memcpy(gr->fr, regs->fr, sizeof(gr->fr));
|
||||
|
||||
gr->sr0 = regs->sr[0];
|
||||
gr->sr1 = regs->sr[1];
|
||||
gr->sr2 = regs->sr[2];
|
||||
gr->sr3 = regs->sr[3];
|
||||
gr->sr4 = regs->sr[4];
|
||||
gr->sr5 = regs->sr[5];
|
||||
gr->sr6 = regs->sr[6];
|
||||
gr->sr7 = regs->sr[7];
|
||||
|
||||
gr->sar = regs->sar;
|
||||
gr->iir = regs->iir;
|
||||
gr->isr = regs->isr;
|
||||
gr->ior = regs->ior;
|
||||
gr->ipsw = regs->ipsw;
|
||||
gr->cr27 = regs->cr27;
|
||||
|
||||
gr->iaoq_f = regs->iaoq[0];
|
||||
gr->iasq_f = regs->iasq[0];
|
||||
|
||||
gr->iaoq_b = regs->iaoq[1];
|
||||
gr->iasq_b = regs->iasq[1];
|
||||
}
|
||||
|
||||
void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)
|
||||
{
|
||||
struct parisc_gdb_regs *gr = (struct parisc_gdb_regs *)gdb_regs;
|
||||
|
||||
|
||||
memcpy(regs->gr, gr->gpr, sizeof(regs->gr));
|
||||
memcpy(regs->fr, gr->fr, sizeof(regs->fr));
|
||||
|
||||
regs->sr[0] = gr->sr0;
|
||||
regs->sr[1] = gr->sr1;
|
||||
regs->sr[2] = gr->sr2;
|
||||
regs->sr[3] = gr->sr3;
|
||||
regs->sr[4] = gr->sr4;
|
||||
regs->sr[5] = gr->sr5;
|
||||
regs->sr[6] = gr->sr6;
|
||||
regs->sr[7] = gr->sr7;
|
||||
|
||||
regs->sar = gr->sar;
|
||||
regs->iir = gr->iir;
|
||||
regs->isr = gr->isr;
|
||||
regs->ior = gr->ior;
|
||||
regs->ipsw = gr->ipsw;
|
||||
regs->cr27 = gr->cr27;
|
||||
|
||||
regs->iaoq[0] = gr->iaoq_f;
|
||||
regs->iasq[0] = gr->iasq_f;
|
||||
|
||||
regs->iaoq[1] = gr->iaoq_b;
|
||||
regs->iasq[1] = gr->iasq_b;
|
||||
}
|
||||
|
||||
void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs,
|
||||
struct task_struct *task)
|
||||
{
|
||||
struct pt_regs *regs = task_pt_regs(task);
|
||||
unsigned long gr30, iaoq;
|
||||
|
||||
gr30 = regs->gr[30];
|
||||
iaoq = regs->iaoq[0];
|
||||
|
||||
regs->gr[30] = regs->ksp;
|
||||
regs->iaoq[0] = regs->kpc;
|
||||
pt_regs_to_gdb_regs(gdb_regs, regs);
|
||||
|
||||
regs->gr[30] = gr30;
|
||||
regs->iaoq[0] = iaoq;
|
||||
|
||||
}
|
||||
|
||||
static void step_instruction_queue(struct pt_regs *regs)
|
||||
{
|
||||
regs->iaoq[0] = regs->iaoq[1];
|
||||
regs->iaoq[1] += 4;
|
||||
}
|
||||
|
||||
void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long ip)
|
||||
{
|
||||
regs->iaoq[0] = ip;
|
||||
regs->iaoq[1] = ip + 4;
|
||||
}
|
||||
|
||||
int kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt)
|
||||
{
|
||||
int ret = probe_kernel_read(bpt->saved_instr, (char *)bpt->bpt_addr,
|
||||
BREAK_INSTR_SIZE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
__patch_text((void *)bpt->bpt_addr,
|
||||
*(unsigned int *)&arch_kgdb_ops.gdb_bpt_instr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt)
|
||||
{
|
||||
__patch_text((void *)bpt->bpt_addr, *(unsigned int *)&bpt->saved_instr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kgdb_arch_handle_exception(int trap, int signo,
|
||||
int err_code, char *inbuf, char *outbuf,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
unsigned long addr;
|
||||
char *p = inbuf + 1;
|
||||
|
||||
switch (inbuf[0]) {
|
||||
case 'D':
|
||||
case 'c':
|
||||
case 'k':
|
||||
kgdb_contthread = NULL;
|
||||
kgdb_single_step = 0;
|
||||
|
||||
if (kgdb_hex2long(&p, &addr))
|
||||
kgdb_arch_set_pc(regs, addr);
|
||||
else if (trap == 9 && regs->iir ==
|
||||
PARISC_KGDB_COMPILED_BREAK_INSN)
|
||||
step_instruction_queue(regs);
|
||||
return 0;
|
||||
case 's':
|
||||
kgdb_single_step = 1;
|
||||
if (kgdb_hex2long(&p, &addr)) {
|
||||
kgdb_arch_set_pc(regs, addr);
|
||||
} else if (trap == 9 && regs->iir ==
|
||||
PARISC_KGDB_COMPILED_BREAK_INSN) {
|
||||
step_instruction_queue(regs);
|
||||
mtctl(-1, 0);
|
||||
} else {
|
||||
mtctl(0, 0);
|
||||
}
|
||||
regs->gr[0] |= PSW_R;
|
||||
return 0;
|
||||
|
||||
}
|
||||
return -1;
|
||||
}
|
291
arch/parisc/kernel/kprobes.c
Normal file
291
arch/parisc/kernel/kprobes.c
Normal file
@@ -0,0 +1,291 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* arch/parisc/kernel/kprobes.c
|
||||
*
|
||||
* PA-RISC kprobes implementation
|
||||
*
|
||||
* Copyright (c) 2019 Sven Schnelle <svens@stackframe.org>
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/patch.h>
|
||||
|
||||
DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
|
||||
DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
|
||||
|
||||
int __kprobes arch_prepare_kprobe(struct kprobe *p)
|
||||
{
|
||||
if ((unsigned long)p->addr & 3UL)
|
||||
return -EINVAL;
|
||||
|
||||
p->ainsn.insn = get_insn_slot();
|
||||
if (!p->ainsn.insn)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(p->ainsn.insn, p->addr,
|
||||
MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
|
||||
p->opcode = *p->addr;
|
||||
flush_insn_slot(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __kprobes arch_remove_kprobe(struct kprobe *p)
|
||||
{
|
||||
if (!p->ainsn.insn)
|
||||
return;
|
||||
|
||||
free_insn_slot(p->ainsn.insn, 0);
|
||||
p->ainsn.insn = NULL;
|
||||
}
|
||||
|
||||
void __kprobes arch_arm_kprobe(struct kprobe *p)
|
||||
{
|
||||
patch_text(p->addr, PARISC_KPROBES_BREAK_INSN);
|
||||
}
|
||||
|
||||
void __kprobes arch_disarm_kprobe(struct kprobe *p)
|
||||
{
|
||||
patch_text(p->addr, p->opcode);
|
||||
}
|
||||
|
||||
static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
|
||||
{
|
||||
kcb->prev_kprobe.kp = kprobe_running();
|
||||
kcb->prev_kprobe.status = kcb->kprobe_status;
|
||||
}
|
||||
|
||||
static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
|
||||
{
|
||||
__this_cpu_write(current_kprobe, kcb->prev_kprobe.kp);
|
||||
kcb->kprobe_status = kcb->prev_kprobe.status;
|
||||
}
|
||||
|
||||
static inline void __kprobes set_current_kprobe(struct kprobe *p)
|
||||
{
|
||||
__this_cpu_write(current_kprobe, p);
|
||||
}
|
||||
|
||||
static void __kprobes setup_singlestep(struct kprobe *p,
|
||||
struct kprobe_ctlblk *kcb, struct pt_regs *regs)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
int __kprobes parisc_kprobe_break_handler(struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe *p;
|
||||
struct kprobe_ctlblk *kcb;
|
||||
|
||||
preempt_disable();
|
||||
|
||||
kcb = get_kprobe_ctlblk();
|
||||
p = get_kprobe((unsigned long *)regs->iaoq[0]);
|
||||
|
||||
if (!p) {
|
||||
preempt_enable_no_resched();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (kprobe_running()) {
|
||||
/*
|
||||
* We have reentered the kprobe_handler, since another kprobe
|
||||
* was hit while within the handler, we save the original
|
||||
* kprobes and single step on the instruction of the new probe
|
||||
* without calling any user handlers to avoid recursive
|
||||
* kprobes.
|
||||
*/
|
||||
save_previous_kprobe(kcb);
|
||||
set_current_kprobe(p);
|
||||
kprobes_inc_nmissed_count(p);
|
||||
setup_singlestep(p, kcb, regs);
|
||||
kcb->kprobe_status = KPROBE_REENTER;
|
||||
return 1;
|
||||
}
|
||||
|
||||
set_current_kprobe(p);
|
||||
kcb->kprobe_status = KPROBE_HIT_ACTIVE;
|
||||
|
||||
/* If we have no pre-handler or it returned 0, we continue with
|
||||
* normal processing. If we have a pre-handler and it returned
|
||||
* non-zero - which means user handler setup registers to exit
|
||||
* to another instruction, we must skip the single stepping.
|
||||
*/
|
||||
|
||||
if (!p->pre_handler || !p->pre_handler(p, regs)) {
|
||||
setup_singlestep(p, kcb, regs);
|
||||
kcb->kprobe_status = KPROBE_HIT_SS;
|
||||
} else {
|
||||
reset_current_kprobe();
|
||||
preempt_enable_no_resched();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int __kprobes parisc_kprobe_ss_handler(struct pt_regs *regs)
|
||||
{
|
||||
struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
|
||||
struct kprobe *p = kprobe_running();
|
||||
|
||||
if (regs->iaoq[0] != (unsigned long)p->ainsn.insn+4)
|
||||
return 0;
|
||||
|
||||
/* restore back original saved kprobe variables and continue */
|
||||
if (kcb->kprobe_status == KPROBE_REENTER) {
|
||||
restore_previous_kprobe(kcb);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* for absolute branch instructions we can copy iaoq_b. for relative
|
||||
* branch instructions we need to calculate the new address based on the
|
||||
* difference between iaoq_f and iaoq_b. We cannot use iaoq_b without
|
||||
* modificationt because it's based on our ainsn.insn address.
|
||||
*/
|
||||
|
||||
if (p->post_handler)
|
||||
p->post_handler(p, regs, 0);
|
||||
|
||||
switch (regs->iir >> 26) {
|
||||
case 0x38: /* BE */
|
||||
case 0x39: /* BE,L */
|
||||
case 0x3a: /* BV */
|
||||
case 0x3b: /* BVE */
|
||||
/* for absolute branches, regs->iaoq[1] has already the right
|
||||
* address
|
||||
*/
|
||||
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];
|
||||
break;
|
||||
}
|
||||
kcb->kprobe_status = KPROBE_HIT_SSDONE;
|
||||
reset_current_kprobe();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline void kretprobe_trampoline(void)
|
||||
{
|
||||
asm volatile("nop");
|
||||
asm volatile("nop");
|
||||
}
|
||||
|
||||
static int __kprobes trampoline_probe_handler(struct kprobe *p,
|
||||
struct pt_regs *regs);
|
||||
|
||||
static struct kprobe trampoline_p = {
|
||||
.pre_handler = trampoline_probe_handler
|
||||
};
|
||||
|
||||
static int __kprobes trampoline_probe_handler(struct kprobe *p,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
struct kretprobe_instance *ri = NULL;
|
||||
struct hlist_head *head, empty_rp;
|
||||
struct hlist_node *tmp;
|
||||
unsigned long flags, orig_ret_address = 0;
|
||||
unsigned long trampoline_address = (unsigned long)trampoline_p.addr;
|
||||
kprobe_opcode_t *correct_ret_addr = NULL;
|
||||
|
||||
INIT_HLIST_HEAD(&empty_rp);
|
||||
kretprobe_hash_lock(current, &head, &flags);
|
||||
|
||||
/*
|
||||
* It is possible to have multiple instances associated with a given
|
||||
* task either because multiple functions in the call path have
|
||||
* a return probe installed on them, and/or more than one return
|
||||
* probe was registered for a target function.
|
||||
*
|
||||
* We can handle this because:
|
||||
* - instances are always inserted at the head of the list
|
||||
* - when multiple return probes are registered for the same
|
||||
* function, the first instance's ret_addr will point to the
|
||||
* real return address, and all the rest will point to
|
||||
* kretprobe_trampoline
|
||||
*/
|
||||
hlist_for_each_entry_safe(ri, tmp, head, hlist) {
|
||||
if (ri->task != current)
|
||||
/* another task is sharing our hash bucket */
|
||||
continue;
|
||||
|
||||
orig_ret_address = (unsigned long)ri->ret_addr;
|
||||
|
||||
if (orig_ret_address != trampoline_address)
|
||||
/*
|
||||
* This is the real return address. Any other
|
||||
* instances associated with this task are for
|
||||
* other calls deeper on the call stack
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
kretprobe_assert(ri, orig_ret_address, trampoline_address);
|
||||
|
||||
correct_ret_addr = ri->ret_addr;
|
||||
hlist_for_each_entry_safe(ri, tmp, head, hlist) {
|
||||
if (ri->task != current)
|
||||
/* another task is sharing our hash bucket */
|
||||
continue;
|
||||
|
||||
orig_ret_address = (unsigned long)ri->ret_addr;
|
||||
if (ri->rp && ri->rp->handler) {
|
||||
__this_cpu_write(current_kprobe, &ri->rp->kp);
|
||||
get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE;
|
||||
ri->ret_addr = correct_ret_addr;
|
||||
ri->rp->handler(ri, regs);
|
||||
__this_cpu_write(current_kprobe, NULL);
|
||||
}
|
||||
|
||||
recycle_rp_inst(ri, &empty_rp);
|
||||
|
||||
if (orig_ret_address != trampoline_address)
|
||||
/*
|
||||
* This is the real return address. Any other
|
||||
* instances associated with this task are for
|
||||
* other calls deeper on the call stack
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
kretprobe_hash_unlock(current, &flags);
|
||||
|
||||
hlist_for_each_entry_safe(ri, tmp, &empty_rp, hlist) {
|
||||
hlist_del(&ri->hlist);
|
||||
kfree(ri);
|
||||
}
|
||||
instruction_pointer_set(regs, orig_ret_address);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
ri->ret_addr = (kprobe_opcode_t *)regs->gr[2];
|
||||
|
||||
/* Replace the return addr with trampoline addr. */
|
||||
regs->gr[2] = (unsigned long)trampoline_p.addr;
|
||||
}
|
||||
|
||||
int __kprobes arch_trampoline_kprobe(struct kprobe *p)
|
||||
{
|
||||
return p->addr == trampoline_p.addr;
|
||||
}
|
||||
bool arch_kprobe_on_func_entry(unsigned long offset)
|
||||
{
|
||||
return !offset;
|
||||
}
|
||||
|
||||
int __init arch_init_kprobes(void)
|
||||
{
|
||||
trampoline_p.addr = (kprobe_opcode_t *)
|
||||
dereference_function_descriptor(kretprobe_trampoline);
|
||||
return register_kprobe(&trampoline_p);
|
||||
}
|
@@ -311,39 +311,6 @@ fdsync:
|
||||
nop
|
||||
ENDPROC_CFI(flush_data_cache_local)
|
||||
|
||||
/* Macros to serialize TLB purge operations on SMP. */
|
||||
|
||||
.macro tlb_lock la,flags,tmp
|
||||
#ifdef CONFIG_SMP
|
||||
98:
|
||||
#if __PA_LDCW_ALIGNMENT > 4
|
||||
load32 pa_tlb_lock + __PA_LDCW_ALIGNMENT-1, \la
|
||||
depi 0,31,__PA_LDCW_ALIGN_ORDER, \la
|
||||
#else
|
||||
load32 pa_tlb_lock, \la
|
||||
#endif
|
||||
rsm PSW_SM_I,\flags
|
||||
1: LDCW 0(\la),\tmp
|
||||
cmpib,<>,n 0,\tmp,3f
|
||||
2: ldw 0(\la),\tmp
|
||||
cmpb,<> %r0,\tmp,1b
|
||||
nop
|
||||
b,n 2b
|
||||
3:
|
||||
99: ALTERNATIVE(98b, 99b, ALT_COND_NO_SMP, INSN_NOP)
|
||||
#endif
|
||||
.endm
|
||||
|
||||
.macro tlb_unlock la,flags,tmp
|
||||
#ifdef CONFIG_SMP
|
||||
98: ldi 1,\tmp
|
||||
sync
|
||||
stw \tmp,0(\la)
|
||||
mtsm \flags
|
||||
99: ALTERNATIVE(98b, 99b, ALT_COND_NO_SMP, INSN_NOP)
|
||||
#endif
|
||||
.endm
|
||||
|
||||
/* Clear page using kernel mapping. */
|
||||
|
||||
ENTRY_CFI(clear_page_asm)
|
||||
@@ -601,10 +568,8 @@ ENTRY_CFI(copy_user_page_asm)
|
||||
pdtlb,l %r0(%r28)
|
||||
pdtlb,l %r0(%r29)
|
||||
#else
|
||||
tlb_lock %r20,%r21,%r22
|
||||
0: pdtlb %r0(%r28)
|
||||
1: pdtlb %r0(%r29)
|
||||
tlb_unlock %r20,%r21,%r22
|
||||
ALTERNATIVE(0b, 0b+4, ALT_COND_NO_SMP, INSN_PxTLB)
|
||||
ALTERNATIVE(1b, 1b+4, ALT_COND_NO_SMP, INSN_PxTLB)
|
||||
#endif
|
||||
@@ -743,9 +708,7 @@ ENTRY_CFI(clear_user_page_asm)
|
||||
#ifdef CONFIG_PA20
|
||||
pdtlb,l %r0(%r28)
|
||||
#else
|
||||
tlb_lock %r20,%r21,%r22
|
||||
0: pdtlb %r0(%r28)
|
||||
tlb_unlock %r20,%r21,%r22
|
||||
ALTERNATIVE(0b, 0b+4, ALT_COND_NO_SMP, INSN_PxTLB)
|
||||
#endif
|
||||
|
||||
@@ -821,9 +784,7 @@ ENTRY_CFI(flush_dcache_page_asm)
|
||||
#ifdef CONFIG_PA20
|
||||
pdtlb,l %r0(%r28)
|
||||
#else
|
||||
tlb_lock %r20,%r21,%r22
|
||||
0: pdtlb %r0(%r28)
|
||||
tlb_unlock %r20,%r21,%r22
|
||||
ALTERNATIVE(0b, 0b+4, ALT_COND_NO_SMP, INSN_PxTLB)
|
||||
#endif
|
||||
|
||||
@@ -882,9 +843,7 @@ ENTRY_CFI(purge_dcache_page_asm)
|
||||
#ifdef CONFIG_PA20
|
||||
pdtlb,l %r0(%r28)
|
||||
#else
|
||||
tlb_lock %r20,%r21,%r22
|
||||
0: pdtlb %r0(%r28)
|
||||
tlb_unlock %r20,%r21,%r22
|
||||
ALTERNATIVE(0b, 0b+4, ALT_COND_NO_SMP, INSN_PxTLB)
|
||||
#endif
|
||||
|
||||
@@ -948,10 +907,8 @@ ENTRY_CFI(flush_icache_page_asm)
|
||||
1: pitlb,l %r0(%sr4,%r28)
|
||||
ALTERNATIVE(1b, 1b+4, ALT_COND_NO_SPLIT_TLB, INSN_NOP)
|
||||
#else
|
||||
tlb_lock %r20,%r21,%r22
|
||||
0: pdtlb %r0(%r28)
|
||||
1: pitlb %r0(%sr4,%r28)
|
||||
tlb_unlock %r20,%r21,%r22
|
||||
ALTERNATIVE(0b, 0b+4, ALT_COND_NO_SMP, INSN_PxTLB)
|
||||
ALTERNATIVE(1b, 1b+4, ALT_COND_NO_SMP, INSN_PxTLB)
|
||||
ALTERNATIVE(1b, 1b+4, ALT_COND_NO_SPLIT_TLB, INSN_NOP)
|
||||
|
@@ -138,12 +138,6 @@ extern void $$dyncall(void);
|
||||
EXPORT_SYMBOL($$dyncall);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DISCONTIGMEM
|
||||
#include <asm/mmzone.h>
|
||||
EXPORT_SYMBOL(node_data);
|
||||
EXPORT_SYMBOL(pfnnid_map);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FUNCTION_TRACER
|
||||
extern void _mcount(void);
|
||||
EXPORT_SYMBOL(_mcount);
|
||||
|
77
arch/parisc/kernel/patch.c
Normal file
77
arch/parisc/kernel/patch.c
Normal file
@@ -0,0 +1,77 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* functions to patch RO kernel text during runtime
|
||||
*
|
||||
* Copyright (c) 2019 Sven Schnelle <svens@stackframe.org>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/stop_machine.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/fixmap.h>
|
||||
#include <asm/patch.h>
|
||||
|
||||
struct patch {
|
||||
void *addr;
|
||||
unsigned int insn;
|
||||
};
|
||||
|
||||
static void __kprobes *patch_map(void *addr, int fixmap)
|
||||
{
|
||||
unsigned long uintaddr = (uintptr_t) addr;
|
||||
bool module = !core_kernel_text(uintaddr);
|
||||
struct page *page;
|
||||
|
||||
if (module && IS_ENABLED(CONFIG_STRICT_MODULE_RWX))
|
||||
page = vmalloc_to_page(addr);
|
||||
else if (!module && IS_ENABLED(CONFIG_STRICT_KERNEL_RWX))
|
||||
page = virt_to_page(addr);
|
||||
else
|
||||
return addr;
|
||||
|
||||
set_fixmap(fixmap, page_to_phys(page));
|
||||
|
||||
return (void *) (__fix_to_virt(fixmap) + (uintaddr & ~PAGE_MASK));
|
||||
}
|
||||
|
||||
static void __kprobes patch_unmap(int fixmap)
|
||||
{
|
||||
clear_fixmap(fixmap);
|
||||
}
|
||||
|
||||
void __kprobes __patch_text(void *addr, unsigned int insn)
|
||||
{
|
||||
void *waddr = addr;
|
||||
int size;
|
||||
|
||||
waddr = patch_map(addr, FIX_TEXT_POKE0);
|
||||
*(u32 *)waddr = insn;
|
||||
size = sizeof(u32);
|
||||
flush_kernel_vmap_range(waddr, size);
|
||||
patch_unmap(FIX_TEXT_POKE0);
|
||||
flush_icache_range((uintptr_t)(addr),
|
||||
(uintptr_t)(addr) + size);
|
||||
}
|
||||
|
||||
static int __kprobes patch_text_stop_machine(void *data)
|
||||
{
|
||||
struct patch *patch = data;
|
||||
|
||||
__patch_text(patch->addr, patch->insn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __kprobes patch_text(void *addr, unsigned int insn)
|
||||
{
|
||||
struct patch patch = {
|
||||
.addr = addr,
|
||||
.insn = insn,
|
||||
};
|
||||
|
||||
stop_machine_cpuslocked(patch_text_stop_machine, &patch, NULL);
|
||||
}
|
@@ -193,6 +193,7 @@ int dump_task_fpu (struct task_struct *tsk, elf_fpregset_t *r)
|
||||
*/
|
||||
|
||||
int running_on_qemu __read_mostly;
|
||||
EXPORT_SYMBOL(running_on_qemu);
|
||||
|
||||
void __cpuidle arch_cpu_idle_dead(void)
|
||||
{
|
||||
|
@@ -305,7 +305,8 @@ void __init collect_boot_cpu_data(void)
|
||||
|
||||
if (pdc_model_platform_info(orig_prod_num, current_prod_num, serial_no) == PDC_OK) {
|
||||
printk(KERN_INFO "product %s, original product %s, S/N: %s\n",
|
||||
current_prod_num, orig_prod_num, serial_no);
|
||||
current_prod_num[0] ? current_prod_num : "n/a",
|
||||
orig_prod_num, serial_no);
|
||||
add_device_randomness(orig_prod_num, strlen(orig_prod_num));
|
||||
add_device_randomness(current_prod_num, strlen(current_prod_num));
|
||||
add_device_randomness(serial_no, strlen(serial_no));
|
||||
|
@@ -789,3 +789,38 @@ const char *regs_query_register_name(unsigned int offset)
|
||||
return roff->name;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* regs_within_kernel_stack() - check the address in the stack
|
||||
* @regs: pt_regs which contains kernel stack pointer.
|
||||
* @addr: address which is checked.
|
||||
*
|
||||
* regs_within_kernel_stack() checks @addr is within the kernel stack page(s).
|
||||
* If @addr is within the kernel stack, it returns true. If not, returns false.
|
||||
*/
|
||||
int regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
|
||||
{
|
||||
return ((addr & ~(THREAD_SIZE - 1)) ==
|
||||
(kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* regs_get_kernel_stack_nth() - get Nth entry of the stack
|
||||
* @regs: pt_regs which contains kernel stack pointer.
|
||||
* @n: stack entry number.
|
||||
*
|
||||
* regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which
|
||||
* is specified by @regs. If the @n th entry is NOT in the kernel stack,
|
||||
* this returns 0.
|
||||
*/
|
||||
unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n)
|
||||
{
|
||||
unsigned long *addr = (unsigned long *)kernel_stack_pointer(regs);
|
||||
|
||||
addr -= n;
|
||||
|
||||
if (!regs_within_kernel_stack(regs, (unsigned long)addr))
|
||||
return 0;
|
||||
|
||||
return *addr;
|
||||
}
|
||||
|
@@ -343,6 +343,12 @@ static int __init parisc_init(void)
|
||||
boot_cpu_data.cpu_hz / 1000000,
|
||||
boot_cpu_data.cpu_hz % 1000000 );
|
||||
|
||||
#if defined(CONFIG_64BIT) && defined(CONFIG_SMP)
|
||||
/* Don't serialize TLB flushes if we run on one CPU only. */
|
||||
if (num_online_cpus() == 1)
|
||||
pa_serialize_tlb_flushes = 0;
|
||||
#endif
|
||||
|
||||
apply_alternatives_all();
|
||||
parisc_setup_cache_timing();
|
||||
|
||||
|
@@ -86,7 +86,8 @@ static unsigned long mmap_upper_limit(struct rlimit *rlim_stack)
|
||||
stack_base = STACK_SIZE_MAX;
|
||||
|
||||
/* Add space for stack randomization. */
|
||||
stack_base += (STACK_RND_MASK << PAGE_SHIFT);
|
||||
if (current->flags & PF_RANDOMIZE)
|
||||
stack_base += (STACK_RND_MASK << PAGE_SHIFT);
|
||||
|
||||
return PAGE_ALIGN(STACK_TOP - stack_base);
|
||||
}
|
||||
|
@@ -48,7 +48,7 @@ registers).
|
||||
*/
|
||||
#define KILL_INSN break 0,0
|
||||
|
||||
.level LEVEL
|
||||
.level PA_ASM_LEVEL
|
||||
|
||||
.text
|
||||
|
||||
@@ -640,7 +640,9 @@ cas_action:
|
||||
sub,<> %r28, %r25, %r0
|
||||
2: stw %r24, 0(%r26)
|
||||
/* Free lock */
|
||||
sync
|
||||
#ifdef CONFIG_SMP
|
||||
LDCW 0(%sr2,%r20), %r1 /* Barrier */
|
||||
#endif
|
||||
stw %r20, 0(%sr2,%r20)
|
||||
#if ENABLE_LWS_DEBUG
|
||||
/* Clear thread register indicator */
|
||||
@@ -655,7 +657,9 @@ cas_action:
|
||||
3:
|
||||
/* Error occurred on load or store */
|
||||
/* Free lock */
|
||||
sync
|
||||
#ifdef CONFIG_SMP
|
||||
LDCW 0(%sr2,%r20), %r1 /* Barrier */
|
||||
#endif
|
||||
stw %r20, 0(%sr2,%r20)
|
||||
#if ENABLE_LWS_DEBUG
|
||||
stw %r0, 4(%sr2,%r20)
|
||||
@@ -857,7 +861,9 @@ cas2_action:
|
||||
|
||||
cas2_end:
|
||||
/* Free lock */
|
||||
sync
|
||||
#ifdef CONFIG_SMP
|
||||
LDCW 0(%sr2,%r20), %r1 /* Barrier */
|
||||
#endif
|
||||
stw %r20, 0(%sr2,%r20)
|
||||
/* Enable interrupts */
|
||||
ssm PSW_SM_I, %r0
|
||||
@@ -868,7 +874,9 @@ cas2_end:
|
||||
22:
|
||||
/* Error occurred on load or store */
|
||||
/* Free lock */
|
||||
sync
|
||||
#ifdef CONFIG_SMP
|
||||
LDCW 0(%sr2,%r20), %r1 /* Barrier */
|
||||
#endif
|
||||
stw %r20, 0(%sr2,%r20)
|
||||
ssm PSW_SM_I, %r0
|
||||
ldo 1(%r0),%r28
|
||||
|
@@ -42,6 +42,8 @@
|
||||
#include <asm/unwind.h>
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <linux/kgdb.h>
|
||||
#include <linux/kprobes.h>
|
||||
|
||||
#include "../math-emu/math-emu.h" /* for handle_fpe() */
|
||||
|
||||
@@ -293,6 +295,22 @@ static void handle_break(struct pt_regs *regs)
|
||||
(tt == BUG_TRAP_TYPE_NONE) ? 9 : 0);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
if (unlikely(iir == PARISC_KPROBES_BREAK_INSN)) {
|
||||
parisc_kprobe_break_handler(regs);
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KGDB
|
||||
if (unlikely(iir == PARISC_KGDB_COMPILED_BREAK_INSN ||
|
||||
iir == PARISC_KGDB_BREAK_INSN)) {
|
||||
kgdb_handle_exception(9, SIGTRAP, 0, regs);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (unlikely(iir != GDB_BREAK_INSN))
|
||||
parisc_printk_ratelimited(0, regs,
|
||||
KERN_DEBUG "break %d,%d: pid=%d command='%s'\n",
|
||||
@@ -518,6 +536,19 @@ void notrace handle_interruption(int code, struct pt_regs *regs)
|
||||
case 3:
|
||||
/* 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);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (user_space(regs))
|
||||
handle_gdb_break(regs, TRAP_TRACE);
|
||||
/* else this must be the start of a syscall - just let it run */
|
||||
|
@@ -18,6 +18,9 @@
|
||||
*(.data..vm0.pgd) \
|
||||
*(.data..vm0.pte)
|
||||
|
||||
/* No __ro_after_init data in the .rodata section - which will always be ro */
|
||||
#define RO_AFTER_INIT_DATA
|
||||
|
||||
#include <asm-generic/vmlinux.lds.h>
|
||||
|
||||
/* needed for the processor specific cache alignment size */
|
||||
|
Reference in New Issue
Block a user