Skip to content

Commit 3b051e8

Browse files
svens-s390Vasily Gorbik
authored andcommitted
s390: add support for BEAR enhancement facility
The Breaking-Event-Address-Register (BEAR) stores the address of the last breaking event instruction. Breaking events are usually instructions that change the program flow - for example branches, and instructions that modify the address in the PSW like lpswe. This is useful for debugging wild branches, because one could easily figure out where the wild branch was originating from. What is problematic is that lpswe is considered a breaking event, and therefore overwrites BEAR on kernel exit. The BEAR enhancement facility adds new instructions that allow to save/restore BEAR and also an lpswey instruction that doesn't cause a breaking event. So we can save BEAR on kernel entry and restore it on exit to user space. Signed-off-by: Sven Schnelle <[email protected]> Reviewed-by: Heiko Carstens <[email protected]> Signed-off-by: Vasily Gorbik <[email protected]>
1 parent 5d17d4e commit 3b051e8

File tree

11 files changed

+87
-24
lines changed

11 files changed

+87
-24
lines changed

arch/s390/include/asm/cpu.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#ifndef __ASSEMBLY__
1313

1414
#include <linux/types.h>
15+
#include <linux/jump_label.h>
1516

1617
struct cpuid
1718
{
@@ -21,5 +22,7 @@ struct cpuid
2122
unsigned int unused : 16;
2223
} __attribute__ ((packed, aligned(8)));
2324

25+
DECLARE_STATIC_KEY_FALSE(cpu_has_bear);
26+
2427
#endif /* __ASSEMBLY__ */
2528
#endif /* _ASM_S390_CPU_H */

