Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/parisc/kernel/alternative.c
26292 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Alternative live-patching for parisc.
4
* Copyright (C) 2018 Helge Deller <[email protected]>
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
124