Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

parisc: add support for patching multiple words

add patch_text_multiple() which allows to patch multiple
text words in memory. This can be used to copy functions.

Signed-off-by: Sven Schnelle <svens@stackframe.org>
Signed-off-by: Helge Deller <deller@gmx.de>

authored by

Sven Schnelle and committed by
Helge Deller
4e87ace9 79c3ba32

+61 -16
+3 -1
arch/parisc/include/asm/patch.h
··· 4 4 5 5 /* stop machine and patch kernel text */ 6 6 void patch_text(void *addr, unsigned int insn); 7 + void patch_text_multiple(void *addr, u32 *insn, unsigned int len); 7 8 8 9 /* patch kernel text with machine already stopped (e.g. in kgdb) */ 9 - void __patch_text(void *addr, unsigned int insn); 10 + void __patch_text(void *addr, u32 insn); 11 + void __patch_text_multiple(void *addr, u32 *insn, unsigned int len); 10 12 11 13 #endif
+58 -15
arch/parisc/kernel/patch.c
··· 17 17 18 18 struct patch { 19 19 void *addr; 20 - unsigned int insn; 20 + u32 *insn; 21 + unsigned int len; 21 22 }; 22 23 23 - static void __kprobes *patch_map(void *addr, int fixmap) 24 - { 24 + static DEFINE_RAW_SPINLOCK(patch_lock); 25 + 26 + static void __kprobes *patch_map(void *addr, int fixmap, int *need_unmap) 25 27 unsigned long uintaddr = (uintptr_t) addr; 26 28 bool module = !core_kernel_text(uintaddr); 27 29 struct page *page; 28 30 31 + *need_unmap = 0; 29 32 if (module && IS_ENABLED(CONFIG_STRICT_MODULE_RWX)) 30 33 page = vmalloc_to_page(addr); 31 34 else if (!module && IS_ENABLED(CONFIG_STRICT_KERNEL_RWX)) ··· 36 33 else 37 34 return addr; 38 35 36 + *need_unmap = 1; 39 37 set_fixmap(fixmap, page_to_phys(page)); 40 38 41 39 return (void *) (__fix_to_virt(fixmap) + (uintaddr & ~PAGE_MASK)); ··· 47 43 clear_fixmap(fixmap); 48 44 } 49 45 50 - void __kprobes __patch_text(void *addr, unsigned int insn) 46 + void __kprobes __patch_text_multiple(void *addr, u32 *insn, unsigned int len) 51 47 { 52 - void *waddr = addr; 53 - int size; 48 + unsigned long start = (unsigned long)addr; 49 + unsigned long end = (unsigned long)addr + len; 50 + u32 *p, *fixmap; 51 + int mapped; 54 52 55 - waddr = patch_map(addr, FIX_TEXT_POKE0); 56 - *(u32 *)waddr = insn; 57 - size = sizeof(u32); 58 - flush_kernel_vmap_range(waddr, size); 59 - patch_unmap(FIX_TEXT_POKE0); 60 - flush_icache_range((uintptr_t)(addr), 61 - (uintptr_t)(addr) + size); 53 + /* Make sure we don't have any aliases in cache */ 54 + flush_kernel_vmap_range(addr, len); 55 + flush_icache_range(start, end); 56 + 57 + p = fixmap = patch_map(addr, FIX_TEXT_POKE0, &mapped); 58 + 59 + while (len >= 4) { 60 + *p++ = *insn++; 61 + addr += sizeof(u32); 62 + len -= sizeof(u32); 63 + if (len && offset_in_page(addr) == 0) { 64 + /* 65 + * We're crossing a page boundary, so 66 + * need to remap 67 + */ 68 + flush_kernel_vmap_range((void *)fixmap, 69 + (p-fixmap) * sizeof(*p)); 70 + if (mapped) 71 + patch_unmap(FIX_TEXT_POKE0); 72 + p = fixmap = patch_map(addr, FIX_TEXT_POKE0, &mapped); 73 + } 74 + } 75 + 76 + flush_kernel_vmap_range((void *)fixmap, (p-fixmap) * sizeof(*p)); 77 + if (mapped) 78 + patch_unmap(FIX_TEXT_POKE0); 79 + flush_icache_range(start, end); 80 + } 81 + 82 + void __kprobes __patch_text(void *addr, u32 insn) 83 + { 84 + __patch_text_multiple(addr, &insn, sizeof(insn)); 62 85 } 63 86 64 87 static int __kprobes patch_text_stop_machine(void *data) 65 88 { 66 89 struct patch *patch = data; 67 90 68 - __patch_text(patch->addr, patch->insn); 69 - 91 + __patch_text_multiple(patch->addr, patch->insn, patch->len); 70 92 return 0; 71 93 } 72 94 ··· 100 70 { 101 71 struct patch patch = { 102 72 .addr = addr, 73 + .insn = &insn, 74 + .len = sizeof(insn), 75 + }; 76 + 77 + stop_machine_cpuslocked(patch_text_stop_machine, &patch, NULL); 78 + } 79 + 80 + void __kprobes patch_text_multiple(void *addr, u32 *insn, unsigned int len) 81 + { 82 + 83 + struct patch patch = { 84 + .addr = addr, 103 85 .insn = insn, 86 + .len = len 104 87 }; 105 88 106 89 stop_machine_cpuslocked(patch_text_stop_machine, &patch, NULL);