Skip to content

Commit 15e64ef

Browse files
committed
parisc: Add lightweight spinlock checks
Add a lightweight spinlock check which uses only two instructions per spinlock call. It detects if a spinlock has been trashed by some memory corruption and then halts the kernel. It will not detect uninitialized spinlocks, for which CONFIG_DEBUG_SPINLOCK needs to be enabled. This lightweight spinlock check shouldn't influence runtime, so it's safe to enable it by default. The __ARCH_SPIN_LOCK_UNLOCKED_VAL constant has been choosen small enough to be able to be loaded by one LDI assembler statement. Signed-off-by: Helge Deller <[email protected]>
1 parent b6405f0 commit 15e64ef

File tree

4 files changed

+61
-7
lines changed

4 files changed

+61
-7
lines changed

arch/parisc/Kconfig.debug

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,12 @@
11
# SPDX-License-Identifier: GPL-2.0
2+
#
3+
config LIGHTWEIGHT_SPINLOCK_CHECK
4+
bool "Enable lightweight spinlock checks"
5+
depends on SMP && !DEBUG_SPINLOCK
6+
default y
7+
help
8+
Add checks with low performance impact to the spinlock functions
9+
to catch memory overwrites at runtime. For more advanced
10+
spinlock debugging you should choose the DEBUG_SPINLOCK option
11+
which will detect unitialized spinlocks too.
12+
If unsure say Y here.

arch/parisc/include/asm/spinlock.h

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,45 @@
77
#include <asm/processor.h>
88
#include <asm/spinlock_types.h>
99

10+
#define SPINLOCK_BREAK_INSN 0x0000c006 /* break 6,6 */
11+
12+
static inline void arch_spin_val_check(int lock_val)
13+
{
14+
if (IS_ENABLED(CONFIG_LIGHTWEIGHT_SPINLOCK_CHECK))
15+
asm volatile( "andcm,= %0,%1,%%r0\n"
16+
".word %2\n"
17+
: : "r" (lock_val), "r" (__ARCH_SPIN_LOCK_UNLOCKED_VAL),
18+
"i" (SPINLOCK_BREAK_INSN));
19+
}
20+
1021
static inline int arch_spin_is_locked(arch_spinlock_t *x)
1122
{
12-
volatile unsigned int *a = __ldcw_align(x);
13-
return READ_ONCE(*a) == 0;
23+
volatile unsigned int *a;
24+
int lock_val;
25+
26+
a = __ldcw_align(x);
27+
lock_val = READ_ONCE(*a);
28+
arch_spin_val_check(lock_val);
29+
return (lock_val == 0);
1430
}
1531

1632
static inline void arch_spin_lock(arch_spinlock_t *x)
1733
{
1834
volatile unsigned int *a;
1935

2036
a = __ldcw_align(x);
21-
while (__ldcw(a) == 0)
37+
do {
38+
int lock_val_old;
39+
40+
lock_val_old = __ldcw(a);
41+
arch_spin_val_check(lock_val_old);
42+
if (lock_val_old)
43+
return; /* got lock */
44+
45+
/* wait until we should try to get lock again */
2246
while (*a == 0)
2347
continue;
48+
} while (1);
2449
}
2550

2651
static inline void arch_spin_unlock(arch_spinlock_t *x)
@@ -29,15 +54,19 @@ static inline void arch_spin_unlock(arch_spinlock_t *x)
2954

3055
a = __ldcw_align(x);
3156
/* Release with ordered store. */
32-
__asm__ __volatile__("stw,ma %0,0(%1)" : : "r"(1), "r"(a) : "memory");
57+
__asm__ __volatile__("stw,ma %0,0(%1)"
58+
: : "r"(__ARCH_SPIN_LOCK_UNLOCKED_VAL), "r"(a) : "memory");
3359
}
3460

3561
static inline int arch_spin_trylock(arch_spinlock_t *x)
3662
{
3763
volatile unsigned int *a;
64+
int lock_val;
3865

3966
a = __ldcw_align(x);
40-
return __ldcw(a) != 0;
67+
lock_val = __ldcw(a);
68+
arch_spin_val_check(lock_val);
69+
return lock_val != 0;
4170
}
4271

4372
/*

arch/parisc/include/asm/spinlock_types.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@
22
#ifndef __ASM_SPINLOCK_TYPES_H
33
#define __ASM_SPINLOCK_TYPES_H
44

5+
#define __ARCH_SPIN_LOCK_UNLOCKED_VAL 0x1a46
6+
57
typedef struct {
68
#ifdef CONFIG_PA20
79
volatile unsigned int slock;
8-
# define __ARCH_SPIN_LOCK_UNLOCKED { 1 }
10+
# define __ARCH_SPIN_LOCK_UNLOCKED { __ARCH_SPIN_LOCK_UNLOCKED_VAL }
911
#else
1012
volatile unsigned int lock[4];
11-
# define __ARCH_SPIN_LOCK_UNLOCKED { { 1, 1, 1, 1 } }
13+
# define __ARCH_SPIN_LOCK_UNLOCKED \
14+
{ { __ARCH_SPIN_LOCK_UNLOCKED_VAL, __ARCH_SPIN_LOCK_UNLOCKED_VAL, \
15+
__ARCH_SPIN_LOCK_UNLOCKED_VAL, __ARCH_SPIN_LOCK_UNLOCKED_VAL } }
1216
#endif
1317
} arch_spinlock_t;
1418

arch/parisc/kernel/traps.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@
4747
#include <linux/kgdb.h>
4848
#include <linux/kprobes.h>
4949

50+
#if defined(CONFIG_LIGHTWEIGHT_SPINLOCK_CHECK)
51+
#include <asm/spinlock.h>
52+
#endif
53+
5054
#include "../math-emu/math-emu.h" /* for handle_fpe() */
5155

5256
static void parisc_show_stack(struct task_struct *task,
@@ -309,6 +313,12 @@ static void handle_break(struct pt_regs *regs)
309313
}
310314
#endif
311315

316+
#ifdef CONFIG_LIGHTWEIGHT_SPINLOCK_CHECK
317+
if ((iir == SPINLOCK_BREAK_INSN) && !user_mode(regs)) {
318+
die_if_kernel("Spinlock was trashed", regs, 1);
319+
}
320+
#endif
321+
312322
if (unlikely(iir != GDB_BREAK_INSN))
313323
parisc_printk_ratelimited(0, regs,
314324
KERN_DEBUG "break %d,%d: pid=%d command='%s'\n",

0 commit comments

Comments
 (0)