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:
Linus Torvalds
2019-05-07 19:34:17 -07:00
50 changed files with 1258 additions and 295 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 */

View File

@@ -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
}

View 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
View 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;
}

View 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);
}

View File

@@ -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)

View File

@@ -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);

View 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);
}

View File

@@ -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)
{

View File

@@ -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));

View File

@@ -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;
}

View File

@@ -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();

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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 */

View File

@@ -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 */