arch/powerpc/kernel/optprobes.c

Source file repositories/reference/linux-study-clean/arch/powerpc/kernel/optprobes.c

File Facts

System
Linux kernel
Corpus path
arch/powerpc/kernel/optprobes.c
Extension
.c
Size
8661 bytes
Lines
305
Domain
Architecture Layer
Bucket
arch/powerpc
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

analyse_instr(&op, &regs, ppc_inst_read(p->ainsn.insn)) == 1) {
		emulate_update_regs(&regs, &op);
		nip = regs.nip;
	}

	return nip;
}

static void optimized_callback(struct optimized_kprobe *op,
			       struct pt_regs *regs)
{
	/* This is possible if op is under delayed unoptimizing */
	if (kprobe_disabled(&op->kp))
		return;

	preempt_disable();

	if (kprobe_running()) {
		kprobes_inc_nmissed_count(&op->kp);
	} else {
		__this_cpu_write(current_kprobe, &op->kp);
		regs_set_return_ip(regs, (unsigned long)op->kp.addr);
		get_kprobe_ctlblk()->kprobe_status = KPROBE_HIT_ACTIVE;
		opt_pre_handler(&op->kp, regs);
		__this_cpu_write(current_kprobe, NULL);
	}

	preempt_enable();
}
NOKPROBE_SYMBOL(optimized_callback);

void arch_remove_optimized_kprobe(struct optimized_kprobe *op)
{
	if (op->optinsn.insn) {
		free_optinsn_slot(op->optinsn.insn, 1);
		op->optinsn.insn = NULL;
	}
}

static void patch_imm32_load_insns(unsigned long val, int reg, kprobe_opcode_t *addr)
{
	patch_instruction(addr++, ppc_inst(PPC_RAW_LIS(reg, PPC_HI(val))));
	patch_instruction(addr, ppc_inst(PPC_RAW_ORI(reg, reg, PPC_LO(val))));
}

/*
 * Generate instructions to load provided immediate 64-bit value
 * to register 'reg' and patch these instructions at 'addr'.
 */
static void patch_imm64_load_insns(unsigned long long val, int reg, kprobe_opcode_t *addr)
{
	patch_instruction(addr++, ppc_inst(PPC_RAW_LIS(reg, PPC_HIGHEST(val))));
	patch_instruction(addr++, ppc_inst(PPC_RAW_ORI(reg, reg, PPC_HIGHER(val))));
	patch_instruction(addr++, ppc_inst(PPC_RAW_SLDI(reg, reg, 32)));
	patch_instruction(addr++, ppc_inst(PPC_RAW_ORIS(reg, reg, PPC_HI(val))));
	patch_instruction(addr, ppc_inst(PPC_RAW_ORI(reg, reg, PPC_LO(val))));
}

static void patch_imm_load_insns(unsigned long val, int reg, kprobe_opcode_t *addr)
{
	if (IS_ENABLED(CONFIG_PPC64))
		patch_imm64_load_insns(val, reg, addr);
	else
		patch_imm32_load_insns(val, reg, addr);
}

int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *p)
{
	ppc_inst_t branch_op_callback, branch_emulate_step, temp;
	unsigned long op_callback_addr, emulate_step_addr;
	kprobe_opcode_t *buff;
	long b_offset;
	unsigned long nip, size;
	int rc, i;

	nip = can_optimize(p);
	if (!nip)
		return -EILSEQ;

	/* Allocate instruction slot for detour buffer */
	buff = get_optinsn_slot();
	if (!buff)
		return -ENOMEM;

	/*
	 * OPTPROBE uses 'b' instruction to branch to optinsn.insn.
	 *
	 * The target address has to be relatively nearby, to permit use
	 * of branch instruction in powerpc, because the address is specified
	 * in an immediate field in the instruction opcode itself, ie 24 bits

Annotation

Implementation Notes