Skip to content

Commit

Permalink
From patchwork series 378606
Browse files Browse the repository at this point in the history
  • Loading branch information
Fox Snowpatch committed Oct 20, 2023
1 parent 16cbbab commit e91efce
Show file tree
Hide file tree
Showing 6 changed files with 271 additions and 57 deletions.
1 change: 1 addition & 0 deletions arch/powerpc/include/asm/code-patching.h
Expand Up @@ -74,6 +74,7 @@ int create_cond_branch(ppc_inst_t *instr, const u32 *addr,
int patch_branch(u32 *addr, unsigned long target, int flags);
int patch_instruction(u32 *addr, ppc_inst_t instr);
int raw_patch_instruction(u32 *addr, ppc_inst_t instr);
int patch_instructions(u32 *addr, u32 *code, size_t len, bool repeat_instr);

static inline unsigned long patch_site_addr(s32 *site)
{
Expand Down
141 changes: 138 additions & 3 deletions arch/powerpc/lib/code-patching.c
Expand Up @@ -204,9 +204,6 @@ void __init poking_init(void)
{
int ret;

if (!IS_ENABLED(CONFIG_STRICT_KERNEL_RWX))
return;

if (mm_patch_enabled())
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
"powerpc/text_poke_mm:online",
Expand Down Expand Up @@ -378,6 +375,144 @@ int patch_instruction(u32 *addr, ppc_inst_t instr)
}
NOKPROBE_SYMBOL(patch_instruction);

static int __patch_instructions(u32 *patch_addr, u32 *code, size_t len, bool repeat_instr)
{
unsigned long start = (unsigned long)patch_addr;

/* Repeat instruction */
if (repeat_instr) {
ppc_inst_t instr = ppc_inst_read(code);

if (ppc_inst_prefixed(instr)) {
u64 val = ppc_inst_as_ulong(instr);

memset64((u64 *)patch_addr, val, len / 8);
} else {
u32 val = ppc_inst_val(instr);

memset32(patch_addr, val, len / 4);
}
} else {
memcpy(patch_addr, code, len);
}

smp_wmb(); /* smp write barrier */
flush_icache_range(start, start + len);
return 0;
}

/*
* A page is mapped and instructions that fit the page are patched.
* Assumes 'len' to be (PAGE_SIZE - offset_in_page(addr)) or below.
*/
static int __do_patch_instructions_mm(u32 *addr, u32 *code, size_t len, bool repeat_instr)
{
struct mm_struct *patching_mm, *orig_mm;
unsigned long pfn = get_patch_pfn(addr);
unsigned long text_poke_addr;
spinlock_t *ptl;
u32 *patch_addr;
pte_t *pte;
int err;

patching_mm = __this_cpu_read(cpu_patching_context.mm);
text_poke_addr = __this_cpu_read(cpu_patching_context.addr);
patch_addr = (u32 *)(text_poke_addr + offset_in_page(addr));

pte = get_locked_pte(patching_mm, text_poke_addr, &ptl);
if (!pte)
return -ENOMEM;

__set_pte_at(patching_mm, text_poke_addr, pte, pfn_pte(pfn, PAGE_KERNEL), 0);

/* order PTE update before use, also serves as the hwsync */
asm volatile("ptesync" ::: "memory");

/* order context switch after arbitrary prior code */
isync();

orig_mm = start_using_temp_mm(patching_mm);

err = __patch_instructions(patch_addr, code, len, repeat_instr);

/* context synchronisation performed by __patch_instructions */
stop_using_temp_mm(patching_mm, orig_mm);

pte_clear(patching_mm, text_poke_addr, pte);
/*
* ptesync to order PTE update before TLB invalidation done
* by radix__local_flush_tlb_page_psize (in _tlbiel_va)
*/
local_flush_tlb_page_psize(patching_mm, text_poke_addr, mmu_virtual_psize);

pte_unmap_unlock(pte, ptl);

return err;
}

/*
* A page is mapped and instructions that fit the page are patched.
* Assumes 'len' to be (PAGE_SIZE - offset_in_page(addr)) or below.
*/
static int __do_patch_instructions(u32 *addr, u32 *code, size_t len, bool repeat_instr)
{
unsigned long pfn = get_patch_pfn(addr);
unsigned long text_poke_addr;
u32 *patch_addr;
pte_t *pte;
int err;

text_poke_addr = (unsigned long)__this_cpu_read(cpu_patching_context.addr) & PAGE_MASK;
patch_addr = (u32 *)(text_poke_addr + offset_in_page(addr));

pte = __this_cpu_read(cpu_patching_context.pte);
__set_pte_at(&init_mm, text_poke_addr, pte, pfn_pte(pfn, PAGE_KERNEL), 0);
/* See ptesync comment in radix__set_pte_at() */
if (radix_enabled())
asm volatile("ptesync" ::: "memory");

err = __patch_instructions(patch_addr, code, len, repeat_instr);

pte_clear(&init_mm, text_poke_addr, pte);
flush_tlb_kernel_range(text_poke_addr, text_poke_addr + PAGE_SIZE);

return err;
}

/*
* Patch 'addr' with 'len' bytes of instructions from 'code'.
*
* If repeat_instr is true, the same instruction is filled for
* 'len' bytes.
*/
int patch_instructions(u32 *addr, u32 *code, size_t len, bool repeat_instr)
{
while (len > 0) {
unsigned long flags;
size_t plen;
int err;

plen = min_t(size_t, PAGE_SIZE - offset_in_page(addr), len);

local_irq_save(flags);
if (mm_patch_enabled())
err = __do_patch_instructions_mm(addr, code, plen, repeat_instr);
else
err = __do_patch_instructions(addr, code, plen, repeat_instr);
local_irq_restore(flags);
if (err)
return err;

len -= plen;
addr = (u32 *)((unsigned long)addr + plen);
if (!repeat_instr)
code = (u32 *)((unsigned long)code + plen);
}

return 0;
}
NOKPROBE_SYMBOL(patch_instructions);

int patch_branch(u32 *addr, unsigned long target, int flags)
{
ppc_inst_t instr;
Expand Down
18 changes: 5 additions & 13 deletions arch/powerpc/net/bpf_jit.h
Expand Up @@ -36,9 +36,6 @@
EMIT(PPC_RAW_BRANCH(offset)); \
} while (0)

/* bl (unconditional 'branch' with link) */
#define PPC_BL(dest) EMIT(PPC_RAW_BL((dest) - (unsigned long)(image + ctx->idx)))

/* "cond" here covers BO:BI fields. */
#define PPC_BCC_SHORT(cond, dest) \
do { \
Expand Down Expand Up @@ -147,12 +144,6 @@ struct codegen_context {
#define BPF_FIXUP_LEN 2 /* Two instructions => 8 bytes */
#endif

static inline void bpf_flush_icache(void *start, void *end)
{
smp_wmb(); /* smp write barrier */
flush_icache_range((unsigned long)start, (unsigned long)end);
}

static inline bool bpf_is_seen_register(struct codegen_context *ctx, int i)
{
return ctx->seen & (1 << (31 - i));
Expand All @@ -169,16 +160,17 @@ static inline void bpf_clear_seen_register(struct codegen_context *ctx, int i)
}

void bpf_jit_init_reg_mapping(struct codegen_context *ctx);
int bpf_jit_emit_func_call_rel(u32 *image, struct codegen_context *ctx, u64 func);
int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, struct codegen_context *ctx,
int bpf_jit_emit_func_call_rel(u32 *image, u32 *fimage, struct codegen_context *ctx, u64 func);
int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct codegen_context *ctx,
u32 *addrs, int pass, bool extra_pass);
void bpf_jit_build_prologue(u32 *image, struct codegen_context *ctx);
void bpf_jit_build_epilogue(u32 *image, struct codegen_context *ctx);
void bpf_jit_realloc_regs(struct codegen_context *ctx);
int bpf_jit_emit_exit_insn(u32 *image, struct codegen_context *ctx, int tmp_reg, long exit_addr);

int bpf_add_extable_entry(struct bpf_prog *fp, u32 *image, int pass, struct codegen_context *ctx,
int insn_idx, int jmp_off, int dst_reg);
int bpf_add_extable_entry(struct bpf_prog *fp, u32 *image, u32 *fimage, int pass,
struct codegen_context *ctx, int insn_idx,
int jmp_off, int dst_reg);

#endif

Expand Down

0 comments on commit e91efce

Please sign in to comment.