aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHelge Deller <deller@gmx.de>2018-10-16 16:38:22 -0400
committerHelge Deller <deller@gmx.de>2018-10-17 11:22:26 -0400
commit3847dab77421867fbc77faacb2f377d44e729e1b (patch)
treeac712f1c1ee3cebd0a72f11b48a68e33a8888cd8
parent34c201ae49fe9e0bf3b389da5869d810f201c740 (diff)
parisc: Add alternative coding infrastructure
This patch adds the necessary code to patch a running kernel at runtime to improve performance. The current implementation offers a few optimizations variants: - When running a SMP kernel on a single UP processor, unwanted assembler statements like locking functions are overwritten with NOPs. When multiple instructions shall be skipped, one branch instruction is used instead of multiple nop instructions. - In the UP case, some pdtlb and pitlb instructions are patched to become pdtlb,l and pitlb,l which only flushes the CPU-local tlb entries instead of broadcasting the flush to other CPUs in the system and thus may improve performance. - fic and fdc instructions are skipped if no I- or D-caches are installed. This should speed up qemu emulation and cacheless systems. - If no cache coherence is needed for IO operations, the relevant fdc and sync instructions in the sba and ccio drivers are replaced by nops. - On systems which share I- and D-TLBs and thus don't have a seperate instruction TLB, the pitlb instruction is replaced by a nop. Live-patching is done early in the boot process, just after having run the system inventory. No drivers are running and thus no external interrupts should arrive. So the hope is that no TLB exceptions will occur during the patching. If this turns out to be wrong we will probably need to do the patching in real-mode. Signed-off-by: Helge Deller <deller@gmx.de>
-rw-r--r--arch/parisc/include/asm/alternative.h47
-rw-r--r--arch/parisc/include/asm/cache.h22
-rw-r--r--arch/parisc/include/asm/pgtable.h3
-rw-r--r--arch/parisc/include/asm/sections.h2
-rw-r--r--arch/parisc/include/asm/tlbflush.h3
-rw-r--r--arch/parisc/kernel/cache.c12
-rw-r--r--arch/parisc/kernel/entry.S10
-rw-r--r--arch/parisc/kernel/pacache.S64
-rw-r--r--arch/parisc/kernel/setup.c81
-rw-r--r--arch/parisc/kernel/signal.c1
-rw-r--r--arch/parisc/kernel/vmlinux.lds.S6
-rw-r--r--arch/parisc/mm/init.c15
-rw-r--r--drivers/parisc/ccio-dma.c12
-rw-r--r--drivers/parisc/sba_iommu.c17
14 files changed, 233 insertions, 62 deletions
diff --git a/arch/parisc/include/asm/alternative.h b/arch/parisc/include/asm/alternative.h
new file mode 100644
index 000000000000..bf485a94d0b4
--- /dev/null
+++ b/arch/parisc/include/asm/alternative.h
@@ -0,0 +1,47 @@
1/* SPDX-License-Identifier: GPL-2.0 */
2#ifndef __ASM_PARISC_ALTERNATIVE_H
3#define __ASM_PARISC_ALTERNATIVE_H
4
5#define ALT_COND_NO_SMP 0x01 /* when running UP instead of SMP */
6#define ALT_COND_NO_DCACHE 0x02 /* if system has no d-cache */
7#define ALT_COND_NO_ICACHE 0x04 /* if system has no i-cache */
8#define ALT_COND_NO_SPLIT_TLB 0x08 /* if split_tlb == 0 */
9#define ALT_COND_NO_IOC_FDC 0x10 /* if I/O cache does not need flushes */
10
11#define INSN_PxTLB 0x02 /* modify pdtlb, pitlb */
12#define INSN_NOP 0x08000240 /* nop */
13
14#ifndef __ASSEMBLY__
15
16#include <linux/init.h>
17#include <linux/types.h>
18#include <linux/stddef.h>
19#include <linux/stringify.h>
20
21struct alt_instr {
22 s32 orig_offset; /* offset to original instructions */
23 u32 len; /* end of original instructions */
24 u32 cond; /* see ALT_COND_XXX */
25 u32 replacement; /* replacement instruction or code */
26};
27
28void set_kernel_text_rw(int enable_read_write);
29
30/* Alternative SMP implementation. */
31#define ALTERNATIVE(cond, replacement) "!0:" \
32 ".section .altinstructions, \"aw\" !" \
33 ".word (0b-4-.), 1, " __stringify(cond) "," \
34 __stringify(replacement) " !" \
35 ".previous"
36
37#else
38
39#define ALTERNATIVE(from, to, cond, replacement)\
40 .section .altinstructions, "aw" ! \
41 .word (from - .), (to - from)/4 ! \
42 .word cond, replacement ! \
43 .previous
44
45#endif /* __ASSEMBLY__ */
46
47#endif /* __ASM_PARISC_ALTERNATIVE_H */
diff --git a/arch/parisc/include/asm/cache.h b/arch/parisc/include/asm/cache.h
index 150b7f30ea90..006fb939cac8 100644
--- a/arch/parisc/include/asm/cache.h
+++ b/arch/parisc/include/asm/cache.h
@@ -6,6 +6,7 @@
6#ifndef __ARCH_PARISC_CACHE_H 6#ifndef __ARCH_PARISC_CACHE_H
7#define __ARCH_PARISC_CACHE_H 7#define __ARCH_PARISC_CACHE_H
8 8
9#include <asm/alternative.h>
9 10
10/* 11/*
11 * PA 2.0 processors have 64 and 128-byte L2 cachelines; PA 1.1 processors 12 * PA 2.0 processors have 64 and 128-byte L2 cachelines; PA 1.1 processors
@@ -41,9 +42,24 @@ extern int icache_stride;
41extern struct pdc_cache_info cache_info; 42extern struct pdc_cache_info cache_info;
42void parisc_setup_cache_timing(void); 43void parisc_setup_cache_timing(void);
43 44
44#define pdtlb(addr) asm volatile("pdtlb 0(%%sr1,%0)" : : "r" (addr)); 45#define pdtlb(addr) asm volatile("pdtlb 0(%%sr1,%0)" \
45#define pitlb(addr) asm volatile("pitlb 0(%%sr1,%0)" : : "r" (addr)); 46 ALTERNATIVE(ALT_COND_NO_SMP, INSN_PxTLB) \
46#define pdtlb_kernel(addr) asm volatile("pdtlb 0(%0)" : : "r" (addr)); 47 : : "r" (addr))
48#define pitlb(addr) asm volatile("pitlb 0(%%sr1,%0)" \
49 ALTERNATIVE(ALT_COND_NO_SMP, INSN_PxTLB) \
50 ALTERNATIVE(ALT_COND_NO_SPLIT_TLB, INSN_NOP) \
51 : : "r" (addr))
52#define pdtlb_kernel(addr) asm volatile("pdtlb 0(%0)" \
53 ALTERNATIVE(ALT_COND_NO_SMP, INSN_PxTLB) \
54 : : "r" (addr))
55
56#define asm_io_fdc(addr) asm volatile("fdc %%r0(%0)" \
57 ALTERNATIVE(ALT_COND_NO_DCACHE, INSN_NOP) \
58 ALTERNATIVE(ALT_COND_NO_IOC_FDC, INSN_NOP) \
59 : : "r" (addr))
60#define asm_io_sync() asm volatile("sync" \
61 ALTERNATIVE(ALT_COND_NO_DCACHE, INSN_NOP) \
62 ALTERNATIVE(ALT_COND_NO_IOC_FDC, INSN_NOP) :: )
47 63
48#endif /* ! __ASSEMBLY__ */ 64#endif /* ! __ASSEMBLY__ */
49 65
diff --git a/arch/parisc/include/asm/pgtable.h b/arch/parisc/include/asm/pgtable.h
index b86c31291f0a..94c0ef7a9e03 100644
--- a/arch/parisc/include/asm/pgtable.h
+++ b/arch/parisc/include/asm/pgtable.h
@@ -43,8 +43,7 @@ static inline void purge_tlb_entries(struct mm_struct *mm, unsigned long addr)
43{ 43{
44 mtsp(mm->context, 1); 44 mtsp(mm->context, 1);
45 pdtlb(addr); 45 pdtlb(addr);
46 if (unlikely(split_tlb)) 46 pitlb(addr);
47 pitlb(addr);
48} 47}
49 48
50/* Certain architectures need to do special things when PTEs 49/* Certain architectures need to do special things when PTEs
diff --git a/arch/parisc/include/asm/sections.h b/arch/parisc/include/asm/sections.h
index 5a40b51df80c..bb52aea0cb21 100644
--- a/arch/parisc/include/asm/sections.h
+++ b/arch/parisc/include/asm/sections.h
@@ -5,6 +5,8 @@
5/* nothing to see, move along */ 5/* nothing to see, move along */
6#include <asm-generic/sections.h> 6#include <asm-generic/sections.h>
7 7
8extern char __alt_instructions[], __alt_instructions_end[];
9
8#ifdef CONFIG_64BIT 10#ifdef CONFIG_64BIT
9 11
10#define HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR 1 12#define HAVE_DEREFERENCE_FUNCTION_DESCRIPTOR 1
diff --git a/arch/parisc/include/asm/tlbflush.h b/arch/parisc/include/asm/tlbflush.h
index 14668bd52d60..6804374efa66 100644
--- a/arch/parisc/include/asm/tlbflush.h
+++ b/arch/parisc/include/asm/tlbflush.h
@@ -85,8 +85,7 @@ static inline void flush_tlb_page(struct vm_area_struct *vma,
85 purge_tlb_start(flags); 85 purge_tlb_start(flags);
86 mtsp(sid, 1); 86 mtsp(sid, 1);
87 pdtlb(addr); 87 pdtlb(addr);
88 if (unlikely(split_tlb)) 88 pitlb(addr);
89 pitlb(addr);
90 purge_tlb_end(flags); 89 purge_tlb_end(flags);
91} 90}
92#endif 91#endif
diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c
index 9f1c29d06574..56dc9791ee23 100644
--- a/arch/parisc/kernel/cache.c
+++ b/arch/parisc/kernel/cache.c
@@ -479,18 +479,6 @@ int __flush_tlb_range(unsigned long sid, unsigned long start,
479 /* Purge TLB entries for small ranges using the pdtlb and 479 /* Purge TLB entries for small ranges using the pdtlb and
480 pitlb instructions. These instructions execute locally 480 pitlb instructions. These instructions execute locally
481 but cause a purge request to be broadcast to other TLBs. */ 481 but cause a purge request to be broadcast to other TLBs. */
482 if (likely(!split_tlb)) {
483 while (start < end) {
484 purge_tlb_start(flags);
485 mtsp(sid, 1);
486 pdtlb(start);
487 purge_tlb_end(flags);
488 start += PAGE_SIZE;
489 }
490 return 0;
491 }
492
493 /* split TLB case */
494 while (start < end) { 482 while (start < end) {
495 purge_tlb_start(flags); 483 purge_tlb_start(flags);
496 mtsp(sid, 1); 484 mtsp(sid, 1);
diff --git a/arch/parisc/kernel/entry.S b/arch/parisc/kernel/entry.S
index 7c85a91b4710..d3e2633cd688 100644
--- a/arch/parisc/kernel/entry.S
+++ b/arch/parisc/kernel/entry.S
@@ -38,6 +38,7 @@
38#include <asm/ldcw.h> 38#include <asm/ldcw.h>
39#include <asm/traps.h> 39#include <asm/traps.h>
40#include <asm/thread_info.h> 40#include <asm/thread_info.h>
41#include <asm/alternative.h>
41 42
42#include <linux/linkage.h> 43#include <linux/linkage.h>
43 44
@@ -464,7 +465,7 @@
464 /* Acquire pa_tlb_lock lock and check page is present. */ 465 /* Acquire pa_tlb_lock lock and check page is present. */
465 .macro tlb_lock spc,ptp,pte,tmp,tmp1,fault 466 .macro tlb_lock spc,ptp,pte,tmp,tmp1,fault
466#ifdef CONFIG_SMP 467#ifdef CONFIG_SMP
467 cmpib,COND(=),n 0,\spc,2f 46898: cmpib,COND(=),n 0,\spc,2f
468 load_pa_tlb_lock \tmp 469 load_pa_tlb_lock \tmp
4691: LDCW 0(\tmp),\tmp1 4701: LDCW 0(\tmp),\tmp1
470 cmpib,COND(=) 0,\tmp1,1b 471 cmpib,COND(=) 0,\tmp1,1b
@@ -473,6 +474,7 @@
473 bb,<,n \pte,_PAGE_PRESENT_BIT,3f 474 bb,<,n \pte,_PAGE_PRESENT_BIT,3f
474 b \fault 475 b \fault
475 stw,ma \spc,0(\tmp) 476 stw,ma \spc,0(\tmp)
47799: ALTERNATIVE(98b, 99b, ALT_COND_NO_SMP, INSN_NOP)
476#endif 478#endif
4772: LDREG 0(\ptp),\pte 4792: LDREG 0(\ptp),\pte
478 bb,>=,n \pte,_PAGE_PRESENT_BIT,\fault 480 bb,>=,n \pte,_PAGE_PRESENT_BIT,\fault
@@ -482,15 +484,17 @@
482 /* Release pa_tlb_lock lock without reloading lock address. */ 484 /* Release pa_tlb_lock lock without reloading lock address. */
483 .macro tlb_unlock0 spc,tmp 485 .macro tlb_unlock0 spc,tmp
484#ifdef CONFIG_SMP 486#ifdef CONFIG_SMP
485 or,COND(=) %r0,\spc,%r0 48798: or,COND(=) %r0,\spc,%r0
486 stw,ma \spc,0(\tmp) 488 stw,ma \spc,0(\tmp)
48999: ALTERNATIVE(98b, 99b, ALT_COND_NO_SMP, INSN_NOP)
487#endif 490#endif
488 .endm 491 .endm
489 492
490 /* Release pa_tlb_lock lock. */ 493 /* Release pa_tlb_lock lock. */
491 .macro tlb_unlock1 spc,tmp 494 .macro tlb_unlock1 spc,tmp
492#ifdef CONFIG_SMP 495#ifdef CONFIG_SMP
493 load_pa_tlb_lock \tmp 49698: load_pa_tlb_lock \tmp
49799: ALTERNATIVE(98b, 99b, ALT_COND_NO_SMP, INSN_NOP)
494 tlb_unlock0 \spc,\tmp 498 tlb_unlock0 \spc,\tmp
495#endif 499#endif
496 .endm 500 .endm
diff --git a/arch/parisc/kernel/pacache.S b/arch/parisc/kernel/pacache.S
index f33bf2d306d6..b41c0136a05f 100644
--- a/arch/parisc/kernel/pacache.S
+++ b/arch/parisc/kernel/pacache.S
@@ -37,6 +37,7 @@
37#include <asm/pgtable.h> 37#include <asm/pgtable.h>
38#include <asm/cache.h> 38#include <asm/cache.h>
39#include <asm/ldcw.h> 39#include <asm/ldcw.h>
40#include <asm/alternative.h>
40#include <linux/linkage.h> 41#include <linux/linkage.h>
41#include <linux/init.h> 42#include <linux/init.h>
42 43
@@ -190,7 +191,7 @@ ENDPROC_CFI(flush_tlb_all_local)
190 .import cache_info,data 191 .import cache_info,data
191 192
192ENTRY_CFI(flush_instruction_cache_local) 193ENTRY_CFI(flush_instruction_cache_local)
193 load32 cache_info, %r1 19488: load32 cache_info, %r1
194 195
195 /* Flush Instruction Cache */ 196 /* Flush Instruction Cache */
196 197
@@ -243,6 +244,7 @@ fioneloop2:
243fisync: 244fisync:
244 sync 245 sync
245 mtsm %r22 /* restore I-bit */ 246 mtsm %r22 /* restore I-bit */
24789: ALTERNATIVE(88b, 89b, ALT_COND_NO_ICACHE, INSN_NOP)
246 bv %r0(%r2) 248 bv %r0(%r2)
247 nop 249 nop
248ENDPROC_CFI(flush_instruction_cache_local) 250ENDPROC_CFI(flush_instruction_cache_local)
@@ -250,7 +252,7 @@ ENDPROC_CFI(flush_instruction_cache_local)
250 252
251 .import cache_info, data 253 .import cache_info, data
252ENTRY_CFI(flush_data_cache_local) 254ENTRY_CFI(flush_data_cache_local)
253 load32 cache_info, %r1 25588: load32 cache_info, %r1
254 256
255 /* Flush Data Cache */ 257 /* Flush Data Cache */
256 258
@@ -304,6 +306,7 @@ fdsync:
304 syncdma 306 syncdma
305 sync 307 sync
306 mtsm %r22 /* restore I-bit */ 308 mtsm %r22 /* restore I-bit */
30989: ALTERNATIVE(88b, 89b, ALT_COND_NO_DCACHE, INSN_NOP)
307 bv %r0(%r2) 310 bv %r0(%r2)
308 nop 311 nop
309ENDPROC_CFI(flush_data_cache_local) 312ENDPROC_CFI(flush_data_cache_local)
@@ -312,6 +315,7 @@ ENDPROC_CFI(flush_data_cache_local)
312 315
313 .macro tlb_lock la,flags,tmp 316 .macro tlb_lock la,flags,tmp
314#ifdef CONFIG_SMP 317#ifdef CONFIG_SMP
31898:
315#if __PA_LDCW_ALIGNMENT > 4 319#if __PA_LDCW_ALIGNMENT > 4
316 load32 pa_tlb_lock + __PA_LDCW_ALIGNMENT-1, \la 320 load32 pa_tlb_lock + __PA_LDCW_ALIGNMENT-1, \la
317 depi 0,31,__PA_LDCW_ALIGN_ORDER, \la 321 depi 0,31,__PA_LDCW_ALIGN_ORDER, \la
@@ -326,15 +330,17 @@ ENDPROC_CFI(flush_data_cache_local)
326 nop 330 nop
327 b,n 2b 331 b,n 2b
3283: 3323:
33399: ALTERNATIVE(98b, 99b, ALT_COND_NO_SMP, INSN_NOP)
329#endif 334#endif
330 .endm 335 .endm
331 336
332 .macro tlb_unlock la,flags,tmp 337 .macro tlb_unlock la,flags,tmp
333#ifdef CONFIG_SMP 338#ifdef CONFIG_SMP
334 ldi 1,\tmp 33998: ldi 1,\tmp
335 sync 340 sync
336 stw \tmp,0(\la) 341 stw \tmp,0(\la)
337 mtsm \flags 342 mtsm \flags
34399: ALTERNATIVE(98b, 99b, ALT_COND_NO_SMP, INSN_NOP)
338#endif 344#endif
339 .endm 345 .endm
340 346
@@ -596,9 +602,11 @@ ENTRY_CFI(copy_user_page_asm)
596 pdtlb,l %r0(%r29) 602 pdtlb,l %r0(%r29)
597#else 603#else
598 tlb_lock %r20,%r21,%r22 604 tlb_lock %r20,%r21,%r22
599 pdtlb %r0(%r28) 6050: pdtlb %r0(%r28)
600 pdtlb %r0(%r29) 6061: pdtlb %r0(%r29)
601 tlb_unlock %r20,%r21,%r22 607 tlb_unlock %r20,%r21,%r22
608 ALTERNATIVE(0b, 0b+4, ALT_COND_NO_SMP, INSN_PxTLB)
609 ALTERNATIVE(1b, 1b+4, ALT_COND_NO_SMP, INSN_PxTLB)
602#endif 610#endif
603 611
604#ifdef CONFIG_64BIT 612#ifdef CONFIG_64BIT
@@ -736,8 +744,9 @@ ENTRY_CFI(clear_user_page_asm)
736 pdtlb,l %r0(%r28) 744 pdtlb,l %r0(%r28)
737#else 745#else
738 tlb_lock %r20,%r21,%r22 746 tlb_lock %r20,%r21,%r22
739 pdtlb %r0(%r28) 7470: pdtlb %r0(%r28)
740 tlb_unlock %r20,%r21,%r22 748 tlb_unlock %r20,%r21,%r22
749 ALTERNATIVE(0b, 0b+4, ALT_COND_NO_SMP, INSN_PxTLB)
741#endif 750#endif
742 751
743#ifdef CONFIG_64BIT 752#ifdef CONFIG_64BIT
@@ -813,11 +822,12 @@ ENTRY_CFI(flush_dcache_page_asm)
813 pdtlb,l %r0(%r28) 822 pdtlb,l %r0(%r28)
814#else 823#else
815 tlb_lock %r20,%r21,%r22 824 tlb_lock %r20,%r21,%r22
816 pdtlb %r0(%r28) 8250: pdtlb %r0(%r28)
817 tlb_unlock %r20,%r21,%r22 826 tlb_unlock %r20,%r21,%r22
827 ALTERNATIVE(0b, 0b+4, ALT_COND_NO_SMP, INSN_PxTLB)
818#endif 828#endif
819 829
820 ldil L%dcache_stride, %r1 83088: ldil L%dcache_stride, %r1
821 ldw R%dcache_stride(%r1), r31 831 ldw R%dcache_stride(%r1), r31
822 832
823#ifdef CONFIG_64BIT 833#ifdef CONFIG_64BIT
@@ -847,6 +857,7 @@ ENTRY_CFI(flush_dcache_page_asm)
847 cmpb,COND(<<) %r28, %r25,1b 857 cmpb,COND(<<) %r28, %r25,1b
848 fdc,m r31(%r28) 858 fdc,m r31(%r28)
849 859
86089: ALTERNATIVE(88b, 89b, ALT_COND_NO_DCACHE, INSN_NOP)
850 sync 861 sync
851 bv %r0(%r2) 862 bv %r0(%r2)
852 nop 863 nop
@@ -874,15 +885,19 @@ ENTRY_CFI(flush_icache_page_asm)
874 885
875#ifdef CONFIG_PA20 886#ifdef CONFIG_PA20
876 pdtlb,l %r0(%r28) 887 pdtlb,l %r0(%r28)
877 pitlb,l %r0(%sr4,%r28) 8881: pitlb,l %r0(%sr4,%r28)
889 ALTERNATIVE(1b, 1b+4, ALT_COND_NO_SPLIT_TLB, INSN_NOP)
878#else 890#else
879 tlb_lock %r20,%r21,%r22 891 tlb_lock %r20,%r21,%r22
880 pdtlb %r0(%r28) 8920: pdtlb %r0(%r28)
881 pitlb %r0(%sr4,%r28) 8931: pitlb %r0(%sr4,%r28)
882 tlb_unlock %r20,%r21,%r22 894 tlb_unlock %r20,%r21,%r22
895 ALTERNATIVE(0b, 0b+4, ALT_COND_NO_SMP, INSN_PxTLB)
896 ALTERNATIVE(1b, 1b+4, ALT_COND_NO_SMP, INSN_PxTLB)
897 ALTERNATIVE(1b, 1b+4, ALT_COND_NO_SPLIT_TLB, INSN_NOP)
883#endif 898#endif
884 899
885 ldil L%icache_stride, %r1 90088: ldil L%icache_stride, %r1
886 ldw R%icache_stride(%r1), %r31 901 ldw R%icache_stride(%r1), %r31
887 902
888#ifdef CONFIG_64BIT 903#ifdef CONFIG_64BIT
@@ -914,13 +929,14 @@ ENTRY_CFI(flush_icache_page_asm)
914 cmpb,COND(<<) %r28, %r25,1b 929 cmpb,COND(<<) %r28, %r25,1b
915 fic,m %r31(%sr4,%r28) 930 fic,m %r31(%sr4,%r28)
916 931
93289: ALTERNATIVE(88b, 89b, ALT_COND_NO_ICACHE, INSN_NOP)
917 sync 933 sync
918 bv %r0(%r2) 934 bv %r0(%r2)
919 nop 935 nop
920ENDPROC_CFI(flush_icache_page_asm) 936ENDPROC_CFI(flush_icache_page_asm)
921 937
922ENTRY_CFI(flush_kernel_dcache_page_asm) 938ENTRY_CFI(flush_kernel_dcache_page_asm)
923 ldil L%dcache_stride, %r1 93988: ldil L%dcache_stride, %r1
924 ldw R%dcache_stride(%r1), %r23 940 ldw R%dcache_stride(%r1), %r23
925 941
926#ifdef CONFIG_64BIT 942#ifdef CONFIG_64BIT
@@ -950,13 +966,14 @@ ENTRY_CFI(flush_kernel_dcache_page_asm)
950 cmpb,COND(<<) %r26, %r25,1b 966 cmpb,COND(<<) %r26, %r25,1b
951 fdc,m %r23(%r26) 967 fdc,m %r23(%r26)
952 968
96989: ALTERNATIVE(88b, 89b, ALT_COND_NO_DCACHE, INSN_NOP)
953 sync 970 sync
954 bv %r0(%r2) 971 bv %r0(%r2)
955 nop 972 nop
956ENDPROC_CFI(flush_kernel_dcache_page_asm) 973ENDPROC_CFI(flush_kernel_dcache_page_asm)
957 974
958ENTRY_CFI(purge_kernel_dcache_page_asm) 975ENTRY_CFI(purge_kernel_dcache_page_asm)
959 ldil L%dcache_stride, %r1 97688: ldil L%dcache_stride, %r1
960 ldw R%dcache_stride(%r1), %r23 977 ldw R%dcache_stride(%r1), %r23
961 978
962#ifdef CONFIG_64BIT 979#ifdef CONFIG_64BIT
@@ -985,13 +1002,14 @@ ENTRY_CFI(purge_kernel_dcache_page_asm)
985 cmpb,COND(<<) %r26, %r25, 1b 1002 cmpb,COND(<<) %r26, %r25, 1b
986 pdc,m %r23(%r26) 1003 pdc,m %r23(%r26)
987 1004
100589: ALTERNATIVE(88b, 89b, ALT_COND_NO_DCACHE, INSN_NOP)
988 sync 1006 sync
989 bv %r0(%r2) 1007 bv %r0(%r2)
990 nop 1008 nop
991ENDPROC_CFI(purge_kernel_dcache_page_asm) 1009ENDPROC_CFI(purge_kernel_dcache_page_asm)
992 1010
993ENTRY_CFI(flush_user_dcache_range_asm) 1011ENTRY_CFI(flush_user_dcache_range_asm)
994 ldil L%dcache_stride, %r1 101288: ldil L%dcache_stride, %r1
995 ldw R%dcache_stride(%r1), %r23 1013 ldw R%dcache_stride(%r1), %r23
996 ldo -1(%r23), %r21 1014 ldo -1(%r23), %r21
997 ANDCM %r26, %r21, %r26 1015 ANDCM %r26, %r21, %r26
@@ -999,13 +1017,14 @@ ENTRY_CFI(flush_user_dcache_range_asm)
9991: cmpb,COND(<<),n %r26, %r25, 1b 10171: cmpb,COND(<<),n %r26, %r25, 1b
1000 fdc,m %r23(%sr3, %r26) 1018 fdc,m %r23(%sr3, %r26)
1001 1019
102089: ALTERNATIVE(88b, 89b, ALT_COND_NO_DCACHE, INSN_NOP)
1002 sync 1021 sync
1003 bv %r0(%r2) 1022 bv %r0(%r2)
1004 nop 1023 nop
1005ENDPROC_CFI(flush_user_dcache_range_asm) 1024ENDPROC_CFI(flush_user_dcache_range_asm)
1006 1025
1007ENTRY_CFI(flush_kernel_dcache_range_asm) 1026ENTRY_CFI(flush_kernel_dcache_range_asm)
1008 ldil L%dcache_stride, %r1 102788: ldil L%dcache_stride, %r1
1009 ldw R%dcache_stride(%r1), %r23 1028 ldw R%dcache_stride(%r1), %r23
1010 ldo -1(%r23), %r21 1029 ldo -1(%r23), %r21
1011 ANDCM %r26, %r21, %r26 1030 ANDCM %r26, %r21, %r26
@@ -1014,13 +1033,14 @@ ENTRY_CFI(flush_kernel_dcache_range_asm)
1014 fdc,m %r23(%r26) 1033 fdc,m %r23(%r26)
1015 1034
1016 sync 1035 sync
103689: ALTERNATIVE(88b, 89b, ALT_COND_NO_DCACHE, INSN_NOP)
1017 syncdma 1037 syncdma
1018 bv %r0(%r2) 1038 bv %r0(%r2)
1019 nop 1039 nop
1020ENDPROC_CFI(flush_kernel_dcache_range_asm) 1040ENDPROC_CFI(flush_kernel_dcache_range_asm)
1021 1041
1022ENTRY_CFI(purge_kernel_dcache_range_asm) 1042ENTRY_CFI(purge_kernel_dcache_range_asm)
1023 ldil L%dcache_stride, %r1 104388: ldil L%dcache_stride, %r1
1024 ldw R%dcache_stride(%r1), %r23 1044 ldw R%dcache_stride(%r1), %r23
1025 ldo -1(%r23), %r21 1045 ldo -1(%r23), %r21
1026 ANDCM %r26, %r21, %r26 1046 ANDCM %r26, %r21, %r26
@@ -1029,13 +1049,14 @@ ENTRY_CFI(purge_kernel_dcache_range_asm)
1029 pdc,m %r23(%r26) 1049 pdc,m %r23(%r26)
1030 1050
1031 sync 1051 sync
105289: ALTERNATIVE(88b, 89b, ALT_COND_NO_DCACHE, INSN_NOP)
1032 syncdma 1053 syncdma
1033 bv %r0(%r2) 1054 bv %r0(%r2)
1034 nop 1055 nop
1035ENDPROC_CFI(purge_kernel_dcache_range_asm) 1056ENDPROC_CFI(purge_kernel_dcache_range_asm)
1036 1057
1037ENTRY_CFI(flush_user_icache_range_asm) 1058ENTRY_CFI(flush_user_icache_range_asm)
1038 ldil L%icache_stride, %r1 105988: ldil L%icache_stride, %r1
1039 ldw R%icache_stride(%r1), %r23 1060 ldw R%icache_stride(%r1), %r23
1040 ldo -1(%r23), %r21 1061 ldo -1(%r23), %r21
1041 ANDCM %r26, %r21, %r26 1062 ANDCM %r26, %r21, %r26
@@ -1043,13 +1064,14 @@ ENTRY_CFI(flush_user_icache_range_asm)
10431: cmpb,COND(<<),n %r26, %r25,1b 10641: cmpb,COND(<<),n %r26, %r25,1b
1044 fic,m %r23(%sr3, %r26) 1065 fic,m %r23(%sr3, %r26)
1045 1066
106789: ALTERNATIVE(88b, 89b, ALT_COND_NO_ICACHE, INSN_NOP)
1046 sync 1068 sync
1047 bv %r0(%r2) 1069 bv %r0(%r2)
1048 nop 1070 nop
1049ENDPROC_CFI(flush_user_icache_range_asm) 1071ENDPROC_CFI(flush_user_icache_range_asm)
1050 1072
1051ENTRY_CFI(flush_kernel_icache_page) 1073ENTRY_CFI(flush_kernel_icache_page)
1052 ldil L%icache_stride, %r1 107488: ldil L%icache_stride, %r1
1053 ldw R%icache_stride(%r1), %r23 1075 ldw R%icache_stride(%r1), %r23
1054 1076
1055#ifdef CONFIG_64BIT 1077#ifdef CONFIG_64BIT
@@ -1079,13 +1101,14 @@ ENTRY_CFI(flush_kernel_icache_page)
1079 cmpb,COND(<<) %r26, %r25, 1b 1101 cmpb,COND(<<) %r26, %r25, 1b
1080 fic,m %r23(%sr4, %r26) 1102 fic,m %r23(%sr4, %r26)
1081 1103
110489: ALTERNATIVE(88b, 89b, ALT_COND_NO_ICACHE, INSN_NOP)
1082 sync 1105 sync
1083 bv %r0(%r2) 1106 bv %r0(%r2)
1084 nop 1107 nop
1085ENDPROC_CFI(flush_kernel_icache_page) 1108ENDPROC_CFI(flush_kernel_icache_page)
1086 1109
1087ENTRY_CFI(flush_kernel_icache_range_asm) 1110ENTRY_CFI(flush_kernel_icache_range_asm)
1088 ldil L%icache_stride, %r1 111188: ldil L%icache_stride, %r1
1089 ldw R%icache_stride(%r1), %r23 1112 ldw R%icache_stride(%r1), %r23
1090 ldo -1(%r23), %r21 1113 ldo -1(%r23), %r21
1091 ANDCM %r26, %r21, %r26 1114 ANDCM %r26, %r21, %r26
@@ -1093,6 +1116,7 @@ ENTRY_CFI(flush_kernel_icache_range_asm)
10931: cmpb,COND(<<),n %r26, %r25, 1b 11161: cmpb,COND(<<),n %r26, %r25, 1b
1094 fic,m %r23(%sr4, %r26) 1117 fic,m %r23(%sr4, %r26)
1095 1118
111989: ALTERNATIVE(88b, 89b, ALT_COND_NO_ICACHE, INSN_NOP)
1096 sync 1120 sync
1097 bv %r0(%r2) 1121 bv %r0(%r2)
1098 nop 1122 nop
diff --git a/arch/parisc/kernel/setup.c b/arch/parisc/kernel/setup.c
index 4e87c35c22b7..db6e7957f9a3 100644
--- a/arch/parisc/kernel/setup.c
+++ b/arch/parisc/kernel/setup.c
@@ -305,6 +305,86 @@ static int __init parisc_init_resources(void)
305 return 0; 305 return 0;
306} 306}
307 307
308static int no_alternatives __initdata;
309static int __init setup_no_alternatives(char *str)
310{
311 no_alternatives = 1;
312 return 1;
313}
314__setup("no-alternatives", setup_no_alternatives);
315
316static void __init apply_alternatives_all(void)
317{
318 struct alt_instr *entry;
319 int index = 0, applied = 0;
320
321
322 pr_info("alternatives: %spatching kernel code\n",
323 no_alternatives ? "NOT " : "");
324 if (no_alternatives)
325 return;
326
327 set_kernel_text_rw(1);
328
329 for (entry = (struct alt_instr *) &__alt_instructions;
330 entry < (struct alt_instr *) &__alt_instructions_end;
331 entry++, index++) {
332
333 u32 *from, len, cond, replacement;
334
335 from = (u32 *)((ulong)&entry->orig_offset + entry->orig_offset);
336 len = entry->len;
337 cond = entry->cond;
338 replacement = entry->replacement;
339
340 WARN_ON(!cond);
341 pr_debug("Check %d: Cond 0x%x, Replace %02d instructions @ 0x%px with 0x%08x\n",
342 index, cond, len, from, replacement);
343
344 if ((cond & ALT_COND_NO_SMP) && (num_online_cpus() != 1))
345 continue;
346 if ((cond & ALT_COND_NO_DCACHE) && (cache_info.dc_size != 0))
347 continue;
348 if ((cond & ALT_COND_NO_ICACHE) && (cache_info.ic_size != 0))
349 continue;
350
351 /*
352 * If the PDC_MODEL capabilities has Non-coherent IO-PDIR bit
353 * set (bit #61, big endian), we have to flush and sync every
354 * time IO-PDIR is changed in Ike/Astro.
355 */
356 if ((cond & ALT_COND_NO_IOC_FDC) &&
357 (boot_cpu_data.pdc.capabilities & PDC_MODEL_IOPDIR_FDC))
358 continue;
359
360 /* Want to replace pdtlb by a pdtlb,l instruction? */
361 if (replacement == INSN_PxTLB) {
362 replacement = *from;
363 if (boot_cpu_data.cpu_type >= pcxu) /* >= pa2.0 ? */
364 replacement |= (1 << 10); /* set el bit */
365 }
366
367 /*
368 * Replace instruction with NOPs?
369 * For long distance insert a branch instruction instead.
370 */
371 if (replacement == INSN_NOP && len > 1)
372 replacement = 0xe8000002 + (len-2)*8; /* "b,n .+8" */
373
374 pr_debug("Do %d: Cond 0x%x, Replace %02d instructions @ 0x%px with 0x%08x\n",
375 index, cond, len, from, replacement);
376
377 /* Replace instruction */
378 *from = replacement;
379 applied++;
380 }
381
382 pr_info("alternatives: applied %d out of %d patches\n", applied, index);
383
384 set_kernel_text_rw(0);
385}
386
387
308extern void gsc_init(void); 388extern void gsc_init(void);
309extern void processor_init(void); 389extern void processor_init(void);
310extern void ccio_init(void); 390extern void ccio_init(void);
@@ -346,6 +426,7 @@ static int __init parisc_init(void)
346 boot_cpu_data.cpu_hz / 1000000, 426 boot_cpu_data.cpu_hz / 1000000,
347 boot_cpu_data.cpu_hz % 1000000 ); 427 boot_cpu_data.cpu_hz % 1000000 );
348 428
429 apply_alternatives_all();
349 parisc_setup_cache_timing(); 430 parisc_setup_cache_timing();
350 431
351 /* These are in a non-obvious order, will fix when we have an iotree */ 432 /* These are in a non-obvious order, will fix when we have an iotree */
diff --git a/arch/parisc/kernel/signal.c b/arch/parisc/kernel/signal.c
index 342073f44d3f..848c1934680b 100644
--- a/arch/parisc/kernel/signal.c
+++ b/arch/parisc/kernel/signal.c
@@ -65,7 +65,6 @@
65#define INSN_LDI_R25_1 0x34190002 /* ldi 1,%r25 (in_syscall=1) */ 65#define INSN_LDI_R25_1 0x34190002 /* ldi 1,%r25 (in_syscall=1) */
66#define INSN_LDI_R20 0x3414015a /* ldi __NR_rt_sigreturn,%r20 */ 66#define INSN_LDI_R20 0x3414015a /* ldi __NR_rt_sigreturn,%r20 */
67#define INSN_BLE_SR2_R0 0xe4008200 /* be,l 0x100(%sr2,%r0),%sr0,%r31 */ 67#define INSN_BLE_SR2_R0 0xe4008200 /* be,l 0x100(%sr2,%r0),%sr0,%r31 */
68#define INSN_NOP 0x08000240 /* nop */
69/* For debugging */ 68/* For debugging */
70#define INSN_DIE_HORRIBLY 0x68000ccc /* stw %r0,0x666(%sr0,%r0) */ 69#define INSN_DIE_HORRIBLY 0x68000ccc /* stw %r0,0x666(%sr0,%r0) */
71 70
diff --git a/arch/parisc/kernel/vmlinux.lds.S b/arch/parisc/kernel/vmlinux.lds.S
index da2e31190efa..c3b1b9c24ede 100644
--- a/arch/parisc/kernel/vmlinux.lds.S
+++ b/arch/parisc/kernel/vmlinux.lds.S
@@ -61,6 +61,12 @@ SECTIONS
61 EXIT_DATA 61 EXIT_DATA
62 } 62 }
63 PERCPU_SECTION(8) 63 PERCPU_SECTION(8)
64 . = ALIGN(4);
65 .altinstructions : {
66 __alt_instructions = .;
67 *(.altinstructions)
68 __alt_instructions_end = .;
69 }
64 . = ALIGN(HUGEPAGE_SIZE); 70 . = ALIGN(HUGEPAGE_SIZE);
65 __init_end = .; 71 __init_end = .;
66 /* freed after init ends here */ 72 /* freed after init ends here */
diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c
index aae9b0d71c1e..e7e626bcd0be 100644
--- a/arch/parisc/mm/init.c
+++ b/arch/parisc/mm/init.c
@@ -511,6 +511,21 @@ static void __init map_pages(unsigned long start_vaddr,
511 } 511 }
512} 512}
513 513
514void __init set_kernel_text_rw(int enable_read_write)
515{
516 unsigned long start = (unsigned long)_stext;
517 unsigned long end = (unsigned long)_etext;
518
519 map_pages(start, __pa(start), end-start,
520 PAGE_KERNEL_RWX, enable_read_write ? 1:0);
521
522 /* force the kernel to see the new TLB entries */
523 __flush_tlb_range(0, start, end);
524
525 /* dump old cached instructions */
526 flush_icache_range(start, end);
527}
528
514void __ref free_initmem(void) 529void __ref free_initmem(void)
515{ 530{
516 unsigned long init_begin = (unsigned long)__init_begin; 531 unsigned long init_begin = (unsigned long)__init_begin;
diff --git a/drivers/parisc/ccio-dma.c b/drivers/parisc/ccio-dma.c
index 614823617b8b..701a7d6a74d5 100644
--- a/drivers/parisc/ccio-dma.c
+++ b/drivers/parisc/ccio-dma.c
@@ -609,14 +609,13 @@ ccio_io_pdir_entry(u64 *pdir_ptr, space_t sid, unsigned long vba,
609 ** PCX-T'? Don't know. (eg C110 or similar K-class) 609 ** PCX-T'? Don't know. (eg C110 or similar K-class)
610 ** 610 **
611 ** See PDC_MODEL/option 0/SW_CAP word for "Non-coherent IO-PDIR bit". 611 ** See PDC_MODEL/option 0/SW_CAP word for "Non-coherent IO-PDIR bit".
612 ** Hopefully we can patch (NOP) these out at boot time somehow.
613 ** 612 **
614 ** "Since PCX-U employs an offset hash that is incompatible with 613 ** "Since PCX-U employs an offset hash that is incompatible with
615 ** the real mode coherence index generation of U2, the PDIR entry 614 ** the real mode coherence index generation of U2, the PDIR entry
616 ** must be flushed to memory to retain coherence." 615 ** must be flushed to memory to retain coherence."
617 */ 616 */
618 asm volatile("fdc %%r0(%0)" : : "r" (pdir_ptr)); 617 asm_io_fdc(pdir_ptr);
619 asm volatile("sync"); 618 asm_io_sync();
620} 619}
621 620
622/** 621/**
@@ -682,17 +681,14 @@ ccio_mark_invalid(struct ioc *ioc, dma_addr_t iova, size_t byte_cnt)
682 ** FIXME: PCX_W platforms don't need FDC/SYNC. (eg C360) 681 ** FIXME: PCX_W platforms don't need FDC/SYNC. (eg C360)
683 ** PCX-U/U+ do. (eg C200/C240) 682 ** PCX-U/U+ do. (eg C200/C240)
684 ** See PDC_MODEL/option 0/SW_CAP for "Non-coherent IO-PDIR bit". 683 ** See PDC_MODEL/option 0/SW_CAP for "Non-coherent IO-PDIR bit".
685 **
686 ** Hopefully someone figures out how to patch (NOP) the
687 ** FDC/SYNC out at boot time.
688 */ 684 */
689 asm volatile("fdc %%r0(%0)" : : "r" (pdir_ptr[7])); 685 asm_io_fdc(pdir_ptr);
690 686
691 iovp += IOVP_SIZE; 687 iovp += IOVP_SIZE;
692 byte_cnt -= IOVP_SIZE; 688 byte_cnt -= IOVP_SIZE;
693 } 689 }
694 690
695 asm volatile("sync"); 691 asm_io_sync();
696 ccio_clear_io_tlb(ioc, CCIO_IOVP(iova), saved_byte_cnt); 692 ccio_clear_io_tlb(ioc, CCIO_IOVP(iova), saved_byte_cnt);
697} 693}
698 694
diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c
index 11de0eccf968..c1e599a429af 100644
--- a/drivers/parisc/sba_iommu.c
+++ b/drivers/parisc/sba_iommu.c
@@ -587,8 +587,7 @@ sba_io_pdir_entry(u64 *pdir_ptr, space_t sid, unsigned long vba,
587 * (bit #61, big endian), we have to flush and sync every time 587 * (bit #61, big endian), we have to flush and sync every time
588 * IO-PDIR is changed in Ike/Astro. 588 * IO-PDIR is changed in Ike/Astro.
589 */ 589 */
590 if (ioc_needs_fdc) 590 asm_io_fdc(pdir_ptr);
591 asm volatile("fdc %%r0(%0)" : : "r" (pdir_ptr));
592} 591}
593 592
594 593
@@ -641,8 +640,8 @@ sba_mark_invalid(struct ioc *ioc, dma_addr_t iova, size_t byte_cnt)
641 do { 640 do {
642 /* clear I/O Pdir entry "valid" bit first */ 641 /* clear I/O Pdir entry "valid" bit first */
643 ((u8 *) pdir_ptr)[7] = 0; 642 ((u8 *) pdir_ptr)[7] = 0;
643 asm_io_fdc(pdir_ptr);
644 if (ioc_needs_fdc) { 644 if (ioc_needs_fdc) {
645 asm volatile("fdc %%r0(%0)" : : "r" (pdir_ptr));
646#if 0 645#if 0
647 entries_per_cacheline = L1_CACHE_SHIFT - 3; 646 entries_per_cacheline = L1_CACHE_SHIFT - 3;
648#endif 647#endif
@@ -661,8 +660,7 @@ sba_mark_invalid(struct ioc *ioc, dma_addr_t iova, size_t byte_cnt)
661 ** could dump core on HPMC. 660 ** could dump core on HPMC.
662 */ 661 */
663 ((u8 *) pdir_ptr)[7] = 0; 662 ((u8 *) pdir_ptr)[7] = 0;
664 if (ioc_needs_fdc) 663 asm_io_fdc(pdir_ptr);
665 asm volatile("fdc %%r0(%0)" : : "r" (pdir_ptr));
666 664
667 WRITE_REG( SBA_IOVA(ioc, iovp, 0, 0), ioc->ioc_hpa+IOC_PCOM); 665 WRITE_REG( SBA_IOVA(ioc, iovp, 0, 0), ioc->ioc_hpa+IOC_PCOM);
668} 666}
@@ -773,8 +771,7 @@ sba_map_single(struct device *dev, void *addr, size_t size,
773 } 771 }
774 772
775 /* force FDC ops in io_pdir_entry() to be visible to IOMMU */ 773 /* force FDC ops in io_pdir_entry() to be visible to IOMMU */
776 if (ioc_needs_fdc) 774 asm_io_sync();
777 asm volatile("sync" : : );
778 775
779#ifdef ASSERT_PDIR_SANITY 776#ifdef ASSERT_PDIR_SANITY
780 sba_check_pdir(ioc,"Check after sba_map_single()"); 777 sba_check_pdir(ioc,"Check after sba_map_single()");
@@ -858,8 +855,7 @@ sba_unmap_page(struct device *dev, dma_addr_t iova, size_t size,
858 sba_free_range(ioc, iova, size); 855 sba_free_range(ioc, iova, size);
859 856
860 /* If fdc's were issued, force fdc's to be visible now */ 857 /* If fdc's were issued, force fdc's to be visible now */
861 if (ioc_needs_fdc) 858 asm_io_sync();
862 asm volatile("sync" : : );
863 859
864 READ_REG(ioc->ioc_hpa+IOC_PCOM); /* flush purges */ 860 READ_REG(ioc->ioc_hpa+IOC_PCOM); /* flush purges */
865#endif /* DELAYED_RESOURCE_CNT == 0 */ 861#endif /* DELAYED_RESOURCE_CNT == 0 */
@@ -1008,8 +1004,7 @@ sba_map_sg(struct device *dev, struct scatterlist *sglist, int nents,
1008 filled = iommu_fill_pdir(ioc, sglist, nents, 0, sba_io_pdir_entry); 1004 filled = iommu_fill_pdir(ioc, sglist, nents, 0, sba_io_pdir_entry);
1009 1005
1010 /* force FDC ops in io_pdir_entry() to be visible to IOMMU */ 1006 /* force FDC ops in io_pdir_entry() to be visible to IOMMU */
1011 if (ioc_needs_fdc) 1007 asm_io_sync();
1012 asm volatile("sync" : : );
1013 1008
1014#ifdef ASSERT_PDIR_SANITY 1009#ifdef ASSERT_PDIR_SANITY
1015 if (sba_check_pdir(ioc,"Check after sba_map_sg()")) 1010 if (sba_check_pdir(ioc,"Check after sba_map_sg()"))