RISC-V: Allow userspace to flush the instruction cache
Despite RISC-V having a direct 'fence.i' instruction available to userspace (which we can't trap!), that's not actually viable when running on Linux because the kernel might schedule a process on another hart. There is no way for userspace to handle this without invoking the kernel (as it doesn't know the thread->hart mappings), so we've defined a RISC-V specific system call to flush the instruction cache. This patch adds both a system call and a VDSO entry. If possible, we'd like to avoid having the system call be considered part of the user-facing ABI and instead restrict that to the VDSO entry -- both just in general to avoid having additional user-visible ABI to maintain, and because we'd prefer that users just call the VDSO entry because there might be a better way to do this in the future (ie, one that doesn't require entering the kernel). Signed-off-by: Andrew Waterman <andrew@sifive.com> Signed-off-by: Palmer Dabbelt <palmer@sifive.com>
This commit is contained in:
parent
08f051eda3
commit
921ebd8f2c
|
@ -52,4 +52,10 @@ void flush_icache_mm(struct mm_struct *mm, bool local);
|
|||
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
/*
|
||||
* Bits in sys_riscv_flush_icache()'s flags argument.
|
||||
*/
|
||||
#define SYS_RISCV_FLUSH_ICACHE_LOCAL 1UL
|
||||
#define SYS_RISCV_FLUSH_ICACHE_ALL (SYS_RISCV_FLUSH_ICACHE_LOCAL)
|
||||
|
||||
#endif /* _ASM_RISCV_CACHEFLUSH_H */
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (C) 2017 SiFive
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _ASM_RISCV_VDSO_SYSCALLS_H
|
||||
#define _ASM_RISCV_VDSO_SYSCALLS_H
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
|
||||
/* These syscalls are only used by the vDSO and are not in the uapi. */
|
||||
#define __NR_riscv_flush_icache (__NR_arch_specific_syscall + 15)
|
||||
__SYSCALL(__NR_riscv_flush_icache, sys_riscv_flush_icache)
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _ASM_RISCV_VDSO_H */
|
|
@ -38,4 +38,8 @@ struct vdso_data {
|
|||
(void __user *)((unsigned long)(base) + __vdso_##name); \
|
||||
})
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
asmlinkage long sys_riscv_flush_icache(uintptr_t, uintptr_t, uintptr_t);
|
||||
#endif
|
||||
|
||||
#endif /* _ASM_RISCV_VDSO_H */
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/syscalls.h>
|
||||
#include <asm/cmpxchg.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
static long riscv_sys_mmap(unsigned long addr, unsigned long len,
|
||||
unsigned long prot, unsigned long flags,
|
||||
|
@ -47,3 +48,34 @@ SYSCALL_DEFINE6(mmap2, unsigned long, addr, unsigned long, len,
|
|||
return riscv_sys_mmap(addr, len, prot, flags, fd, offset, 12);
|
||||
}
|
||||
#endif /* !CONFIG_64BIT */
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
/*
|
||||
* Allows the instruction cache to be flushed from userspace. Despite RISC-V
|
||||
* having a direct 'fence.i' instruction available to userspace (which we
|
||||
* can't trap!), that's not actually viable when running on Linux because the
|
||||
* kernel might schedule a process on another hart. There is no way for
|
||||
* userspace to handle this without invoking the kernel (as it doesn't know the
|
||||
* thread->hart mappings), so we've defined a RISC-V specific system call to
|
||||
* flush the instruction cache.
|
||||
*
|
||||
* sys_riscv_flush_icache() is defined to flush the instruction cache over an
|
||||
* address range, with the flush applying to either all threads or just the
|
||||
* caller. We don't currently do anything with the address range, that's just
|
||||
* in there for forwards compatibility.
|
||||
*/
|
||||
SYSCALL_DEFINE3(riscv_flush_icache, uintptr_t, start, uintptr_t, end,
|
||||
uintptr_t, flags)
|
||||
{
|
||||
struct mm_struct *mm = current->mm;
|
||||
bool local = (flags & SYS_RISCV_FLUSH_ICACHE_LOCAL) != 0;
|
||||
|
||||
/* Check the reserved flags. */
|
||||
if (unlikely(flags & !SYS_RISCV_FLUSH_ICACHE_ALL))
|
||||
return -EINVAL;
|
||||
|
||||
flush_icache_mm(mm, local);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/linkage.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <asm-generic/syscalls.h>
|
||||
#include <asm/vdso.h>
|
||||
|
||||
#undef __SYSCALL
|
||||
#define __SYSCALL(nr, call) [nr] = (call),
|
||||
|
@ -22,4 +23,5 @@
|
|||
void *sys_call_table[__NR_syscalls] = {
|
||||
[0 ... __NR_syscalls - 1] = sys_ni_syscall,
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/vdso-syscalls.h>
|
||||
};
|
||||
|
|
|
@ -6,6 +6,7 @@ vdso-syms += gettimeofday
|
|||
vdso-syms += clock_gettime
|
||||
vdso-syms += clock_getres
|
||||
vdso-syms += getcpu
|
||||
vdso-syms += flush_icache
|
||||
|
||||
# Files to link into the vdso
|
||||
obj-vdso = $(patsubst %, %.o, $(vdso-syms))
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (C) 2017 SiFive
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation, version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/vdso-syscalls.h>
|
||||
|
||||
.text
|
||||
/* int __vdso_flush_icache(void *start, void *end, unsigned long flags); */
|
||||
ENTRY(__vdso_flush_icache)
|
||||
.cfi_startproc
|
||||
#ifdef CONFIG_SMP
|
||||
li a7, __NR_riscv_flush_icache
|
||||
ecall
|
||||
#else
|
||||
fence.i
|
||||
li a0, 0
|
||||
#endif
|
||||
ret
|
||||
.cfi_endproc
|
||||
ENDPROC(__vdso_flush_icache)
|
|
@ -74,6 +74,7 @@ VERSION
|
|||
__vdso_clock_gettime;
|
||||
__vdso_clock_getres;
|
||||
__vdso_getcpu;
|
||||
__vdso_flush_icache;
|
||||
local: *;
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue