diff options
author | Palmer Dabbelt <palmer@sifive.com> | 2017-12-01 16:12:10 -0500 |
---|---|---|
committer | Palmer Dabbelt <palmer@sifive.com> | 2017-12-01 16:12:10 -0500 |
commit | 07f8ba7439f9c942d5bd7b63074e7a1528601713 (patch) | |
tree | a9ea3bf949955e5808617e4b21372c0ed85bc009 | |
parent | f8182f613c9887744eb469b1a3352636481cb395 (diff) | |
parent | 0e710ac6521f13c68a5c634471f40ae448c31e0a (diff) |
RISC-V: User-Visible Changes
This merge contains the user-visible, ABI-breaking changes that we want
to make sure we have in Linux before our first release. Highlights
include:
* VDSO entries for clock_get/gettimeofday/getcpu have been added. These
are simple syscalls now, but we want to let glibc use them from the
start so we can make them faster later.
* A VDSO entry for instruction cache flushing has been added so
userspace can flush the instruction cache.
* The VDSO symbol versions for __vdso_cmpxchg{32,64} have been removed,
as those VDSO entries don't actually exist.
Conflicts:
arch/riscv/include/asm/tlbflush.h
-rw-r--r-- | arch/riscv/include/asm/cacheflush.h | 30 | ||||
-rw-r--r-- | arch/riscv/include/asm/mmu.h | 4 | ||||
-rw-r--r-- | arch/riscv/include/asm/mmu_context.h | 44 | ||||
-rw-r--r-- | arch/riscv/include/asm/pgtable.h | 58 | ||||
-rw-r--r-- | arch/riscv/include/asm/tlbflush.h | 2 | ||||
-rw-r--r-- | arch/riscv/include/asm/vdso-syscalls.h | 28 | ||||
-rw-r--r-- | arch/riscv/include/asm/vdso.h | 4 | ||||
-rw-r--r-- | arch/riscv/kernel/smp.c | 48 | ||||
-rw-r--r-- | arch/riscv/kernel/sys_riscv.c | 33 | ||||
-rw-r--r-- | arch/riscv/kernel/syscall_table.c | 2 | ||||
-rw-r--r-- | arch/riscv/kernel/vdso/Makefile | 7 | ||||
-rw-r--r-- | arch/riscv/kernel/vdso/clock_getres.S | 26 | ||||
-rw-r--r-- | arch/riscv/kernel/vdso/clock_gettime.S | 26 | ||||
-rw-r--r-- | arch/riscv/kernel/vdso/flush_icache.S | 31 | ||||
-rw-r--r-- | arch/riscv/kernel/vdso/getcpu.S | 26 | ||||
-rw-r--r-- | arch/riscv/kernel/vdso/gettimeofday.S | 26 | ||||
-rw-r--r-- | arch/riscv/kernel/vdso/vdso.lds.S | 7 | ||||
-rw-r--r-- | arch/riscv/mm/Makefile | 1 | ||||
-rw-r--r-- | arch/riscv/mm/cacheflush.c | 23 |
19 files changed, 392 insertions, 34 deletions
diff --git a/arch/riscv/include/asm/cacheflush.h b/arch/riscv/include/asm/cacheflush.h index 0595585013b0..efd89a88d2d0 100644 --- a/arch/riscv/include/asm/cacheflush.h +++ b/arch/riscv/include/asm/cacheflush.h | |||
@@ -18,22 +18,44 @@ | |||
18 | 18 | ||
19 | #undef flush_icache_range | 19 | #undef flush_icache_range |
20 | #undef flush_icache_user_range | 20 | #undef flush_icache_user_range |
21 | #undef flush_dcache_page | ||
21 | 22 | ||
22 | static inline void local_flush_icache_all(void) | 23 | static inline void local_flush_icache_all(void) |
23 | { | 24 | { |
24 | asm volatile ("fence.i" ::: "memory"); | 25 | asm volatile ("fence.i" ::: "memory"); |
25 | } | 26 | } |
26 | 27 | ||
28 | #define PG_dcache_clean PG_arch_1 | ||
29 | |||
30 | static inline void flush_dcache_page(struct page *page) | ||
31 | { | ||
32 | if (test_bit(PG_dcache_clean, &page->flags)) | ||
33 | clear_bit(PG_dcache_clean, &page->flags); | ||
34 | } | ||
35 | |||
36 | /* | ||
37 | * RISC-V doesn't have an instruction to flush parts of the instruction cache, | ||
38 | * so instead we just flush the whole thing. | ||
39 | */ | ||
40 | #define flush_icache_range(start, end) flush_icache_all() | ||
41 | #define flush_icache_user_range(vma, pg, addr, len) flush_icache_all() | ||
42 | |||
27 | #ifndef CONFIG_SMP | 43 | #ifndef CONFIG_SMP |
28 | 44 | ||
29 | #define flush_icache_range(start, end) local_flush_icache_all() | 45 | #define flush_icache_all() local_flush_icache_all() |
30 | #define flush_icache_user_range(vma, pg, addr, len) local_flush_icache_all() | 46 | #define flush_icache_mm(mm, local) flush_icache_all() |
31 | 47 | ||
32 | #else /* CONFIG_SMP */ | 48 | #else /* CONFIG_SMP */ |
33 | 49 | ||
34 | #define flush_icache_range(start, end) sbi_remote_fence_i(0) | 50 | #define flush_icache_all() sbi_remote_fence_i(0) |
35 | #define flush_icache_user_range(vma, pg, addr, len) sbi_remote_fence_i(0) | 51 | void flush_icache_mm(struct mm_struct *mm, bool local); |
36 | 52 | ||
37 | #endif /* CONFIG_SMP */ | 53 | #endif /* CONFIG_SMP */ |
38 | 54 | ||
55 | /* | ||
56 | * Bits in sys_riscv_flush_icache()'s flags argument. | ||
57 | */ | ||
58 | #define SYS_RISCV_FLUSH_ICACHE_LOCAL 1UL | ||
59 | #define SYS_RISCV_FLUSH_ICACHE_ALL (SYS_RISCV_FLUSH_ICACHE_LOCAL) | ||
60 | |||
39 | #endif /* _ASM_RISCV_CACHEFLUSH_H */ | 61 | #endif /* _ASM_RISCV_CACHEFLUSH_H */ |
diff --git a/arch/riscv/include/asm/mmu.h b/arch/riscv/include/asm/mmu.h index 66805cba9a27..5df2dccdba12 100644 --- a/arch/riscv/include/asm/mmu.h +++ b/arch/riscv/include/asm/mmu.h | |||
@@ -19,6 +19,10 @@ | |||
19 | 19 | ||
20 | typedef struct { | 20 | typedef struct { |
21 | void *vdso; | 21 | void *vdso; |
22 | #ifdef CONFIG_SMP | ||
23 | /* A local icache flush is needed before user execution can resume. */ | ||
24 | cpumask_t icache_stale_mask; | ||
25 | #endif | ||
22 | } mm_context_t; | 26 | } mm_context_t; |
23 | 27 | ||
24 | #endif /* __ASSEMBLY__ */ | 28 | #endif /* __ASSEMBLY__ */ |
diff --git a/arch/riscv/include/asm/mmu_context.h b/arch/riscv/include/asm/mmu_context.h index de1fc1631fc4..b15b169e3d22 100644 --- a/arch/riscv/include/asm/mmu_context.h +++ b/arch/riscv/include/asm/mmu_context.h | |||
@@ -1,5 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2012 Regents of the University of California | 2 | * Copyright (C) 2012 Regents of the University of California |
3 | * Copyright (C) 2017 SiFive | ||
3 | * | 4 | * |
4 | * This program is free software; you can redistribute it and/or | 5 | * This program is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU General Public License | 6 | * modify it under the terms of the GNU General Public License |
@@ -19,6 +20,7 @@ | |||
19 | #include <linux/mm.h> | 20 | #include <linux/mm.h> |
20 | #include <linux/sched.h> | 21 | #include <linux/sched.h> |
21 | #include <asm/tlbflush.h> | 22 | #include <asm/tlbflush.h> |
23 | #include <asm/cacheflush.h> | ||
22 | 24 | ||
23 | static inline void enter_lazy_tlb(struct mm_struct *mm, | 25 | static inline void enter_lazy_tlb(struct mm_struct *mm, |
24 | struct task_struct *task) | 26 | struct task_struct *task) |
@@ -46,12 +48,54 @@ static inline void set_pgdir(pgd_t *pgd) | |||
46 | csr_write(sptbr, virt_to_pfn(pgd) | SPTBR_MODE); | 48 | csr_write(sptbr, virt_to_pfn(pgd) | SPTBR_MODE); |
47 | } | 49 | } |
48 | 50 | ||
51 | /* | ||
52 | * When necessary, performs a deferred icache flush for the given MM context, | ||
53 | * on the local CPU. RISC-V has no direct mechanism for instruction cache | ||
54 | * shoot downs, so instead we send an IPI that informs the remote harts they | ||
55 | * need to flush their local instruction caches. To avoid pathologically slow | ||
56 | * behavior in a common case (a bunch of single-hart processes on a many-hart | ||
57 | * machine, ie 'make -j') we avoid the IPIs for harts that are not currently | ||
58 | * executing a MM context and instead schedule a deferred local instruction | ||
59 | * cache flush to be performed before execution resumes on each hart. This | ||
60 | * actually performs that local instruction cache flush, which implicitly only | ||
61 | * refers to the current hart. | ||
62 | */ | ||
63 | static inline void flush_icache_deferred(struct mm_struct *mm) | ||
64 | { | ||
65 | #ifdef CONFIG_SMP | ||
66 | unsigned int cpu = smp_processor_id(); | ||
67 | cpumask_t *mask = &mm->context.icache_stale_mask; | ||
68 | |||
69 | if (cpumask_test_cpu(cpu, mask)) { | ||
70 | cpumask_clear_cpu(cpu, mask); | ||
71 | /* | ||
72 | * Ensure the remote hart's writes are visible to this hart. | ||
73 | * This pairs with a barrier in flush_icache_mm. | ||
74 | */ | ||
75 | smp_mb(); | ||
76 | local_flush_icache_all(); | ||
77 | } | ||
78 | #endif | ||
79 | } | ||
80 | |||
49 | static inline void switch_mm(struct mm_struct *prev, | 81 | static inline void switch_mm(struct mm_struct *prev, |
50 | struct mm_struct *next, struct task_struct *task) | 82 | struct mm_struct *next, struct task_struct *task) |
51 | { | 83 | { |
52 | if (likely(prev != next)) { | 84 | if (likely(prev != next)) { |
85 | /* | ||
86 | * Mark the current MM context as inactive, and the next as | ||
87 | * active. This is at least used by the icache flushing | ||
88 | * routines in order to determine who should | ||
89 | */ | ||
90 | unsigned int cpu = smp_processor_id(); | ||
91 | |||
92 | cpumask_clear_cpu(cpu, mm_cpumask(prev)); | ||
93 | cpumask_set_cpu(cpu, mm_cpumask(next)); | ||
94 | |||
53 | set_pgdir(next->pgd); | 95 | set_pgdir(next->pgd); |
54 | local_flush_tlb_all(); | 96 | local_flush_tlb_all(); |
97 | |||
98 | flush_icache_deferred(next); | ||
55 | } | 99 | } |
56 | } | 100 | } |
57 | 101 | ||
diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h index 3399257780b2..2cbd92ed1629 100644 --- a/arch/riscv/include/asm/pgtable.h +++ b/arch/riscv/include/asm/pgtable.h | |||
@@ -178,28 +178,6 @@ static inline pte_t *pte_offset_kernel(pmd_t *pmd, unsigned long addr) | |||
178 | #define pte_offset_map(dir, addr) pte_offset_kernel((dir), (addr)) | 178 | #define pte_offset_map(dir, addr) pte_offset_kernel((dir), (addr)) |
179 | #define pte_unmap(pte) ((void)(pte)) | 179 | #define pte_unmap(pte) ((void)(pte)) |
180 | 180 | ||
181 | /* | ||
182 | * Certain architectures need to do special things when PTEs within | ||
183 | * a page table are directly modified. Thus, the following hook is | ||
184 | * made available. | ||
185 | */ | ||
186 | static inline void set_pte(pte_t *ptep, pte_t pteval) | ||
187 | { | ||
188 | *ptep = pteval; | ||
189 | } | ||
190 | |||
191 | static inline void set_pte_at(struct mm_struct *mm, | ||
192 | unsigned long addr, pte_t *ptep, pte_t pteval) | ||
193 | { | ||
194 | set_pte(ptep, pteval); | ||
195 | } | ||
196 | |||
197 | static inline void pte_clear(struct mm_struct *mm, | ||
198 | unsigned long addr, pte_t *ptep) | ||
199 | { | ||
200 | set_pte_at(mm, addr, ptep, __pte(0)); | ||
201 | } | ||
202 | |||
203 | static inline int pte_present(pte_t pte) | 181 | static inline int pte_present(pte_t pte) |
204 | { | 182 | { |
205 | return (pte_val(pte) & _PAGE_PRESENT); | 183 | return (pte_val(pte) & _PAGE_PRESENT); |
@@ -210,21 +188,22 @@ static inline int pte_none(pte_t pte) | |||
210 | return (pte_val(pte) == 0); | 188 | return (pte_val(pte) == 0); |
211 | } | 189 | } |
212 | 190 | ||
213 | /* static inline int pte_read(pte_t pte) */ | ||
214 | |||
215 | static inline int pte_write(pte_t pte) | 191 | static inline int pte_write(pte_t pte) |
216 | { | 192 | { |
217 | return pte_val(pte) & _PAGE_WRITE; | 193 | return pte_val(pte) & _PAGE_WRITE; |
218 | } | 194 | } |
219 | 195 | ||
196 | static inline int pte_exec(pte_t pte) | ||
197 | { | ||
198 | return pte_val(pte) & _PAGE_EXEC; | ||
199 | } | ||
200 | |||
220 | static inline int pte_huge(pte_t pte) | 201 | static inline int pte_huge(pte_t pte) |
221 | { | 202 | { |
222 | return pte_present(pte) | 203 | return pte_present(pte) |
223 | && (pte_val(pte) & (_PAGE_READ | _PAGE_WRITE | _PAGE_EXEC)); | 204 | && (pte_val(pte) & (_PAGE_READ | _PAGE_WRITE | _PAGE_EXEC)); |
224 | } | 205 | } |
225 | 206 | ||
226 | /* static inline int pte_exec(pte_t pte) */ | ||
227 | |||
228 | static inline int pte_dirty(pte_t pte) | 207 | static inline int pte_dirty(pte_t pte) |
229 | { | 208 | { |
230 | return pte_val(pte) & _PAGE_DIRTY; | 209 | return pte_val(pte) & _PAGE_DIRTY; |
@@ -311,6 +290,33 @@ static inline int pte_same(pte_t pte_a, pte_t pte_b) | |||
311 | return pte_val(pte_a) == pte_val(pte_b); | 290 | return pte_val(pte_a) == pte_val(pte_b); |
312 | } | 291 | } |
313 | 292 | ||
293 | /* | ||
294 | * Certain architectures need to do special things when PTEs within | ||
295 | * a page table are directly modified. Thus, the following hook is | ||
296 | * made available. | ||
297 | */ | ||
298 | static inline void set_pte(pte_t *ptep, pte_t pteval) | ||
299 | { | ||
300 | *ptep = pteval; | ||
301 | } | ||
302 | |||
303 | void flush_icache_pte(pte_t pte); | ||
304 | |||
305 | static inline void set_pte_at(struct mm_struct *mm, | ||
306 | unsigned long addr, pte_t *ptep, pte_t pteval) | ||
307 | { | ||
308 | if (pte_present(pteval) && pte_exec(pteval)) | ||
309 | flush_icache_pte(pteval); | ||
310 | |||
311 | set_pte(ptep, pteval); | ||
312 | } | ||
313 | |||
314 | static inline void pte_clear(struct mm_struct *mm, | ||
315 | unsigned long addr, pte_t *ptep) | ||
316 | { | ||
317 | set_pte_at(mm, addr, ptep, __pte(0)); | ||
318 | } | ||
319 | |||
314 | #define __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS | 320 | #define __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS |
315 | static inline int ptep_set_access_flags(struct vm_area_struct *vma, | 321 | static inline int ptep_set_access_flags(struct vm_area_struct *vma, |
316 | unsigned long address, pte_t *ptep, | 322 | unsigned long address, pte_t *ptep, |
diff --git a/arch/riscv/include/asm/tlbflush.h b/arch/riscv/include/asm/tlbflush.h index c79fab3d377d..715b0f10af58 100644 --- a/arch/riscv/include/asm/tlbflush.h +++ b/arch/riscv/include/asm/tlbflush.h | |||
@@ -17,6 +17,8 @@ | |||
17 | 17 | ||
18 | #ifdef CONFIG_MMU | 18 | #ifdef CONFIG_MMU |
19 | 19 | ||
20 | #include <linux/mm_types.h> | ||
21 | |||
20 | /* | 22 | /* |
21 | * Flush entire local TLB. 'sfence.vma' implicitly fences with the instruction | 23 | * Flush entire local TLB. 'sfence.vma' implicitly fences with the instruction |
22 | * cache as well, so a 'fence.i' is not necessary. | 24 | * cache as well, so a 'fence.i' is not necessary. |
diff --git a/arch/riscv/include/asm/vdso-syscalls.h b/arch/riscv/include/asm/vdso-syscalls.h new file mode 100644 index 000000000000..a2ccf1894929 --- /dev/null +++ b/arch/riscv/include/asm/vdso-syscalls.h | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2017 SiFive | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | */ | ||
16 | |||
17 | #ifndef _ASM_RISCV_VDSO_SYSCALLS_H | ||
18 | #define _ASM_RISCV_VDSO_SYSCALLS_H | ||
19 | |||
20 | #ifdef CONFIG_SMP | ||
21 | |||
22 | /* These syscalls are only used by the vDSO and are not in the uapi. */ | ||
23 | #define __NR_riscv_flush_icache (__NR_arch_specific_syscall + 15) | ||
24 | __SYSCALL(__NR_riscv_flush_icache, sys_riscv_flush_icache) | ||
25 | |||
26 | #endif | ||
27 | |||
28 | #endif /* _ASM_RISCV_VDSO_H */ | ||
diff --git a/arch/riscv/include/asm/vdso.h b/arch/riscv/include/asm/vdso.h index 602f61257553..541544d64c33 100644 --- a/arch/riscv/include/asm/vdso.h +++ b/arch/riscv/include/asm/vdso.h | |||
@@ -38,4 +38,8 @@ struct vdso_data { | |||
38 | (void __user *)((unsigned long)(base) + __vdso_##name); \ | 38 | (void __user *)((unsigned long)(base) + __vdso_##name); \ |
39 | }) | 39 | }) |
40 | 40 | ||
41 | #ifdef CONFIG_SMP | ||
42 | asmlinkage long sys_riscv_flush_icache(uintptr_t, uintptr_t, uintptr_t); | ||
43 | #endif | ||
44 | |||
41 | #endif /* _ASM_RISCV_VDSO_H */ | 45 | #endif /* _ASM_RISCV_VDSO_H */ |
diff --git a/arch/riscv/kernel/smp.c b/arch/riscv/kernel/smp.c index b4a71ec5906f..1b27ade437b4 100644 --- a/arch/riscv/kernel/smp.c +++ b/arch/riscv/kernel/smp.c | |||
@@ -108,3 +108,51 @@ void smp_send_reschedule(int cpu) | |||
108 | { | 108 | { |
109 | send_ipi_message(cpumask_of(cpu), IPI_RESCHEDULE); | 109 | send_ipi_message(cpumask_of(cpu), IPI_RESCHEDULE); |
110 | } | 110 | } |
111 | |||
112 | /* | ||
113 | * Performs an icache flush for the given MM context. RISC-V has no direct | ||
114 | * mechanism for instruction cache shoot downs, so instead we send an IPI that | ||
115 | * informs the remote harts they need to flush their local instruction caches. | ||
116 | * To avoid pathologically slow behavior in a common case (a bunch of | ||
117 | * single-hart processes on a many-hart machine, ie 'make -j') we avoid the | ||
118 | * IPIs for harts that are not currently executing a MM context and instead | ||
119 | * schedule a deferred local instruction cache flush to be performed before | ||
120 | * execution resumes on each hart. | ||
121 | */ | ||
122 | void flush_icache_mm(struct mm_struct *mm, bool local) | ||
123 | { | ||
124 | unsigned int cpu; | ||
125 | cpumask_t others, *mask; | ||
126 | |||
127 | preempt_disable(); | ||
128 | |||
129 | /* Mark every hart's icache as needing a flush for this MM. */ | ||
130 | mask = &mm->context.icache_stale_mask; | ||
131 | cpumask_setall(mask); | ||
132 | /* Flush this hart's I$ now, and mark it as flushed. */ | ||
133 | cpu = smp_processor_id(); | ||
134 | cpumask_clear_cpu(cpu, mask); | ||
135 | local_flush_icache_all(); | ||
136 | |||
137 | /* | ||
138 | * Flush the I$ of other harts concurrently executing, and mark them as | ||
139 | * flushed. | ||
140 | */ | ||
141 | cpumask_andnot(&others, mm_cpumask(mm), cpumask_of(cpu)); | ||
142 | local |= cpumask_empty(&others); | ||
143 | if (mm != current->active_mm || !local) | ||
144 | sbi_remote_fence_i(others.bits); | ||
145 | else { | ||
146 | /* | ||
147 | * It's assumed that at least one strongly ordered operation is | ||
148 | * performed on this hart between setting a hart's cpumask bit | ||
149 | * and scheduling this MM context on that hart. Sending an SBI | ||
150 | * remote message will do this, but in the case where no | ||
151 | * messages are sent we still need to order this hart's writes | ||
152 | * with flush_icache_deferred(). | ||
153 | */ | ||
154 | smp_mb(); | ||
155 | } | ||
156 | |||
157 | preempt_enable(); | ||
158 | } | ||
diff --git a/arch/riscv/kernel/sys_riscv.c b/arch/riscv/kernel/sys_riscv.c index 4351be7d0533..a2ae936a093e 100644 --- a/arch/riscv/kernel/sys_riscv.c +++ b/arch/riscv/kernel/sys_riscv.c | |||
@@ -14,8 +14,8 @@ | |||
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include <linux/syscalls.h> | 16 | #include <linux/syscalls.h> |
17 | #include <asm/cmpxchg.h> | ||
18 | #include <asm/unistd.h> | 17 | #include <asm/unistd.h> |
18 | #include <asm/cacheflush.h> | ||
19 | 19 | ||
20 | static long riscv_sys_mmap(unsigned long addr, unsigned long len, | 20 | static long riscv_sys_mmap(unsigned long addr, unsigned long len, |
21 | unsigned long prot, unsigned long flags, | 21 | unsigned long prot, unsigned long flags, |
@@ -47,3 +47,34 @@ SYSCALL_DEFINE6(mmap2, unsigned long, addr, unsigned long, len, | |||
47 | return riscv_sys_mmap(addr, len, prot, flags, fd, offset, 12); | 47 | return riscv_sys_mmap(addr, len, prot, flags, fd, offset, 12); |
48 | } | 48 | } |
49 | #endif /* !CONFIG_64BIT */ | 49 | #endif /* !CONFIG_64BIT */ |
50 | |||
51 | #ifdef CONFIG_SMP | ||
52 | /* | ||
53 | * Allows the instruction cache to be flushed from userspace. Despite RISC-V | ||
54 | * having a direct 'fence.i' instruction available to userspace (which we | ||
55 | * can't trap!), that's not actually viable when running on Linux because the | ||
56 | * kernel might schedule a process on another hart. There is no way for | ||
57 | * userspace to handle this without invoking the kernel (as it doesn't know the | ||
58 | * thread->hart mappings), so we've defined a RISC-V specific system call to | ||
59 | * flush the instruction cache. | ||
60 | * | ||
61 | * sys_riscv_flush_icache() is defined to flush the instruction cache over an | ||
62 | * address range, with the flush applying to either all threads or just the | ||
63 | * caller. We don't currently do anything with the address range, that's just | ||
64 | * in there for forwards compatibility. | ||
65 | */ | ||
66 | SYSCALL_DEFINE3(riscv_flush_icache, uintptr_t, start, uintptr_t, end, | ||
67 | uintptr_t, flags) | ||
68 | { | ||
69 | struct mm_struct *mm = current->mm; | ||
70 | bool local = (flags & SYS_RISCV_FLUSH_ICACHE_LOCAL) != 0; | ||
71 | |||
72 | /* Check the reserved flags. */ | ||
73 | if (unlikely(flags & !SYS_RISCV_FLUSH_ICACHE_ALL)) | ||
74 | return -EINVAL; | ||
75 | |||
76 | flush_icache_mm(mm, local); | ||
77 | |||
78 | return 0; | ||
79 | } | ||
80 | #endif | ||
diff --git a/arch/riscv/kernel/syscall_table.c b/arch/riscv/kernel/syscall_table.c index 4e30dc5fb593..a5bd6401f95e 100644 --- a/arch/riscv/kernel/syscall_table.c +++ b/arch/riscv/kernel/syscall_table.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/linkage.h> | 15 | #include <linux/linkage.h> |
16 | #include <linux/syscalls.h> | 16 | #include <linux/syscalls.h> |
17 | #include <asm-generic/syscalls.h> | 17 | #include <asm-generic/syscalls.h> |
18 | #include <asm/vdso.h> | ||
18 | 19 | ||
19 | #undef __SYSCALL | 20 | #undef __SYSCALL |
20 | #define __SYSCALL(nr, call) [nr] = (call), | 21 | #define __SYSCALL(nr, call) [nr] = (call), |
@@ -22,4 +23,5 @@ | |||
22 | void *sys_call_table[__NR_syscalls] = { | 23 | void *sys_call_table[__NR_syscalls] = { |
23 | [0 ... __NR_syscalls - 1] = sys_ni_syscall, | 24 | [0 ... __NR_syscalls - 1] = sys_ni_syscall, |
24 | #include <asm/unistd.h> | 25 | #include <asm/unistd.h> |
26 | #include <asm/vdso-syscalls.h> | ||
25 | }; | 27 | }; |
diff --git a/arch/riscv/kernel/vdso/Makefile b/arch/riscv/kernel/vdso/Makefile index 523d0a8ac8db..324568d33921 100644 --- a/arch/riscv/kernel/vdso/Makefile +++ b/arch/riscv/kernel/vdso/Makefile | |||
@@ -1,7 +1,12 @@ | |||
1 | # Copied from arch/tile/kernel/vdso/Makefile | 1 | # Copied from arch/tile/kernel/vdso/Makefile |
2 | 2 | ||
3 | # Symbols present in the vdso | 3 | # Symbols present in the vdso |
4 | vdso-syms = rt_sigreturn | 4 | vdso-syms = rt_sigreturn |
5 | vdso-syms += gettimeofday | ||
6 | vdso-syms += clock_gettime | ||
7 | vdso-syms += clock_getres | ||
8 | vdso-syms += getcpu | ||
9 | vdso-syms += flush_icache | ||
5 | 10 | ||
6 | # Files to link into the vdso | 11 | # Files to link into the vdso |
7 | obj-vdso = $(patsubst %, %.o, $(vdso-syms)) | 12 | obj-vdso = $(patsubst %, %.o, $(vdso-syms)) |
diff --git a/arch/riscv/kernel/vdso/clock_getres.S b/arch/riscv/kernel/vdso/clock_getres.S new file mode 100644 index 000000000000..edf7e2339648 --- /dev/null +++ b/arch/riscv/kernel/vdso/clock_getres.S | |||
@@ -0,0 +1,26 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2017 SiFive | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation, version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #include <linux/linkage.h> | ||
15 | #include <asm/unistd.h> | ||
16 | |||
17 | .text | ||
18 | /* int __vdso_clock_getres(clockid_t clock_id, struct timespec *res); */ | ||
19 | ENTRY(__vdso_clock_getres) | ||
20 | .cfi_startproc | ||
21 | /* For now, just do the syscall. */ | ||
22 | li a7, __NR_clock_getres | ||
23 | ecall | ||
24 | ret | ||
25 | .cfi_endproc | ||
26 | ENDPROC(__vdso_clock_getres) | ||
diff --git a/arch/riscv/kernel/vdso/clock_gettime.S b/arch/riscv/kernel/vdso/clock_gettime.S new file mode 100644 index 000000000000..aac65676c6d5 --- /dev/null +++ b/arch/riscv/kernel/vdso/clock_gettime.S | |||
@@ -0,0 +1,26 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2017 SiFive | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation, version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #include <linux/linkage.h> | ||
15 | #include <asm/unistd.h> | ||
16 | |||
17 | .text | ||
18 | /* int __vdso_clock_gettime(clockid_t clock_id, struct timespec *tp); */ | ||
19 | ENTRY(__vdso_clock_gettime) | ||
20 | .cfi_startproc | ||
21 | /* For now, just do the syscall. */ | ||
22 | li a7, __NR_clock_gettime | ||
23 | ecall | ||
24 | ret | ||
25 | .cfi_endproc | ||
26 | ENDPROC(__vdso_clock_gettime) | ||
diff --git a/arch/riscv/kernel/vdso/flush_icache.S b/arch/riscv/kernel/vdso/flush_icache.S new file mode 100644 index 000000000000..b0fbad74e873 --- /dev/null +++ b/arch/riscv/kernel/vdso/flush_icache.S | |||
@@ -0,0 +1,31 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2017 SiFive | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation, version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #include <linux/linkage.h> | ||
15 | #include <asm/unistd.h> | ||
16 | #include <asm/vdso-syscalls.h> | ||
17 | |||
18 | .text | ||
19 | /* int __vdso_flush_icache(void *start, void *end, unsigned long flags); */ | ||
20 | ENTRY(__vdso_flush_icache) | ||
21 | .cfi_startproc | ||
22 | #ifdef CONFIG_SMP | ||
23 | li a7, __NR_riscv_flush_icache | ||
24 | ecall | ||
25 | #else | ||
26 | fence.i | ||
27 | li a0, 0 | ||
28 | #endif | ||
29 | ret | ||
30 | .cfi_endproc | ||
31 | ENDPROC(__vdso_flush_icache) | ||
diff --git a/arch/riscv/kernel/vdso/getcpu.S b/arch/riscv/kernel/vdso/getcpu.S new file mode 100644 index 000000000000..cc7e98924484 --- /dev/null +++ b/arch/riscv/kernel/vdso/getcpu.S | |||
@@ -0,0 +1,26 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2017 SiFive | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation, version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #include <linux/linkage.h> | ||
15 | #include <asm/unistd.h> | ||
16 | |||
17 | .text | ||
18 | /* int __vdso_getcpu(unsigned *cpu, unsigned *node, void *unused); */ | ||
19 | ENTRY(__vdso_getcpu) | ||
20 | .cfi_startproc | ||
21 | /* For now, just do the syscall. */ | ||
22 | li a7, __NR_getcpu | ||
23 | ecall | ||
24 | ret | ||
25 | .cfi_endproc | ||
26 | ENDPROC(__vdso_getcpu) | ||
diff --git a/arch/riscv/kernel/vdso/gettimeofday.S b/arch/riscv/kernel/vdso/gettimeofday.S new file mode 100644 index 000000000000..da85d33e8990 --- /dev/null +++ b/arch/riscv/kernel/vdso/gettimeofday.S | |||
@@ -0,0 +1,26 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2017 SiFive | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation, version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #include <linux/linkage.h> | ||
15 | #include <asm/unistd.h> | ||
16 | |||
17 | .text | ||
18 | /* int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz); */ | ||
19 | ENTRY(__vdso_gettimeofday) | ||
20 | .cfi_startproc | ||
21 | /* For now, just do the syscall. */ | ||
22 | li a7, __NR_gettimeofday | ||
23 | ecall | ||
24 | ret | ||
25 | .cfi_endproc | ||
26 | ENDPROC(__vdso_gettimeofday) | ||
diff --git a/arch/riscv/kernel/vdso/vdso.lds.S b/arch/riscv/kernel/vdso/vdso.lds.S index 8c9dce95c11d..cd1d47e0724b 100644 --- a/arch/riscv/kernel/vdso/vdso.lds.S +++ b/arch/riscv/kernel/vdso/vdso.lds.S | |||
@@ -70,8 +70,11 @@ VERSION | |||
70 | LINUX_4.15 { | 70 | LINUX_4.15 { |
71 | global: | 71 | global: |
72 | __vdso_rt_sigreturn; | 72 | __vdso_rt_sigreturn; |
73 | __vdso_cmpxchg32; | 73 | __vdso_gettimeofday; |
74 | __vdso_cmpxchg64; | 74 | __vdso_clock_gettime; |
75 | __vdso_clock_getres; | ||
76 | __vdso_getcpu; | ||
77 | __vdso_flush_icache; | ||
75 | local: *; | 78 | local: *; |
76 | }; | 79 | }; |
77 | } | 80 | } |
diff --git a/arch/riscv/mm/Makefile b/arch/riscv/mm/Makefile index 81f7d9ce6d88..eb22ab49b3e0 100644 --- a/arch/riscv/mm/Makefile +++ b/arch/riscv/mm/Makefile | |||
@@ -2,3 +2,4 @@ obj-y += init.o | |||
2 | obj-y += fault.o | 2 | obj-y += fault.o |
3 | obj-y += extable.o | 3 | obj-y += extable.o |
4 | obj-y += ioremap.o | 4 | obj-y += ioremap.o |
5 | obj-y += cacheflush.o | ||
diff --git a/arch/riscv/mm/cacheflush.c b/arch/riscv/mm/cacheflush.c new file mode 100644 index 000000000000..498c0a0814fe --- /dev/null +++ b/arch/riscv/mm/cacheflush.c | |||
@@ -0,0 +1,23 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2017 SiFive | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation, version 2. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #include <asm/pgtable.h> | ||
15 | #include <asm/cacheflush.h> | ||
16 | |||
17 | void flush_icache_pte(pte_t pte) | ||
18 | { | ||
19 | struct page *page = pte_page(pte); | ||
20 | |||
21 | if (!test_and_set_bit(PG_dcache_clean, &page->flags)) | ||
22 | flush_icache_all(); | ||
23 | } | ||