1
1
#include <linux/kernel.h>
2
+ #include <linux/spinlock.h>
2
3
#include <linux/kprobes.h>
4
+ #include <linux/mm.h>
3
5
#include <linux/stop_machine.h>
4
6
5
7
#include <asm/cacheflush.h>
8
+ #include <asm/fixmap.h>
6
9
#include <asm/smp_plat.h>
7
10
#include <asm/opcodes.h>
8
11
@@ -13,21 +16,77 @@ struct patch {
13
16
unsigned int insn ;
14
17
};
15
18
16
- void __kprobes __patch_text (void * addr , unsigned int insn )
19
+ static DEFINE_SPINLOCK (patch_lock );
20
+
21
+ static void __kprobes * patch_map (void * addr , int fixmap , unsigned long * flags )
22
+ __acquires (& patch_lock )
23
+ {
24
+ unsigned int uintaddr = (uintptr_t ) addr ;
25
+ bool module = !core_kernel_text (uintaddr );
26
+ struct page * page ;
27
+
28
+ if (module && IS_ENABLED (CONFIG_DEBUG_SET_MODULE_RONX ))
29
+ page = vmalloc_to_page (addr );
30
+ else if (!module && IS_ENABLED (CONFIG_DEBUG_RODATA ))
31
+ page = virt_to_page (addr );
32
+ else
33
+ return addr ;
34
+
35
+ if (flags )
36
+ spin_lock_irqsave (& patch_lock , * flags );
37
+ else
38
+ __acquire (& patch_lock );
39
+
40
+ set_fixmap (fixmap , page_to_phys (page ));
41
+
42
+ return (void * ) (__fix_to_virt (fixmap ) + (uintaddr & ~PAGE_MASK ));
43
+ }
44
+
45
+ static void __kprobes patch_unmap (int fixmap , unsigned long * flags )
46
+ __releases (& patch_lock )
47
+ {
48
+ clear_fixmap (fixmap );
49
+
50
+ if (flags )
51
+ spin_unlock_irqrestore (& patch_lock , * flags );
52
+ else
53
+ __release (& patch_lock );
54
+ }
55
+
56
+ void __kprobes __patch_text_real (void * addr , unsigned int insn , bool remap )
17
57
{
18
58
bool thumb2 = IS_ENABLED (CONFIG_THUMB2_KERNEL );
59
+ unsigned int uintaddr = (uintptr_t ) addr ;
60
+ bool twopage = false;
61
+ unsigned long flags ;
62
+ void * waddr = addr ;
19
63
int size ;
20
64
65
+ if (remap )
66
+ waddr = patch_map (addr , FIX_TEXT_POKE0 , & flags );
67
+ else
68
+ __acquire (& patch_lock );
69
+
21
70
if (thumb2 && __opcode_is_thumb16 (insn )) {
22
- * (u16 * )addr = __opcode_to_mem_thumb16 (insn );
71
+ * (u16 * )waddr = __opcode_to_mem_thumb16 (insn );
23
72
size = sizeof (u16 );
24
- } else if (thumb2 && (( uintptr_t ) addr & 2 )) {
73
+ } else if (thumb2 && (uintaddr & 2 )) {
25
74
u16 first = __opcode_thumb32_first (insn );
26
75
u16 second = __opcode_thumb32_second (insn );
27
- u16 * addrh = addr ;
76
+ u16 * addrh0 = waddr ;
77
+ u16 * addrh1 = waddr + 2 ;
78
+
79
+ twopage = (uintaddr & ~PAGE_MASK ) == PAGE_SIZE - 2 ;
80
+ if (twopage && remap )
81
+ addrh1 = patch_map (addr + 2 , FIX_TEXT_POKE1 , NULL );
82
+
83
+ * addrh0 = __opcode_to_mem_thumb16 (first );
84
+ * addrh1 = __opcode_to_mem_thumb16 (second );
28
85
29
- addrh [0 ] = __opcode_to_mem_thumb16 (first );
30
- addrh [1 ] = __opcode_to_mem_thumb16 (second );
86
+ if (twopage && addrh1 != addr + 2 ) {
87
+ flush_kernel_vmap_range (addrh1 , 2 );
88
+ patch_unmap (FIX_TEXT_POKE1 , NULL );
89
+ }
31
90
32
91
size = sizeof (u32 );
33
92
} else {
@@ -36,10 +95,16 @@ void __kprobes __patch_text(void *addr, unsigned int insn)
36
95
else
37
96
insn = __opcode_to_mem_arm (insn );
38
97
39
- * (u32 * )addr = insn ;
98
+ * (u32 * )waddr = insn ;
40
99
size = sizeof (u32 );
41
100
}
42
101
102
+ if (waddr != addr ) {
103
+ flush_kernel_vmap_range (waddr , twopage ? size / 2 : size );
104
+ patch_unmap (FIX_TEXT_POKE0 , & flags );
105
+ } else
106
+ __release (& patch_lock );
107
+
43
108
flush_icache_range ((uintptr_t )(addr ),
44
109
(uintptr_t )(addr ) + size );
45
110
}
@@ -60,16 +125,5 @@ void __kprobes patch_text(void *addr, unsigned int insn)
60
125
.insn = insn ,
61
126
};
62
127
63
- if (cache_ops_need_broadcast ()) {
64
- stop_machine (patch_text_stop_machine , & patch , cpu_online_mask );
65
- } else {
66
- bool straddles_word = IS_ENABLED (CONFIG_THUMB2_KERNEL )
67
- && __opcode_is_thumb32 (insn )
68
- && ((uintptr_t )addr & 2 );
69
-
70
- if (straddles_word )
71
- stop_machine (patch_text_stop_machine , & patch , NULL );
72
- else
73
- __patch_text (addr , insn );
74
- }
128
+ stop_machine (patch_text_stop_machine , & patch , NULL );
75
129
}
0 commit comments