aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWill Deacon <will.deacon@arm.com>2013-02-28 11:48:11 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2013-03-03 17:54:15 -0500
commit862c588f062fe9339a180cf6429e4df1855c376a (patch)
tree20348a057421b00913563d103c1cd430d1a7bb6f
parent8a4e3a9ead7e37ce1505602b564c15da09ac039f (diff)
ARM: 7660/1: tlb: add branch predictor maintenance operations
The ARM architecture requires explicit branch predictor maintenance when updating an instruction stream for a given virtual address. In reality, this isn't so much of a burden because the branch predictor is flushed during the cache maintenance required to make the new instructions visible to the I-side of the processor. However, there are still some cases where explicit flushing is required, so add a local_bp_flush_all operation to deal with this. Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Will Deacon <will.deacon@arm.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r--arch/arm/include/asm/tlbflush.h34
-rw-r--r--arch/arm/kernel/smp_tlb.c12
2 files changed, 40 insertions, 6 deletions
diff --git a/arch/arm/include/asm/tlbflush.h b/arch/arm/include/asm/tlbflush.h
index 6e924d3a77eb..4db8c8820f0d 100644
--- a/arch/arm/include/asm/tlbflush.h
+++ b/arch/arm/include/asm/tlbflush.h
@@ -34,10 +34,13 @@
34#define TLB_V6_D_ASID (1 << 17) 34#define TLB_V6_D_ASID (1 << 17)
35#define TLB_V6_I_ASID (1 << 18) 35#define TLB_V6_I_ASID (1 << 18)
36 36
37#define TLB_V6_BP (1 << 19)
38
37/* Unified Inner Shareable TLB operations (ARMv7 MP extensions) */ 39/* Unified Inner Shareable TLB operations (ARMv7 MP extensions) */
38#define TLB_V7_UIS_PAGE (1 << 19) 40#define TLB_V7_UIS_PAGE (1 << 20)
39#define TLB_V7_UIS_FULL (1 << 20) 41#define TLB_V7_UIS_FULL (1 << 21)
40#define TLB_V7_UIS_ASID (1 << 21) 42#define TLB_V7_UIS_ASID (1 << 22)
43#define TLB_V7_UIS_BP (1 << 23)
41 44
42#define TLB_BARRIER (1 << 28) 45#define TLB_BARRIER (1 << 28)
43#define TLB_L2CLEAN_FR (1 << 29) /* Feroceon */ 46#define TLB_L2CLEAN_FR (1 << 29) /* Feroceon */
@@ -150,7 +153,8 @@
150#define v6wbi_tlb_flags (TLB_WB | TLB_DCLEAN | TLB_BARRIER | \ 153#define v6wbi_tlb_flags (TLB_WB | TLB_DCLEAN | TLB_BARRIER | \
151 TLB_V6_I_FULL | TLB_V6_D_FULL | \ 154 TLB_V6_I_FULL | TLB_V6_D_FULL | \
152 TLB_V6_I_PAGE | TLB_V6_D_PAGE | \ 155 TLB_V6_I_PAGE | TLB_V6_D_PAGE | \
153 TLB_V6_I_ASID | TLB_V6_D_ASID) 156 TLB_V6_I_ASID | TLB_V6_D_ASID | \
157 TLB_V6_BP)
154 158
155#ifdef CONFIG_CPU_TLB_V6 159#ifdef CONFIG_CPU_TLB_V6
156# define v6wbi_possible_flags v6wbi_tlb_flags 160# define v6wbi_possible_flags v6wbi_tlb_flags
@@ -166,9 +170,11 @@
166#endif 170#endif
167 171
168#define v7wbi_tlb_flags_smp (TLB_WB | TLB_DCLEAN | TLB_BARRIER | \ 172#define v7wbi_tlb_flags_smp (TLB_WB | TLB_DCLEAN | TLB_BARRIER | \
169 TLB_V7_UIS_FULL | TLB_V7_UIS_PAGE | TLB_V7_UIS_ASID) 173 TLB_V7_UIS_FULL | TLB_V7_UIS_PAGE | \
174 TLB_V7_UIS_ASID | TLB_V7_UIS_BP)
170#define v7wbi_tlb_flags_up (TLB_WB | TLB_DCLEAN | TLB_BARRIER | \ 175#define v7wbi_tlb_flags_up (TLB_WB | TLB_DCLEAN | TLB_BARRIER | \
171 TLB_V6_U_FULL | TLB_V6_U_PAGE | TLB_V6_U_ASID) 176 TLB_V6_U_FULL | TLB_V6_U_PAGE | \
177 TLB_V6_U_ASID | TLB_V6_BP)
172 178
173#ifdef CONFIG_CPU_TLB_V7 179#ifdef CONFIG_CPU_TLB_V7
174 180
@@ -430,6 +436,20 @@ static inline void local_flush_tlb_kernel_page(unsigned long kaddr)
430 } 436 }
431} 437}
432 438
439static inline void local_flush_bp_all(void)
440{
441 const int zero = 0;
442 const unsigned int __tlb_flag = __cpu_tlb_flags;
443
444 if (tlb_flag(TLB_V7_UIS_BP))
445 asm("mcr p15, 0, %0, c7, c1, 6" : : "r" (zero));
446 else if (tlb_flag(TLB_V6_BP))
447 asm("mcr p15, 0, %0, c7, c5, 6" : : "r" (zero));
448
449 if (tlb_flag(TLB_BARRIER))
450 isb();
451}
452
433/* 453/*
434 * flush_pmd_entry 454 * flush_pmd_entry
435 * 455 *
@@ -480,6 +500,7 @@ static inline void clean_pmd_entry(void *pmd)
480#define flush_tlb_kernel_page local_flush_tlb_kernel_page 500#define flush_tlb_kernel_page local_flush_tlb_kernel_page
481#define flush_tlb_range local_flush_tlb_range 501#define flush_tlb_range local_flush_tlb_range
482#define flush_tlb_kernel_range local_flush_tlb_kernel_range 502#define flush_tlb_kernel_range local_flush_tlb_kernel_range
503#define flush_bp_all local_flush_bp_all
483#else 504#else
484extern void flush_tlb_all(void); 505extern void flush_tlb_all(void);
485extern void flush_tlb_mm(struct mm_struct *mm); 506extern void flush_tlb_mm(struct mm_struct *mm);
@@ -487,6 +508,7 @@ extern void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr);
487extern void flush_tlb_kernel_page(unsigned long kaddr); 508extern void flush_tlb_kernel_page(unsigned long kaddr);
488extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end); 509extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end);
489extern void flush_tlb_kernel_range(unsigned long start, unsigned long end); 510extern void flush_tlb_kernel_range(unsigned long start, unsigned long end);
511extern void flush_bp_all(void);
490#endif 512#endif
491 513
492/* 514/*
diff --git a/arch/arm/kernel/smp_tlb.c b/arch/arm/kernel/smp_tlb.c
index 02c5d2ce23bf..bd0300531399 100644
--- a/arch/arm/kernel/smp_tlb.c
+++ b/arch/arm/kernel/smp_tlb.c
@@ -64,6 +64,11 @@ static inline void ipi_flush_tlb_kernel_range(void *arg)
64 local_flush_tlb_kernel_range(ta->ta_start, ta->ta_end); 64 local_flush_tlb_kernel_range(ta->ta_start, ta->ta_end);
65} 65}
66 66
67static inline void ipi_flush_bp_all(void *ignored)
68{
69 local_flush_bp_all();
70}
71
67void flush_tlb_all(void) 72void flush_tlb_all(void)
68{ 73{
69 if (tlb_ops_need_broadcast()) 74 if (tlb_ops_need_broadcast())
@@ -127,3 +132,10 @@ void flush_tlb_kernel_range(unsigned long start, unsigned long end)
127 local_flush_tlb_kernel_range(start, end); 132 local_flush_tlb_kernel_range(start, end);
128} 133}
129 134
135void flush_bp_all(void)
136{
137 if (tlb_ops_need_broadcast())
138 on_each_cpu(ipi_flush_bp_all, NULL, 1);
139 else
140 local_flush_bp_all();
141}