1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Alternative live-patching for parisc. |
4 | * Copyright (C) 2018 Helge Deller <deller@gmx.de> |
5 | * |
6 | */ |
7 | |
8 | #include <asm/processor.h> |
9 | #include <asm/sections.h> |
10 | #include <asm/alternative.h> |
11 | #include <asm/cacheflush.h> |
12 | |
13 | #include <linux/module.h> |
14 | |
15 | static int no_alternatives; |
16 | static int __init setup_no_alternatives(char *str) |
17 | { |
18 | no_alternatives = 1; |
19 | return 1; |
20 | } |
21 | __setup("no-alternatives" , setup_no_alternatives); |
22 | |
23 | void __init_or_module apply_alternatives(struct alt_instr *start, |
24 | struct alt_instr *end, const char *module_name) |
25 | { |
26 | struct alt_instr *entry; |
27 | int index = 0, applied = 0; |
28 | int num_cpus = num_present_cpus(); |
29 | u16 cond_check; |
30 | |
31 | cond_check = ALT_COND_ALWAYS | |
32 | ((num_cpus == 1) ? ALT_COND_NO_SMP : 0) | |
33 | ((cache_info.dc_size == 0) ? ALT_COND_NO_DCACHE : 0) | |
34 | ((cache_info.ic_size == 0) ? ALT_COND_NO_ICACHE : 0) | |
35 | (running_on_qemu ? ALT_COND_RUN_ON_QEMU : 0) | |
36 | ((split_tlb == 0) ? ALT_COND_NO_SPLIT_TLB : 0) | |
37 | /* |
38 | * If the PDC_MODEL capabilities has Non-coherent IO-PDIR bit |
39 | * set (bit #61, big endian), we have to flush and sync every |
40 | * time IO-PDIR is changed in Ike/Astro. |
41 | */ |
42 | (((boot_cpu_data.cpu_type > pcxw_) && |
43 | ((boot_cpu_data.pdc.capabilities & PDC_MODEL_IOPDIR_FDC) == 0)) |
44 | ? ALT_COND_NO_IOC_FDC : 0); |
45 | |
46 | for (entry = start; entry < end; entry++, index++) { |
47 | |
48 | u32 *from, replacement; |
49 | u16 cond; |
50 | s16 len; |
51 | |
52 | from = (u32 *)((ulong)&entry->orig_offset + entry->orig_offset); |
53 | len = entry->len; |
54 | cond = entry->cond; |
55 | replacement = entry->replacement; |
56 | |
57 | WARN_ON(!cond); |
58 | |
59 | if ((cond & ALT_COND_ALWAYS) == 0 && no_alternatives) |
60 | continue; |
61 | |
62 | pr_debug("Check %d: Cond 0x%x, Replace %02d instructions @ 0x%px with 0x%08x\n" , |
63 | index, cond, len, from, replacement); |
64 | |
65 | /* Bounce out if none of the conditions are true. */ |
66 | if ((cond & cond_check) == 0) |
67 | continue; |
68 | |
69 | /* Want to replace pdtlb by a pdtlb,l instruction? */ |
70 | if (replacement == INSN_PxTLB) { |
71 | replacement = *from; |
72 | if (boot_cpu_data.cpu_type >= pcxu) /* >= pa2.0 ? */ |
73 | replacement |= (1 << 10); /* set el bit */ |
74 | } |
75 | |
76 | /* |
77 | * Replace instruction with NOPs? |
78 | * For long distance insert a branch instruction instead. |
79 | */ |
80 | if (replacement == INSN_NOP && len > 1) |
81 | replacement = 0xe8000002 + (len-2)*8; /* "b,n .+8" */ |
82 | |
83 | pr_debug("ALTERNATIVE %3d: Cond %2x, Replace %2d instructions to 0x%08x @ 0x%px (%pS)\n" , |
84 | index, cond, len, replacement, from, from); |
85 | |
86 | if (len < 0) { |
87 | /* Replace multiple instruction by new code */ |
88 | u32 *source; |
89 | len = -len; |
90 | source = (u32 *)((ulong)&entry->replacement + entry->replacement); |
91 | memcpy(from, source, 4 * len); |
92 | } else { |
93 | /* Replace by one instruction */ |
94 | *from = replacement; |
95 | } |
96 | applied++; |
97 | } |
98 | |
99 | pr_info("%s%salternatives: applied %d out of %d patches\n" , |
100 | module_name ? : "" , module_name ? " " : "" , |
101 | applied, index); |
102 | } |
103 | |
104 | |
105 | void __init apply_alternatives_all(void) |
106 | { |
107 | set_kernel_text_rw(1); |
108 | |
109 | apply_alternatives((struct alt_instr *) &__alt_instructions, |
110 | (struct alt_instr *) &__alt_instructions_end, NULL); |
111 | |
112 | if (cache_info.dc_size == 0 && cache_info.ic_size == 0) { |
113 | pr_info("alternatives: optimizing cache-flushes.\n" ); |
114 | static_branch_disable(&parisc_has_cache); |
115 | } |
116 | if (cache_info.dc_size == 0) |
117 | static_branch_disable(&parisc_has_dcache); |
118 | if (cache_info.ic_size == 0) |
119 | static_branch_disable(&parisc_has_icache); |
120 | |
121 | set_kernel_text_rw(0); |
122 | } |
123 | |