RISC-V: Add a syscall for HW probing
We don't have enough space for these all in ELF_HWCAP{,2} and there's no system call that quite does this, so let's just provide an arch-specific one to probe for hardware capabilities. This currently just provides m{arch,imp,vendor}id, but with the key-value pairs we can pass more in the future. Co-developed-by: Palmer Dabbelt <palmer@rivosinc.com> Signed-off-by: Evan Green <evan@rivosinc.com> Reviewed-by: Conor Dooley <conor.dooley@microchip.com> Reviewed-by: Heiko Stuebner <heiko.stuebner@vrull.eu> Tested-by: Heiko Stuebner <heiko.stuebner@vrull.eu> Reviewed-by: Paul Walmsley <paul.walmsley@sifive.com> Link: https://lore.kernel.org/r/20230407231103.2622178-3-evan@rivosinc.com Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
This commit is contained in:
parent
ff77cf5b2e
commit
ea3de9ce8a
41
Documentation/riscv/hwprobe.rst
Normal file
41
Documentation/riscv/hwprobe.rst
Normal file
@ -0,0 +1,41 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
RISC-V Hardware Probing Interface
|
||||
---------------------------------
|
||||
|
||||
The RISC-V hardware probing interface is based around a single syscall, which
|
||||
is defined in <asm/hwprobe.h>::
|
||||
|
||||
struct riscv_hwprobe {
|
||||
__s64 key;
|
||||
__u64 value;
|
||||
};
|
||||
|
||||
long sys_riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
|
||||
size_t cpu_count, cpu_set_t *cpus,
|
||||
unsigned int flags);
|
||||
|
||||
The arguments are split into three groups: an array of key-value pairs, a CPU
|
||||
set, and some flags. The key-value pairs are supplied with a count. Userspace
|
||||
must prepopulate the key field for each element, and the kernel will fill in the
|
||||
value if the key is recognized. If a key is unknown to the kernel, its key field
|
||||
will be cleared to -1, and its value set to 0. The CPU set is defined by
|
||||
CPU_SET(3). For value-like keys (eg. vendor/arch/impl), the returned value will
|
||||
be only be valid if all CPUs in the given set have the same value. Otherwise -1
|
||||
will be returned. For boolean-like keys, the value returned will be a logical
|
||||
AND of the values for the specified CPUs. Usermode can supply NULL for cpus and
|
||||
0 for cpu_count as a shortcut for all online CPUs. There are currently no flags,
|
||||
this value must be zero for future compatibility.
|
||||
|
||||
On success 0 is returned, on failure a negative error code is returned.
|
||||
|
||||
The following keys are defined:
|
||||
|
||||
* :c:macro:`RISCV_HWPROBE_KEY_MVENDORID`: Contains the value of ``mvendorid``,
|
||||
as defined by the RISC-V privileged architecture specification.
|
||||
|
||||
* :c:macro:`RISCV_HWPROBE_KEY_MARCHID`: Contains the value of ``marchid``, as
|
||||
defined by the RISC-V privileged architecture specification.
|
||||
|
||||
* :c:macro:`RISCV_HWPROBE_KEY_MIMPLID`: Contains the value of ``mimplid``, as
|
||||
defined by the RISC-V privileged architecture specification.
|
@ -7,6 +7,7 @@ RISC-V architecture
|
||||
|
||||
boot-image-header
|
||||
vm-layout
|
||||
hwprobe
|
||||
patch-acceptance
|
||||
uabi
|
||||
|
||||
|
13
arch/riscv/include/asm/hwprobe.h
Normal file
13
arch/riscv/include/asm/hwprobe.h
Normal file
@ -0,0 +1,13 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/*
|
||||
* Copyright 2023 Rivos, Inc
|
||||
*/
|
||||
|
||||
#ifndef _ASM_HWPROBE_H
|
||||
#define _ASM_HWPROBE_H
|
||||
|
||||
#include <uapi/asm/hwprobe.h>
|
||||
|
||||
#define RISCV_HWPROBE_MAX_KEY 2
|
||||
|
||||
#endif
|
@ -10,6 +10,7 @@
|
||||
#ifndef _ASM_RISCV_SYSCALL_H
|
||||
#define _ASM_RISCV_SYSCALL_H
|
||||
|
||||
#include <asm/hwprobe.h>
|
||||
#include <uapi/linux/audit.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/err.h>
|
||||
@ -75,4 +76,7 @@ static inline int syscall_get_arch(struct task_struct *task)
|
||||
}
|
||||
|
||||
asmlinkage long sys_riscv_flush_icache(uintptr_t, uintptr_t, uintptr_t);
|
||||
|
||||
asmlinkage long sys_riscv_hwprobe(struct riscv_hwprobe *, size_t, size_t,
|
||||
unsigned long *, unsigned int);
|
||||
#endif /* _ASM_RISCV_SYSCALL_H */
|
||||
|
25
arch/riscv/include/uapi/asm/hwprobe.h
Normal file
25
arch/riscv/include/uapi/asm/hwprobe.h
Normal file
@ -0,0 +1,25 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/*
|
||||
* Copyright 2023 Rivos, Inc
|
||||
*/
|
||||
|
||||
#ifndef _UAPI_ASM_HWPROBE_H
|
||||
#define _UAPI_ASM_HWPROBE_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* Interface for probing hardware capabilities from userspace, see
|
||||
* Documentation/riscv/hwprobe.rst for more information.
|
||||
*/
|
||||
struct riscv_hwprobe {
|
||||
__s64 key;
|
||||
__u64 value;
|
||||
};
|
||||
|
||||
#define RISCV_HWPROBE_KEY_MVENDORID 0
|
||||
#define RISCV_HWPROBE_KEY_MARCHID 1
|
||||
#define RISCV_HWPROBE_KEY_MIMPID 2
|
||||
/* Increase RISCV_HWPROBE_MAX_KEY when adding items. */
|
||||
|
||||
#endif
|
@ -43,3 +43,12 @@
|
||||
#define __NR_riscv_flush_icache (__NR_arch_specific_syscall + 15)
|
||||
#endif
|
||||
__SYSCALL(__NR_riscv_flush_icache, sys_riscv_flush_icache)
|
||||
|
||||
/*
|
||||
* Allows userspace to query the kernel for CPU architecture and
|
||||
* microarchitecture details across a given set of CPUs.
|
||||
*/
|
||||
#ifndef __NR_riscv_hwprobe
|
||||
#define __NR_riscv_hwprobe (__NR_arch_specific_syscall + 14)
|
||||
#endif
|
||||
__SYSCALL(__NR_riscv_hwprobe, sys_riscv_hwprobe)
|
||||
|
@ -6,8 +6,11 @@
|
||||
*/
|
||||
|
||||
#include <linux/syscalls.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/hwprobe.h>
|
||||
#include <asm/sbi.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <asm-generic/mman-common.h>
|
||||
|
||||
static long riscv_sys_mmap(unsigned long addr, unsigned long len,
|
||||
@ -69,3 +72,133 @@ SYSCALL_DEFINE3(riscv_flush_icache, uintptr_t, start, uintptr_t, end,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The hwprobe interface, for allowing userspace to probe to see which features
|
||||
* are supported by the hardware. See Documentation/riscv/hwprobe.rst for more
|
||||
* details.
|
||||
*/
|
||||
static void hwprobe_arch_id(struct riscv_hwprobe *pair,
|
||||
const struct cpumask *cpus)
|
||||
{
|
||||
u64 id = -1ULL;
|
||||
bool first = true;
|
||||
int cpu;
|
||||
|
||||
for_each_cpu(cpu, cpus) {
|
||||
u64 cpu_id;
|
||||
|
||||
switch (pair->key) {
|
||||
case RISCV_HWPROBE_KEY_MVENDORID:
|
||||
cpu_id = riscv_cached_mvendorid(cpu);
|
||||
break;
|
||||
case RISCV_HWPROBE_KEY_MIMPID:
|
||||
cpu_id = riscv_cached_mimpid(cpu);
|
||||
break;
|
||||
case RISCV_HWPROBE_KEY_MARCHID:
|
||||
cpu_id = riscv_cached_marchid(cpu);
|
||||
break;
|
||||
}
|
||||
|
||||
if (first)
|
||||
id = cpu_id;
|
||||
|
||||
/*
|
||||
* If there's a mismatch for the given set, return -1 in the
|
||||
* value.
|
||||
*/
|
||||
if (id != cpu_id) {
|
||||
id = -1ULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pair->value = id;
|
||||
}
|
||||
|
||||
static void hwprobe_one_pair(struct riscv_hwprobe *pair,
|
||||
const struct cpumask *cpus)
|
||||
{
|
||||
switch (pair->key) {
|
||||
case RISCV_HWPROBE_KEY_MVENDORID:
|
||||
case RISCV_HWPROBE_KEY_MARCHID:
|
||||
case RISCV_HWPROBE_KEY_MIMPID:
|
||||
hwprobe_arch_id(pair, cpus);
|
||||
break;
|
||||
|
||||
/*
|
||||
* For forward compatibility, unknown keys don't fail the whole
|
||||
* call, but get their element key set to -1 and value set to 0
|
||||
* indicating they're unrecognized.
|
||||
*/
|
||||
default:
|
||||
pair->key = -1;
|
||||
pair->value = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int do_riscv_hwprobe(struct riscv_hwprobe __user *pairs,
|
||||
size_t pair_count, size_t cpu_count,
|
||||
unsigned long __user *cpus_user,
|
||||
unsigned int flags)
|
||||
{
|
||||
size_t out;
|
||||
int ret;
|
||||
cpumask_t cpus;
|
||||
|
||||
/* Check the reserved flags. */
|
||||
if (flags != 0)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* The interface supports taking in a CPU mask, and returns values that
|
||||
* are consistent across that mask. Allow userspace to specify NULL and
|
||||
* 0 as a shortcut to all online CPUs.
|
||||
*/
|
||||
cpumask_clear(&cpus);
|
||||
if (!cpu_count && !cpus_user) {
|
||||
cpumask_copy(&cpus, cpu_online_mask);
|
||||
} else {
|
||||
if (cpu_count > cpumask_size())
|
||||
cpu_count = cpumask_size();
|
||||
|
||||
ret = copy_from_user(&cpus, cpus_user, cpu_count);
|
||||
if (ret)
|
||||
return -EFAULT;
|
||||
|
||||
/*
|
||||
* Userspace must provide at least one online CPU, without that
|
||||
* there's no way to define what is supported.
|
||||
*/
|
||||
cpumask_and(&cpus, &cpus, cpu_online_mask);
|
||||
if (cpumask_empty(&cpus))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (out = 0; out < pair_count; out++, pairs++) {
|
||||
struct riscv_hwprobe pair;
|
||||
|
||||
if (get_user(pair.key, &pairs->key))
|
||||
return -EFAULT;
|
||||
|
||||
pair.value = 0;
|
||||
hwprobe_one_pair(&pair, &cpus);
|
||||
ret = put_user(pair.key, &pairs->key);
|
||||
if (ret == 0)
|
||||
ret = put_user(pair.value, &pairs->value);
|
||||
|
||||
if (ret)
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE5(riscv_hwprobe, struct riscv_hwprobe __user *, pairs,
|
||||
size_t, pair_count, size_t, cpu_count, unsigned long __user *,
|
||||
cpus, unsigned int, flags)
|
||||
{
|
||||
return do_riscv_hwprobe(pairs, pair_count, cpu_count,
|
||||
cpus, flags);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user