Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 921ebd8f authored by Andrew Waterman's avatar Andrew Waterman Committed by Palmer Dabbelt
Browse files

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: default avatarAndrew Waterman <andrew@sifive.com>
Signed-off-by: default avatarPalmer Dabbelt <palmer@sifive.com>
parent 08f051ed
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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 */
+28 −0
Original line number Diff line number Diff line
/*
 * 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 */
+4 −0
Original line number Diff line number Diff line
@@ -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 */
+32 −0
Original line number Diff line number Diff line
@@ -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
+2 −0
Original line number Diff line number Diff line
@@ -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>
};
Loading