diff options
author | Helge Deller <deller@gmx.de> | 2019-08-12 13:11:06 -0400 |
---|---|---|
committer | Helge Deller <deller@gmx.de> | 2019-08-12 13:17:39 -0400 |
commit | 82992fc70f98dee091faa926eb5cecadda5c84f4 (patch) | |
tree | 915078a06f9ed8bd736bbd29d52eca237af19b91 /arch/parisc | |
parent | 83af58f8068ea3f7b3c537c37a30887bfa585069 (diff) |
parisc: Add ALTERNATIVE_CODE() and ALT_COND_RUN_ON_QEMU
The macro ALTERNATIVE_CODE() allows assembly code to patch in a series
of new assembler statements given at a specific start address.
The ALT_COND_RUN_ON_QEMU condition is true if the kernel is started in a
qemu emulation.
Signed-off-by: Helge Deller <deller@gmx.de>
Diffstat (limited to 'arch/parisc')
-rw-r--r-- | arch/parisc/include/asm/alternative.h | 11 | ||||
-rw-r--r-- | arch/parisc/kernel/alternative.c | 23 |
2 files changed, 27 insertions, 7 deletions
diff --git a/arch/parisc/include/asm/alternative.h b/arch/parisc/include/asm/alternative.h index 793d8baa3a10..0ec54f43d6d2 100644 --- a/arch/parisc/include/asm/alternative.h +++ b/arch/parisc/include/asm/alternative.h | |||
@@ -8,6 +8,7 @@ | |||
8 | #define ALT_COND_NO_ICACHE 0x04 /* if system has no i-cache */ | 8 | #define ALT_COND_NO_ICACHE 0x04 /* if system has no i-cache */ |
9 | #define ALT_COND_NO_SPLIT_TLB 0x08 /* if split_tlb == 0 */ | 9 | #define ALT_COND_NO_SPLIT_TLB 0x08 /* if split_tlb == 0 */ |
10 | #define ALT_COND_NO_IOC_FDC 0x10 /* if I/O cache does not need flushes */ | 10 | #define ALT_COND_NO_IOC_FDC 0x10 /* if I/O cache does not need flushes */ |
11 | #define ALT_COND_RUN_ON_QEMU 0x20 /* if running on QEMU */ | ||
11 | 12 | ||
12 | #define INSN_PxTLB 0x02 /* modify pdtlb, pitlb */ | 13 | #define INSN_PxTLB 0x02 /* modify pdtlb, pitlb */ |
13 | #define INSN_NOP 0x08000240 /* nop */ | 14 | #define INSN_NOP 0x08000240 /* nop */ |
@@ -21,7 +22,7 @@ | |||
21 | 22 | ||
22 | struct alt_instr { | 23 | struct alt_instr { |
23 | s32 orig_offset; /* offset to original instructions */ | 24 | s32 orig_offset; /* offset to original instructions */ |
24 | u32 len; /* end of original instructions */ | 25 | s32 len; /* end of original instructions */ |
25 | u32 cond; /* see ALT_COND_XXX */ | 26 | u32 cond; /* see ALT_COND_XXX */ |
26 | u32 replacement; /* replacement instruction or code */ | 27 | u32 replacement; /* replacement instruction or code */ |
27 | }; | 28 | }; |
@@ -40,12 +41,20 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end, | |||
40 | 41 | ||
41 | #else | 42 | #else |
42 | 43 | ||
44 | /* to replace one single instructions by a new instruction */ | ||
43 | #define ALTERNATIVE(from, to, cond, replacement)\ | 45 | #define ALTERNATIVE(from, to, cond, replacement)\ |
44 | .section .altinstructions, "aw" ! \ | 46 | .section .altinstructions, "aw" ! \ |
45 | .word (from - .), (to - from)/4 ! \ | 47 | .word (from - .), (to - from)/4 ! \ |
46 | .word cond, replacement ! \ | 48 | .word cond, replacement ! \ |
47 | .previous | 49 | .previous |
48 | 50 | ||
51 | /* to replace multiple instructions by new code */ | ||
52 | #define ALTERNATIVE_CODE(from, num_instructions, cond, new_instr_ptr)\ | ||
53 | .section .altinstructions, "aw" ! \ | ||
54 | .word (from - .), -num_instructions ! \ | ||
55 | .word cond, (new_instr_ptr - .) ! \ | ||
56 | .previous | ||
57 | |||
49 | #endif /* __ASSEMBLY__ */ | 58 | #endif /* __ASSEMBLY__ */ |
50 | 59 | ||
51 | #endif /* __ASM_PARISC_ALTERNATIVE_H */ | 60 | #endif /* __ASM_PARISC_ALTERNATIVE_H */ |
diff --git a/arch/parisc/kernel/alternative.c b/arch/parisc/kernel/alternative.c index ca1f5ca0540a..3c66d5c4d90d 100644 --- a/arch/parisc/kernel/alternative.c +++ b/arch/parisc/kernel/alternative.c | |||
@@ -28,7 +28,8 @@ void __init_or_module apply_alternatives(struct alt_instr *start, | |||
28 | 28 | ||
29 | for (entry = start; entry < end; entry++, index++) { | 29 | for (entry = start; entry < end; entry++, index++) { |
30 | 30 | ||
31 | u32 *from, len, cond, replacement; | 31 | u32 *from, cond, replacement; |
32 | s32 len; | ||
32 | 33 | ||
33 | from = (u32 *)((ulong)&entry->orig_offset + entry->orig_offset); | 34 | from = (u32 *)((ulong)&entry->orig_offset + entry->orig_offset); |
34 | len = entry->len; | 35 | len = entry->len; |
@@ -49,6 +50,8 @@ void __init_or_module apply_alternatives(struct alt_instr *start, | |||
49 | continue; | 50 | continue; |
50 | if ((cond & ALT_COND_NO_ICACHE) && (cache_info.ic_size != 0)) | 51 | if ((cond & ALT_COND_NO_ICACHE) && (cache_info.ic_size != 0)) |
51 | continue; | 52 | continue; |
53 | if ((cond & ALT_COND_RUN_ON_QEMU) && !running_on_qemu) | ||
54 | continue; | ||
52 | 55 | ||
53 | /* | 56 | /* |
54 | * If the PDC_MODEL capabilities has Non-coherent IO-PDIR bit | 57 | * If the PDC_MODEL capabilities has Non-coherent IO-PDIR bit |
@@ -74,11 +77,19 @@ void __init_or_module apply_alternatives(struct alt_instr *start, | |||
74 | if (replacement == INSN_NOP && len > 1) | 77 | if (replacement == INSN_NOP && len > 1) |
75 | replacement = 0xe8000002 + (len-2)*8; /* "b,n .+8" */ | 78 | replacement = 0xe8000002 + (len-2)*8; /* "b,n .+8" */ |
76 | 79 | ||
77 | pr_debug("Do %d: Cond 0x%x, Replace %02d instructions @ 0x%px with 0x%08x\n", | 80 | pr_debug("ALTERNATIVE %3d: Cond %2x, Replace %2d instructions to 0x%08x @ 0x%px (%pS)\n", |
78 | index, cond, len, from, replacement); | 81 | index, cond, len, replacement, from, from); |
79 | 82 | ||
80 | /* Replace instruction */ | 83 | if (len < 0) { |
81 | *from = replacement; | 84 | /* Replace multiple instruction by new code */ |
85 | u32 *source; | ||
86 | len = -len; | ||
87 | source = (u32 *)((ulong)&entry->replacement + entry->replacement); | ||
88 | memcpy(from, source, 4 * len); | ||
89 | } else { | ||
90 | /* Replace by one instruction */ | ||
91 | *from = replacement; | ||
92 | } | ||
82 | applied++; | 93 | applied++; |
83 | } | 94 | } |
84 | 95 | ||