Skip to content

Commit 465cabc

Browse files
hbathinimpe
authored andcommitted
powerpc/code-patching: introduce patch_instructions()
patch_instruction() entails setting up pte, patching the instruction, clearing the pte and flushing the tlb. If multiple instructions need to be patched, every instruction would have to go through the above drill unnecessarily. Instead, introduce patch_instructions() function that sets up the pte, clears the pte and flushes the tlb only once per page range of instructions to be patched. Duplicate most of the patch_instruction() code instead of merging with it, to avoid the performance degradation observed on ppc32, for patch_instruction(), with the code path merged. Also, setup poking_init() always as BPF expects poking_init() to be setup even when STRICT_KERNEL_RWX is off. Signed-off-by: Hari Bathini <[email protected]> Acked-by: Song Liu <[email protected]> Signed-off-by: Michael Ellerman <[email protected]> Link: https://msgid.link/[email protected]
1 parent aad26d3 commit 465cabc

File tree

2 files changed

+139
-3
lines changed

2 files changed

+139
-3
lines changed

arch/powerpc/include/asm/code-patching.h

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ int create_cond_branch(ppc_inst_t *instr, const u32 *addr,
7474
int patch_branch(u32 *addr, unsigned long target, int flags);
7575
int patch_instruction(u32 *addr, ppc_inst_t instr);
7676
int raw_patch_instruction(u32 *addr, ppc_inst_t instr);
77+
int patch_instructions(u32 *addr, u32 *code, size_t len, bool repeat_instr);
7778

7879
static inline unsigned long patch_site_addr(s32 *site)
7980
{

arch/powerpc/lib/code-patching.c

+138-3
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,6 @@ void __init poking_init(void)
205205
{
206206
int ret;
207207

208-
if (!IS_ENABLED(CONFIG_STRICT_KERNEL_RWX))
209-
return;
210-
211208
if (mm_patch_enabled())
212209
ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
213210
"powerpc/text_poke_mm:online",
@@ -375,6 +372,144 @@ int patch_instruction(u32 *addr, ppc_inst_t instr)
375372
}
376373
NOKPROBE_SYMBOL(patch_instruction);
377374

375+
static int __patch_instructions(u32 *patch_addr, u32 *code, size_t len, bool repeat_instr)
376+
{
377+
unsigned long start = (unsigned long)patch_addr;
378+
379+
/* Repeat instruction */
380+
if (repeat_instr) {
381+
ppc_inst_t instr = ppc_inst_read(code);
382+
383+
if (ppc_inst_prefixed(instr)) {
384+
u64 val = ppc_inst_as_ulong(instr);
385+
386+
memset64((u64 *)patch_addr, val, len / 8);
387+
} else {
388+
u32 val = ppc_inst_val(instr);
389+
390+
memset32(patch_addr, val, len / 4);
391+
}
392+
} else {
393+
memcpy(patch_addr, code, len);
394+
}
395+
396+
smp_wmb(); /* smp write barrier */
397+
flush_icache_range(start, start + len);
398+
return 0;
399+
}
400+
401+
/*
402+
* A page is mapped and instructions that fit the page are patched.
403+
* Assumes 'len' to be (PAGE_SIZE - offset_in_page(addr)) or below.
404+
*/
405+
static int __do_patch_instructions_mm(u32 *addr, u32 *code, size_t len, bool repeat_instr)
406+
{
407+
struct mm_struct *patching_mm, *orig_mm;
408+
unsigned long pfn = get_patch_pfn(addr);
409+
unsigned long text_poke_addr;
410+
spinlock_t *ptl;
411+
u32 *patch_addr;
412+
pte_t *pte;
413+
int err;
414+
415+
patching_mm = __this_cpu_read(cpu_patching_context.mm);
416+
text_poke_addr = __this_cpu_read(cpu_patching_context.addr);
417+
patch_addr = (u32 *)(text_poke_addr + offset_in_page(addr));
418+
419+
pte = get_locked_pte(patching_mm, text_poke_addr, &ptl);
420+
if (!pte)
421+
return -ENOMEM;
422+
423+
__set_pte_at(patching_mm, text_poke_addr, pte, pfn_pte(pfn, PAGE_KERNEL), 0);
424+
425+
/* order PTE update before use, also serves as the hwsync */
426+
asm volatile("ptesync" ::: "memory");
427+
428+
/* order context switch after arbitrary prior code */
429+
isync();
430+
431+
orig_mm = start_using_temp_mm(patching_mm);
432+
433+
err = __patch_instructions(patch_addr, code, len, repeat_instr);
434+
435+
/* context synchronisation performed by __patch_instructions */
436+
stop_using_temp_mm(patching_mm, orig_mm);
437+
438+
pte_clear(patching_mm, text_poke_addr, pte);
439+
/*
440+
* ptesync to order PTE update before TLB invalidation done
441+
* by radix__local_flush_tlb_page_psize (in _tlbiel_va)
442+
*/
443+
local_flush_tlb_page_psize(patching_mm, text_poke_addr, mmu_virtual_psize);
444+
445+
pte_unmap_unlock(pte, ptl);
446+
447+
return err;
448+
}
449+
450+
/*
451+
* A page is mapped and instructions that fit the page are patched.
452+
* Assumes 'len' to be (PAGE_SIZE - offset_in_page(addr)) or below.
453+
*/
454+
static int __do_patch_instructions(u32 *addr, u32 *code, size_t len, bool repeat_instr)
455+
{
456+
unsigned long pfn = get_patch_pfn(addr);
457+
unsigned long text_poke_addr;
458+
u32 *patch_addr;
459+
pte_t *pte;
460+
int err;
461+
462+
text_poke_addr = (unsigned long)__this_cpu_read(cpu_patching_context.addr) & PAGE_MASK;
463+
patch_addr = (u32 *)(text_poke_addr + offset_in_page(addr));
464+
465+
pte = __this_cpu_read(cpu_patching_context.pte);
466+
__set_pte_at(&init_mm, text_poke_addr, pte, pfn_pte(pfn, PAGE_KERNEL), 0);
467+
/* See ptesync comment in radix__set_pte_at() */
468+
if (radix_enabled())
469+
asm volatile("ptesync" ::: "memory");
470+
471+
err = __patch_instructions(patch_addr, code, len, repeat_instr);
472+
473+
pte_clear(&init_mm, text_poke_addr, pte);
474+
flush_tlb_kernel_range(text_poke_addr, text_poke_addr + PAGE_SIZE);
475+
476+
return err;
477+
}
478+
479+
/*
480+
* Patch 'addr' with 'len' bytes of instructions from 'code'.
481+
*
482+
* If repeat_instr is true, the same instruction is filled for
483+
* 'len' bytes.
484+
*/
485+
int patch_instructions(u32 *addr, u32 *code, size_t len, bool repeat_instr)
486+
{
487+
while (len > 0) {
488+
unsigned long flags;
489+
size_t plen;
490+
int err;
491+
492+
plen = min_t(size_t, PAGE_SIZE - offset_in_page(addr), len);
493+
494+
local_irq_save(flags);
495+
if (mm_patch_enabled())
496+
err = __do_patch_instructions_mm(addr, code, plen, repeat_instr);
497+
else
498+
err = __do_patch_instructions(addr, code, plen, repeat_instr);
499+
local_irq_restore(flags);
500+
if (err)
501+
return err;
502+
503+
len -= plen;
504+
addr = (u32 *)((unsigned long)addr + plen);
505+
if (!repeat_instr)
506+
code = (u32 *)((unsigned long)code + plen);
507+
}
508+
509+
return 0;
510+
}
511+
NOKPROBE_SYMBOL(patch_instructions);
512+
378513
int patch_branch(u32 *addr, unsigned long target, int flags)
379514
{
380515
ppc_inst_t instr;

0 commit comments

Comments
 (0)