arch/s390/include/asm/lowcore.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,10 @@ struct lowcore {
9393
psw_t return_psw; /* 0x0290 */
9494
psw_t return_mcck_psw; /* 0x02a0 */
9595

96+
__u64 last_break; /* 0x02b0 */
97+
9698
/* CPU accounting and timing values. */
97-
__u64 sys_enter_timer; /* 0x02b0 */
98-
__u8 pad_0x02b8[0x02c0-0x02b8]; /* 0x02b8 */
99+
__u64 sys_enter_timer; /* 0x02b8 */
99100
__u64 mcck_enter_timer; /* 0x02c0 */
100101
__u64 exit_timer; /* 0x02c8 */
101102
__u64 user_timer; /* 0x02d0 */
@@ -188,7 +189,7 @@ struct lowcore {
188189
__u32 tod_progreg_save_area; /* 0x1324 */
189190
__u32 cpu_timer_save_area[2]; /* 0x1328 */
190191
__u32 clock_comp_save_area[2]; /* 0x1330 */
191-
__u8 pad_0x1338[0x1340-0x1338]; /* 0x1338 */
192+
__u64 last_break_save_area; /* 0x1338 */
192193
__u32 access_regs_save_area[16]; /* 0x1340 */
193194
__u64 cregs_save_area[16]; /* 0x1380 */
194195
__u8 pad_0x1400[0x1800-0x1400]; /* 0x1400 */

arch/s390/kernel/asm-offsets.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ int main(void)
3535
OFFSET(__PT_ORIG_GPR2, pt_regs, orig_gpr2);
3636
OFFSET(__PT_FLAGS, pt_regs, flags);
3737
OFFSET(__PT_CR1, pt_regs, cr1);
38+
OFFSET(__PT_LAST_BREAK, pt_regs, last_break);
3839
DEFINE(__PT_SIZE, sizeof(struct pt_regs));
3940
BLANK();
4041
/* stack_frame offsets */
@@ -127,6 +128,7 @@ int main(void)
127128
OFFSET(__LC_PREEMPT_COUNT, lowcore, preempt_count);
128129
OFFSET(__LC_GMAP, lowcore, gmap);
129130
OFFSET(__LC_BR_R1, lowcore, br_r1_trampoline);
131+
OFFSET(__LC_LAST_BREAK, lowcore, last_break);
130132
/* software defined ABI-relevant lowcore locations 0xe00 - 0xe20 */
131133
OFFSET(__LC_DUMP_REIPL, lowcore, ipib);
132134
/* hardware defined lowcore locations 0x1000 - 0x18ff */
@@ -140,6 +142,7 @@ int main(void)
140142
OFFSET(__LC_TOD_PROGREG_SAVE_AREA, lowcore, tod_progreg_save_area);
141143
OFFSET(__LC_CPU_TIMER_SAVE_AREA, lowcore, cpu_timer_save_area);
142144
OFFSET(__LC_CLOCK_COMP_SAVE_AREA, lowcore, clock_comp_save_area);
145+
OFFSET(__LC_LAST_BREAK_SAVE_AREA, lowcore, last_break_save_area);
143146
OFFSET(__LC_AREGS_SAVE_AREA, lowcore, access_regs_save_area);
144147
OFFSET(__LC_CREGS_SAVE_AREA, lowcore, cregs_save_area);
145148
OFFSET(__LC_PGM_TDB, lowcore, pgm_tdb);

arch/s390/kernel/entry.S

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,22 @@ STACK_INIT = STACK_SIZE - STACK_FRAME_OVERHEAD - __PT_SIZE
5252

5353
_LPP_OFFSET = __LC_LPP
5454

55+
.macro STBEAR address
56+
ALTERNATIVE "", ".insn s,0xb2010000,\address", 193
57+
.endm
58+
59+
.macro LBEAR address
60+
ALTERNATIVE "", ".insn s,0xb2000000,\address", 193
61+
.endm
62+
63+
.macro LPSWEY address,lpswe
64+
ALTERNATIVE "b \lpswe", ".insn siy,0xeb0000000071,\address,0", 193
65+
.endm
66+
67+
.macro MBEAR reg
68+
ALTERNATIVE "", __stringify(mvc __PT_LAST_BREAK(8,\reg),__LC_LAST_BREAK), 193
69+
.endm
70+
5571
.macro CHECK_STACK savearea
5672
#ifdef CONFIG_CHECK_STACK
5773
tml %r15,STACK_SIZE - CONFIG_STACK_GUARD
@@ -302,6 +318,7 @@ ENTRY(system_call)
302318
BPOFF
303319
lghi %r14,0
304320
.Lsysc_per:
321+
STBEAR __LC_LAST_BREAK
305322
lctlg %c1,%c1,__LC_KERNEL_ASCE
306323
lg %r12,__LC_CURRENT
307324
lg %r15,__LC_KERNEL_STACK
@@ -321,14 +338,16 @@ ENTRY(system_call)
321338
xgr %r11,%r11
322339
la %r2,STACK_FRAME_OVERHEAD(%r15) # pointer to pt_regs
323340
mvc __PT_R8(64,%r2),__LC_SAVE_AREA_SYNC
341+
MBEAR %r2
324342
lgr %r3,%r14
325343
brasl %r14,__do_syscall
326344
lctlg %c1,%c1,__LC_USER_ASCE
327345
mvc __LC_RETURN_PSW(16),STACK_FRAME_OVERHEAD+__PT_PSW(%r15)
328346
BPEXIT __TI_flags(%r12),_TIF_ISOLATE_BP
347+
LBEAR STACK_FRAME_OVERHEAD+__PT_LAST_BREAK(%r15)
329348
lmg %r0,%r15,STACK_FRAME_OVERHEAD+__PT_R0(%r15)
330349
stpt __LC_EXIT_TIMER
331-
b __LC_RETURN_LPSWE
350+
LPSWEY __LC_RETURN_PSW,__LC_RETURN_LPSWE
332351
ENDPROC(system_call)
333352

334353
#
@@ -340,9 +359,10 @@ ENTRY(ret_from_fork)
340359
lctlg %c1,%c1,__LC_USER_ASCE
341360
mvc __LC_RETURN_PSW(16),STACK_FRAME_OVERHEAD+__PT_PSW(%r15)
342361
BPEXIT __TI_flags(%r12),_TIF_ISOLATE_BP
362+
LBEAR STACK_FRAME_OVERHEAD+__PT_LAST_BREAK(%r15)
343363
lmg %r0,%r15,STACK_FRAME_OVERHEAD+__PT_R0(%r15)
344364
stpt __LC_EXIT_TIMER
345-
b __LC_RETURN_LPSWE
365+
LPSWEY __LC_RETURN_PSW,__LC_RETURN_LPSWE
346366
ENDPROC(ret_from_fork)
347367

348368
/*
@@ -382,6 +402,7 @@ ENTRY(pgm_check_handler)
382402
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
383403
stmg %r0,%r7,__PT_R0(%r11)
384404
mvc __PT_R8(64,%r11),__LC_SAVE_AREA_SYNC
405+
mvc __PT_LAST_BREAK(8,%r11),__LC_PGM_LAST_BREAK
385406
stmg %r8,%r9,__PT_PSW(%r11)
386407

387408
# clear user controlled registers to prevent speculative use
@@ -401,8 +422,9 @@ ENTRY(pgm_check_handler)
401422
stpt __LC_EXIT_TIMER
402423
.Lpgm_exit_kernel:
403424
mvc __LC_RETURN_PSW(16),STACK_FRAME_OVERHEAD+__PT_PSW(%r15)
425+
LBEAR STACK_FRAME_OVERHEAD+__PT_LAST_BREAK(%r15)
404426
lmg %r0,%r15,STACK_FRAME_OVERHEAD+__PT_R0(%r15)
405-
b __LC_RETURN_LPSWE
427+
LPSWEY __LC_RETURN_PSW,__LC_RETURN_LPSWE
406428

407429
#
408430
# single stepped system call
@@ -412,7 +434,8 @@ ENTRY(pgm_check_handler)
412434
larl %r14,.Lsysc_per
413435
stg %r14,__LC_RETURN_PSW+8
414436
lghi %r14,1
415-
lpswe __LC_RETURN_PSW # branch to .Lsysc_per
437+
LBEAR __LC_PGM_LAST_BREAK
438+
LPSWEY __LC_RETURN_PSW,__LC_RETURN_LPSWE # branch to .Lsysc_per
416439
ENDPROC(pgm_check_handler)
417440

418441
/*
@@ -422,6 +445,7 @@ ENDPROC(pgm_check_handler)
422445
ENTRY(\name)
423446
STCK __LC_INT_CLOCK
424447
stpt __LC_SYS_ENTER_TIMER
448+
STBEAR __LC_LAST_BREAK
425449
BPOFF
426450
stmg %r8,%r15,__LC_SAVE_AREA_ASYNC
427451
lg %r12,__LC_CURRENT
@@ -453,6 +477,7 @@ ENTRY(\name)
453477
xgr %r10,%r10
454478
xc __PT_FLAGS(8,%r11),__PT_FLAGS(%r11)
455479
mvc __PT_R8(64,%r11),__LC_SAVE_AREA_ASYNC
480+
MBEAR %r11
456481
stmg %r8,%r9,__PT_PSW(%r11)
457482
tm %r8,0x0001 # coming from user space?
458483
jno 1f
@@ -465,8 +490,9 @@ ENTRY(\name)
465490
lctlg %c1,%c1,__LC_USER_ASCE
466491
BPEXIT __TI_flags(%r12),_TIF_ISOLATE_BP
467492
stpt __LC_EXIT_TIMER
468-
2: lmg %r0,%r15,__PT_R0(%r11)
469-
b __LC_RETURN_LPSWE
493+
2: LBEAR __PT_LAST_BREAK(%r11)
494+
lmg %r0,%r15,__PT_R0(%r11)
495+
LPSWEY __LC_RETURN_PSW,__LC_RETURN_LPSWE
470496
ENDPROC(\name)
471497
.endm
472498

@@ -505,6 +531,7 @@ ENTRY(mcck_int_handler)
505531
BPOFF
506532
la %r1,4095 # validate r1
507533
spt __LC_CPU_TIMER_SAVE_AREA-4095(%r1) # validate cpu timer
534+
LBEAR __LC_LAST_BREAK_SAVE_AREA-4095(%r1) # validate bear
508535
lmg %r0,%r15,__LC_GPREGS_SAVE_AREA-4095(%r1)# validate gprs
509536
lg %r12,__LC_CURRENT
510537
lmg %r8,%r9,__LC_MCK_OLD_PSW
@@ -591,8 +618,10 @@ ENTRY(mcck_int_handler)
591618
jno 0f
592619
BPEXIT __TI_flags(%r12),_TIF_ISOLATE_BP
593620
stpt __LC_EXIT_TIMER
594-
0: lmg %r11,%r15,__PT_R11(%r11)
595-
b __LC_RETURN_MCCK_LPSWE
621+
0: ALTERNATIVE "", __stringify(lghi %r12,__LC_LAST_BREAK_SAVE_AREA),193
622+
LBEAR 0(%r12)
623+
lmg %r11,%r15,__PT_R11(%r11)
624+
LPSWEY __LC_RETURN_MCCK_PSW,__LC_RETURN_MCCK_LPSWE
596625

597626
.Lmcck_panic:
598627
/*

arch/s390/kernel/irq.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,11 @@ void noinstr do_io_irq(struct pt_regs *regs)
140140

141141
irq_enter();
142142

143-
if (user_mode(regs))
143+
if (user_mode(regs)) {
144144
update_timer_sys();
145+
if (static_branch_likely(&cpu_has_bear))
146+
current->thread.last_break = regs->last_break;
147+
}
145148

146149
from_idle = !user_mode(regs) && regs->psw.addr == (unsigned long)psw_idle_exit;
147150
if (from_idle)
@@ -171,8 +174,11 @@ void noinstr do_ext_irq(struct pt_regs *regs)
171174

172175
irq_enter();
173176

174-
if (user_mode(regs))
177+
if (user_mode(regs)) {
175178
update_timer_sys();
179+
if (static_branch_likely(&cpu_has_bear))
180+
current->thread.last_break = regs->last_break;
181+
}
176182

177183
regs->int_code = S390_lowcore.ext_int_code_addr;
178184
regs->int_parm = S390_lowcore.ext_params;

arch/s390/kernel/process.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp,
141141
frame->childregs.gprs[10] = arg;
142142
frame->childregs.gprs[11] = (unsigned long)do_exit;
143143
frame->childregs.orig_gpr2 = -1;
144-
144+
frame->childregs.last_break = 1;
145145
return 0;
146146
}
147147
frame->childregs = *current_pt_regs();

arch/s390/kernel/setup.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@ unsigned long MODULES_END;
174174
struct lowcore *lowcore_ptr[NR_CPUS];
175175
EXPORT_SYMBOL(lowcore_ptr);
176176

177+
DEFINE_STATIC_KEY_FALSE(cpu_has_bear);
178+
177179
/*
178180
* The Write Back bit position in the physaddr is given by the SLPC PCI.
179181
* Leaving the mask zero always uses write through which is safe
@@ -1038,6 +1040,9 @@ void __init setup_arch(char **cmdline_p)
10381040
smp_detect_cpus();
10391041
topology_init_early();
10401042

1043+
if (test_facility(193))
1044+
static_branch_enable(&cpu_has_bear);
1045+
10411046
/*
10421047
* Create kernel page tables and switch to virtual addressing.
10431048
*/

arch/s390/kernel/syscall.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@ void noinstr __do_syscall(struct pt_regs *regs, int per_trap)
154154
regs->psw = S390_lowcore.svc_old_psw;
155155
regs->int_code = S390_lowcore.svc_int_code;
156156
update_timer_sys();
157+
if (static_branch_likely(&cpu_has_bear))
158+
current->thread.last_break = regs->last_break;
157159

158160
local_irq_enable();
159161
regs->orig_gpr2 = regs->gprs[2];

arch/s390/kernel/traps.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,6 @@ static void (*pgm_check_table[128])(struct pt_regs *regs);
300300

301301
void noinstr __do_pgm_check(struct pt_regs *regs)
302302
{
303-
unsigned long last_break = S390_lowcore.pgm_last_break;
304303
unsigned int trapnr;
305304
irqentry_state_t state;
306305

@@ -311,10 +310,11 @@ void noinstr __do_pgm_check(struct pt_regs *regs)
311310

312311
if (user_mode(regs)) {
313312
update_timer_sys();
314-
if (last_break < 4096)
315-
last_break = 1;
316-
current->thread.last_break = last_break;
317-
regs->last_break = last_break;
313+
if (!static_branch_likely(&cpu_has_bear)) {
314+
if (regs->last_break < 4096)
315+
regs->last_break = 1;
316+
}
317+
current->thread.last_break = regs->last_break;
318318
}
319319

320320
if (S390_lowcore.pgm_code & 0x0200) {

arch/s390/mm/dump_pagetables.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <linux/kasan.h>
99
#include <asm/ptdump.h>
1010
#include <asm/kasan.h>
11+
#include <asm/nospec-branch.h>
1112
#include <asm/sections.h>
1213

1314
static unsigned long max_addr;
@@ -116,8 +117,13 @@ static void note_prot_wx(struct pg_state *st, unsigned long addr)
116117
return;
117118
if (st->current_prot & _PAGE_NOEXEC)
118119
return;
119-
/* The first lowcore page is currently still W+X. */
120-
if (addr == PAGE_SIZE)
120+
/*
121+
* The first lowcore page is W+X if spectre mitigations are using
122+
* trampolines or the BEAR enhancements facility is not installed,
123+
* in which case we have two lpswe instructions in lowcore that need
124+
* to be executable.
125+
*/
126+
if (addr == PAGE_SIZE && (nospec_uses_trampoline() || !static_key_enabled(&cpu_has_bear)))
121127
return;
122128
WARN_ONCE(1, "s390/mm: Found insecure W+X mapping at address %pS\n",
123129
(void *)st->start_address);
@@ -203,7 +209,9 @@ void ptdump_check_wx(void)
203209
if (st.wx_pages)
204210
pr_warn("Checked W+X mappings: FAILED, %lu W+X pages found\n", st.wx_pages);
205211
else
206-
pr_info("Checked W+X mappings: passed, no unexpected W+X pages found\n");
212+
pr_info("Checked W+X mappings: passed, no %sW+X pages found\n",
213+
(nospec_uses_trampoline() || !static_key_enabled(&cpu_has_bear)) ?
214+
"unexpected " : "");
207215
}
208216
#endif /* CONFIG_DEBUG_WX */
209217

0 commit comments

Comments
 (0)