arch/s390/lib/test_unwind.c

Source file repositories/reference/linux-study-clean/arch/s390/lib/test_unwind.c

File Facts

System
Linux kernel
Corpus path
arch/s390/lib/test_unwind.c
Extension
.c
Size
14280 bytes
Lines
524
Domain
Architecture Layer
Bucket
arch/s390
Inferred role
Architecture Layer: implementation source
Status
source implementation candidate

Why This File Exists

CPU and platform-specific kernel glue: boot entry, traps, syscall entry, interrupts, page tables, context switch, and low-level barriers.

Dependency Surface

Detected Declarations

Annotated Snippet

struct unwindme {
	int flags;
	int ret;
	struct task_struct *task;
	struct completion task_ready;
	wait_queue_head_t task_wq;
	unsigned long sp;
};

static struct unwindme *unwindme;

/* Values of unwindme.flags. */
#define UWM_DEFAULT		0x0
#define UWM_THREAD		0x1	/* Unwind a separate task. */
#define UWM_REGS		0x2	/* Pass regs to test_unwind(). */
#define UWM_SP			0x4	/* Pass sp to test_unwind(). */
#define UWM_CALLER		0x8	/* Unwind starting from caller. */
#define UWM_SWITCH_STACK	0x10	/* Use call_on_stack. */
#define UWM_IRQ			0x20	/* Unwind from irq context. */
#define UWM_PGM			0x40	/* Unwind from program check handler */
#define UWM_KPROBE_ON_FTRACE	0x80	/* Unwind from kprobe handler called via ftrace. */
#define UWM_FTRACE		0x100	/* Unwind from ftrace handler. */
#define UWM_KRETPROBE		0x200	/* Unwind through kretprobed function. */
#define UWM_KRETPROBE_HANDLER	0x400	/* Unwind from kretprobe handler. */

static __always_inline struct pt_regs fake_pt_regs(void)
{
	struct pt_regs regs;

	memset(&regs, 0, sizeof(regs));
	regs.gprs[15] = current_stack_pointer;

	asm volatile(
		"basr	%[psw_addr],0"
		: [psw_addr] "=d" (regs.psw.addr));
	return regs;
}

static int kretprobe_ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
	struct unwindme *u = unwindme;

	if (!(u->flags & UWM_KRETPROBE_HANDLER))
		return 0;

	u->ret = test_unwind(NULL, (u->flags & UWM_REGS) ? regs : NULL,
			     (u->flags & UWM_SP) ? u->sp : 0);

	return 0;
}

static noinline notrace int test_unwind_kretprobed_func(struct unwindme *u)
{
	struct pt_regs regs;

	if (!(u->flags & UWM_KRETPROBE))
		return 0;

	regs = fake_pt_regs();
	return test_unwind(NULL, (u->flags & UWM_REGS) ? &regs : NULL,
			   (u->flags & UWM_SP) ? u->sp : 0);
}

static noinline int test_unwind_kretprobed_func_caller(struct unwindme *u)
{
	return test_unwind_kretprobed_func(u);
}

static int test_unwind_kretprobe(struct unwindme *u)
{
	int ret;
	struct kretprobe my_kretprobe;

	if (!IS_ENABLED(CONFIG_KPROBES))
		kunit_skip(current_test, "requires CONFIG_KPROBES");

	u->ret = -1; /* make sure kprobe is called */
	unwindme = u;

	memset(&my_kretprobe, 0, sizeof(my_kretprobe));
	my_kretprobe.handler = kretprobe_ret_handler;
	my_kretprobe.maxactive = 1;
	my_kretprobe.kp.addr = (kprobe_opcode_t *)test_unwind_kretprobed_func;

	ret = register_kretprobe(&my_kretprobe);

	if (ret < 0) {
		kunit_err(current_test, "register_kretprobe failed %d\n", ret);
		return -EINVAL;
	}

Annotation

Implementation Notes