Skip to content

Commit

Permalink
parisc: add parisc code patching
Browse files Browse the repository at this point in the history
Instead of re-mapping the whole kernel text with RWX rights
add a patch_text() which can be used to replace instructions
in the kernel .text section. Based on the ARM implementation.

Signed-off-by: Sven Schnelle <[email protected]>
Signed-off-by: Helge Deller <[email protected]>
  • Loading branch information
svenschnelle authored and hdeller committed May 3, 2019
1 parent ccfbc68 commit 620a53d
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 1 deletion.
11 changes: 11 additions & 0 deletions arch/parisc/include/asm/patch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _PARISC_KERNEL_PATCH_H
#define _PARISC_KERNEL_PATCH_H

/* stop machine and patch kernel text */
void patch_text(void *addr, unsigned int insn);

/* patch kernel text with machine already stopped (e.g. in kgdb) */
void __patch_text(void *addr, unsigned int insn);

#endif
3 changes: 2 additions & 1 deletion arch/parisc/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ obj-y := cache.o pacache.o setup.o pdt.o traps.o time.o irq.o \
pa7300lc.o syscall.o entry.o sys_parisc.o firmware.o \
ptrace.o hardware.o inventory.o drivers.o alternative.o \
signal.o hpmc.o real2.o parisc_ksyms.o unaligned.o \
process.o processor.o pdc_cons.o pdc_chassis.o unwind.o
process.o processor.o pdc_cons.o pdc_chassis.o unwind.o \
patch.o

ifdef CONFIG_FUNCTION_TRACER
# Do not profile debug and lowlevel utilities
Expand Down
78 changes: 78 additions & 0 deletions arch/parisc/kernel/patch.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: GPL-2.0
/*
* functions to patch RO kernel text during runtime
*
* Copyright (c) 2019 Sven Schnelle <[email protected]>
*/

#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/kprobes.h>
#include <linux/mm.h>
#include <linux/stop_machine.h>

#include <asm/cacheflush.h>
#include <asm/fixmap.h>
#include <asm/patch.h>

struct patch {
void *addr;
unsigned int insn;
};

static void __kprobes *patch_map(void *addr, int fixmap, unsigned long *flags)
{
unsigned long uintaddr = (uintptr_t) addr;
bool module = !core_kernel_text(uintaddr);
struct page *page;

if (module && IS_ENABLED(CONFIG_STRICT_MODULE_RWX))
page = vmalloc_to_page(addr);
else if (!module && IS_ENABLED(CONFIG_STRICT_KERNEL_RWX))
page = virt_to_page(addr);
else
return addr;

set_fixmap(fixmap, page_to_phys(page));

return (void *) (__fix_to_virt(fixmap) + (uintaddr & ~PAGE_MASK));
}

static void __kprobes patch_unmap(int fixmap, unsigned long *flags)
{
clear_fixmap(fixmap);
}

void __kprobes __patch_text(void *addr, unsigned int insn)
{
unsigned long flags;
void *waddr = addr;
int size;

waddr = patch_map(addr, FIX_TEXT_POKE0, &flags);
*(u32 *)waddr = insn;
size = sizeof(u32);
flush_kernel_vmap_range(waddr, size);
patch_unmap(FIX_TEXT_POKE0, &flags);
flush_icache_range((uintptr_t)(addr),
(uintptr_t)(addr) + size);
}

static int __kprobes patch_text_stop_machine(void *data)
{
struct patch *patch = data;

__patch_text(patch->addr, patch->insn);

return 0;
}

void __kprobes patch_text(void *addr, unsigned int insn)
{
struct patch patch = {
.addr = addr,
.insn = insn,
};

stop_machine_cpuslocked(patch_text_stop_machine, &patch, NULL);
}

0 comments on commit 620a53d

Please sign in to comment.