Commit 17248ea0 authored by Sven Schnelle's avatar Sven Schnelle Committed by Vasily Gorbik
Browse files

s390: fix __EMIT_BUG() macro



Setting a kprobe on getname_flags() failed:

$ echo 'p:tmr1 getname_flags +0(%r2):ustring' > kprobe_events
-bash: echo: write error: Invalid argument

Debugging the kprobes code showed that the address of
getname_flags() is contained in the __bug_table. Kprobes
doesn't allow to set probes at BUG() locations.

$ objdump -j  __bug_table -x build/fs/namei.o
[..]
0000000000000108 R_390_PC32        .text+0x00000000000075a8
000000000000010c R_390_PC32        .L223+0x0000000000000004

I was expecting getname_flags() to start with a BUG(), but:

7598:       e3 20 10 00 00 04       lg      %r2,0(%r1)
759e:       c0 f4 00 00 00 00       jg      759e <putname+0x7e>
75a0: R_390_PLT32DBL    kmem_cache_free+0x2
75a4:       a7 f4 00 01             j       75a6 <putname+0x86>

00000000000075a8 <getname_flags>:
75a8:       c0 04 00 00 00 00       brcl    0,75a8 <getname_flags>
75ae:       eb 6f f0 48 00 24       stmg    %r6,%r15,72(%r15)
75b4:       b9 04 00 ef             lgr     %r14,%r15
75b8:       e3 f0 ff a8 ff 71       lay     %r15,-88(%r15)

So the BUG() is actually the last opcode of the previous function.
Fix this by switching to using the MONITOR CALL (MC) instruction,
and set the entry in __bug_table to the beginning of that MC.

Reviewed-by: default avatarHeiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: default avatarSven Schnelle <svens@linux.ibm.com>
Signed-off-by: default avatarVasily Gorbik <gor@linux.ibm.com>
parent 45f7a0da
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -329,7 +329,7 @@ ENTRY(startup_kdump)
	.quad	.Lduct			# cr5: primary-aste origin
	.quad	0			# cr6:	I/O interrupts
	.quad	0			# cr7:	secondary space segment table
	.quad	0			# cr8:	access registers translation
	.quad	0x0000000000008000	# cr8:	access registers translation
	.quad	0			# cr9:	tracing off
	.quad	0			# cr10: tracing off
	.quad	0			# cr11: tracing off
+7 −9
Original line number Diff line number Diff line
@@ -10,15 +10,14 @@

#define __EMIT_BUG(x) do {					\
	asm_inline volatile(					\
		"0:	j	0b+2\n"				\
		"1:\n"						\
		"0:	mc	0,0\n"				\
		".section .rodata.str,\"aMS\",@progbits,1\n"	\
		"2:	.asciz	\""__FILE__"\"\n"		\
		"1:	.asciz	\""__FILE__"\"\n"		\
		".previous\n"					\
		".section __bug_table,\"awM\",@progbits,%2\n"	\
		"3:	.long	1b-3b,2b-3b\n"			\
		"2:	.long	0b-2b,1b-2b\n"			\
		"	.short	%0,%1\n"			\
		"	.org	3b+%2\n"			\
		"	.org	2b+%2\n"			\
		".previous\n"					\
		: : "i" (__LINE__),				\
		    "i" (x),					\
@@ -29,12 +28,11 @@

#define __EMIT_BUG(x) do {					\
	asm_inline volatile(					\
		"0:	j	0b+2\n"				\
		"1:\n"						\
		"0:	mc	0,0\n"				\
		".section __bug_table,\"awM\",@progbits,%1\n"	\
		"2:	.long	1b-2b\n"			\
		"1:	.long	0b-1b\n"			\
		"	.short	%0\n"				\
		"	.org	2b+%1\n"			\
		"	.org	1b+%1\n"			\
		".previous\n"					\
		: : "i" (x),					\
		    "i" (sizeof(struct bug_entry)));		\
+1 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ void specification_exception(struct pt_regs *regs);
void transaction_exception(struct pt_regs *regs);
void translation_exception(struct pt_regs *regs);
void vector_exception(struct pt_regs *regs);
void monitor_event_exception(struct pt_regs *regs);

void do_per_trap(struct pt_regs *regs);
void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str);
+1 −1
Original line number Diff line number Diff line
@@ -81,7 +81,7 @@ PGM_CHECK_DEFAULT /* 3c */
PGM_CHECK_DEFAULT			/* 3d */
PGM_CHECK_DEFAULT			/* 3e */
PGM_CHECK_DEFAULT			/* 3f */
PGM_CHECK_DEFAULT			/* 40 */
PGM_CHECK(monitor_event_exception)	/* 40 */
PGM_CHECK_DEFAULT			/* 41 */
PGM_CHECK_DEFAULT			/* 42 */
PGM_CHECK_DEFAULT			/* 43 */
+36 −5
Original line number Diff line number Diff line
@@ -53,11 +53,6 @@ void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str)
                if (fixup)
			regs->psw.addr = extable_fixup(fixup);
		else {
			enum bug_trap_type btt;

			btt = report_bug(regs->psw.addr, regs);
			if (btt == BUG_TRAP_TYPE_WARN)
				return;
			die(regs, str);
		}
        }
@@ -245,6 +240,27 @@ void space_switch_exception(struct pt_regs *regs)
	do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event");
}

void monitor_event_exception(struct pt_regs *regs)
{
	const struct exception_table_entry *fixup;

	if (user_mode(regs))
		return;

	switch (report_bug(regs->psw.addr - (regs->int_code >> 16), regs)) {
	case BUG_TRAP_TYPE_NONE:
		fixup = s390_search_extables(regs->psw.addr);
		if (fixup)
			regs->psw.addr = extable_fixup(fixup);
		break;
	case BUG_TRAP_TYPE_WARN:
		break;
	case BUG_TRAP_TYPE_BUG:
		die(regs, "monitor event");
		break;
	}
}

void kernel_stack_overflow(struct pt_regs *regs)
{
	bust_spinlocks(1);
@@ -255,8 +271,23 @@ void kernel_stack_overflow(struct pt_regs *regs)
}
NOKPROBE_SYMBOL(kernel_stack_overflow);

static void test_monitor_call(void)
{
	int val = 1;

	asm volatile(
		"	mc	0,0\n"
		"0:	xgr	%0,%0\n"
		"1:\n"
		EX_TABLE(0b,1b)
		: "+d" (val));
	if (!val)
		panic("Monitor call doesn't work!\n");
}

void __init trap_init(void)
{
	sort_extable(__start_dma_ex_table, __stop_dma_ex_table);
	local_mcck_enable();
	test_monitor_call();
}