Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Documentation/arch/riscv/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ RISC-V architecture
vm-layout
hwprobe
patch-acceptance
pmu-sse
uabi
vector
cmodx
Expand Down
55 changes: 55 additions & 0 deletions Documentation/arch/riscv/pmu-sse.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
.. SPDX-License-Identifier: GPL-2.0

========================================
RISC-V PMU overflow delivery through SSE
========================================

When ``CONFIG_RISCV_PMU_SBI_SSE`` is enabled and firmware provides the local
PMU overflow event, the RISC-V SBI PMU driver uses Supervisor Software Events
(SSE) to deliver counter overflows.

Delivery selection
==================

The delivery mechanism is selected once while the PMU device is probed. The
driver first tries to register and enable the local PMU overflow SSE event. It
tries the ordinary PMU interrupt only when the SSE extension or the local PMU
event is explicitly unsupported. It does not change the delivery mechanism
after the PMU has been registered.

Other SSE setup errors do not prove that firmware released the overflow route,
so the driver does not enable the ordinary PMU interrupt. The PMU remains
available for counting but not sampling. If setup retained an SSE event, the
PMU keeps quiescing that event around perf scheduling changes without rearming
it. This preserves callback ownership without creating two possible delivery
mechanisms for the same hardware overflow.

Interrupted context
===================

SSE enters Linux through a supervisor handler context constructed by firmware.
The entry contract preserves the interrupted GPRs except ``a6`` and ``a7``,
which Linux reads from the interrupted-register event attributes. Linux then
reconstructs the interrupted ``pt_regs`` and publishes it while dispatching the
event.

Perf uses that context for register samples and callchains. Kernel stack
walking verifies that the interrupted frame belongs to the current task stack
before dereferencing it. For sensitive entry paths and IRQ stacks, Linux
records the interrupted PC without walking a stack whose bounds cannot be
proved. User callchains use the existing no-fault user unwinder. A DWARF
raw user-stack copy from an SSE handler must not take a page fault, so it
walks the current task's page tables with fast-only GUP, copies each resident
page through its kernel mapping, and stops at the first non-resident page,
preserving perf's truncated-user-stack semantics.

CPU power management
====================

The PMU and SSE CPU power-management callbacks are ordered according to the
selected delivery mechanism. With SSE delivery, SSE events are masked before
the lower-priority PMU callback disables the local PMU event and stops the
counters on entry. On exit, the hart is unmasked while the local PMU event is
still disabled. The PMU callback then restores the counters and enables the
event, so an unmask failure leaves the counters stopped. The ordinary
interrupt path retains the existing PMU ordering.
22 changes: 22 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -23318,6 +23318,7 @@ C: irc://irc.libera.chat/riscv
P: Documentation/arch/riscv/patch-acceptance.rst
T: git git://git.kernel.org/pub/scm/linux/kernel/git/riscv/linux.git
F: arch/riscv/
F: tools/testing/selftests/riscv/
N: riscv
K: riscv

Expand All @@ -23330,6 +23331,13 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/iommu/linux.git
F: Documentation/devicetree/bindings/iommu/riscv,iommu.yaml
F: drivers/iommu/riscv/

RISC-V FIRMWARE DRIVERS
M: Conor Dooley <conor@kernel.org>
L: linux-riscv@lists.infradead.org
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/conor/linux.git
F: drivers/firmware/riscv/

RISC-V MICROCHIP SUPPORT
M: Conor Dooley <conor.dooley@microchip.com>
M: Daire McNamara <daire.mcnamara@microchip.com>
Expand Down Expand Up @@ -23385,6 +23393,7 @@ M: Atish Patra <atish.patra@linux.dev>
R: Anup Patel <anup@brainfault.org>
L: linux-riscv@lists.infradead.org
S: Supported
F: Documentation/arch/riscv/pmu-sse.rst
F: drivers/perf/riscv_pmu.c
F: drivers/perf/riscv_pmu_legacy.c
F: drivers/perf/riscv_pmu_sbi.c
Expand Down Expand Up @@ -23424,6 +23433,19 @@ F: arch/riscv/boot/dts/spacemit/
N: spacemit
K: spacemit

RISC-V SUPERVISOR SOFTWARE EVENTS
M: Zhanpeng Zhang <zhangzhanpeng.jasper@bytedance.com>
M: Himanshu Chauhan <himanshu.chauhan@oss.qualcomm.com>
R: Yunhui Cui <cuiyunhui@bytedance.com>
L: linux-riscv@lists.infradead.org
S: Maintained
F: arch/riscv/include/asm/sse.h
F: arch/riscv/kernel/sbi_sse.c
F: arch/riscv/kernel/sbi_sse_entry.S
F: drivers/firmware/riscv/riscv_sbi_sse.c
F: include/linux/riscv_sbi_sse.h
F: tools/testing/selftests/riscv/sse/

RISC-V TENSTORRENT SoC SUPPORT
M: Drew Fustini <dfustini@oss.tenstorrent.com>
M: Joel Stanley <jms@oss.tenstorrent.com>
Expand Down
14 changes: 11 additions & 3 deletions arch/riscv/include/asm/asm.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,16 +91,24 @@
.endm

#ifdef CONFIG_SMP
.macro asm_per_cpu dst sym tmp
lw \tmp, TASK_TI_CPU_NUM(tp)
slli \tmp, \tmp, RISCV_LGPTR
.macro asm_per_cpu_with_cpu dst sym tmp cpu
slli \tmp, \cpu, RISCV_LGPTR
la \dst, __per_cpu_offset
add \dst, \dst, \tmp
REG_L \tmp, 0(\dst)
la \dst, \sym
add \dst, \dst, \tmp
.endm

.macro asm_per_cpu dst sym tmp
lw \tmp, TASK_TI_CPU_NUM(tp)
asm_per_cpu_with_cpu \dst \sym \tmp \tmp
.endm
#else /* CONFIG_SMP */
.macro asm_per_cpu_with_cpu dst sym tmp cpu
la \dst, \sym
.endm

.macro asm_per_cpu dst sym tmp
la \dst, \sym
.endm
Expand Down
10 changes: 10 additions & 0 deletions arch/riscv/include/asm/perf_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@
(regs)->sp = current_stack_pointer; \
(regs)->status = SR_PP; \
}

#ifdef CONFIG_RISCV_SBI_SSE
/*
* Raw user-stack sampling can run in the NMI-like SSE context. Route it
* through an implementation that does not fault on a non-resident page.
*/
unsigned long riscv_perf_out_copy_user(void *dst, const void *src,
unsigned long n);
#define arch_perf_out_copy_user riscv_perf_out_copy_user
#endif
#endif

#endif /* _ASM_RISCV_PERF_EVENT_H */
63 changes: 63 additions & 0 deletions arch/riscv/include/asm/sbi.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ enum sbi_ext_id {
SBI_EXT_FWFT = 0x46574654,
SBI_EXT_MPXY = 0x4D505859,
SBI_EXT_DBTR = 0x44425452,
SBI_EXT_SSE = 0x535345,

/* Experimentals extensions must lie within this range */
SBI_EXT_EXPERIMENTAL_START = 0x08000000,
Expand Down Expand Up @@ -506,6 +507,68 @@ enum sbi_mpxy_rpmi_attribute_id {
#define SBI_MPXY_CHAN_CAP_SEND_WITHOUT_RESP BIT(4)
#define SBI_MPXY_CHAN_CAP_GET_NOTIFICATIONS BIT(5)

enum sbi_ext_sse_fid {
SBI_SSE_EVENT_ATTR_READ = 0,
SBI_SSE_EVENT_ATTR_WRITE,
SBI_SSE_EVENT_REGISTER,
SBI_SSE_EVENT_UNREGISTER,
SBI_SSE_EVENT_ENABLE,
SBI_SSE_EVENT_DISABLE,
SBI_SSE_EVENT_COMPLETE,
SBI_SSE_EVENT_INJECT,
SBI_SSE_HART_UNMASK,
SBI_SSE_HART_MASK,
};

enum sbi_sse_state {
SBI_SSE_STATE_UNUSED = 0,
SBI_SSE_STATE_REGISTERED = 1,
SBI_SSE_STATE_ENABLED = 2,
SBI_SSE_STATE_RUNNING = 3,
};

/* SBI SSE Event Attributes. */
enum sbi_sse_attr_id {
SBI_SSE_ATTR_STATUS = 0x00000000,
SBI_SSE_ATTR_PRIO = 0x00000001,
SBI_SSE_ATTR_CONFIG = 0x00000002,
SBI_SSE_ATTR_PREFERRED_HART = 0x00000003,
SBI_SSE_ATTR_ENTRY_PC = 0x00000004,
SBI_SSE_ATTR_ENTRY_ARG = 0x00000005,
SBI_SSE_ATTR_INTERRUPTED_SEPC = 0x00000006,
SBI_SSE_ATTR_INTERRUPTED_FLAGS = 0x00000007,
SBI_SSE_ATTR_INTERRUPTED_A6 = 0x00000008,
SBI_SSE_ATTR_INTERRUPTED_A7 = 0x00000009,

SBI_SSE_ATTR_MAX = 0x0000000A
};

#define SBI_SSE_ATTR_STATUS_STATE_OFFSET 0
#define SBI_SSE_ATTR_STATUS_STATE_MASK 0x3
#define SBI_SSE_ATTR_STATUS_PENDING_OFFSET 2
#define SBI_SSE_ATTR_STATUS_INJECT_OFFSET 3

#define SBI_SSE_ATTR_CONFIG_ONESHOT BIT(0)

#define SBI_SSE_ATTR_INTERRUPTED_FLAGS_SSTATUS_SPP BIT(0)
#define SBI_SSE_ATTR_INTERRUPTED_FLAGS_SSTATUS_SPIE BIT(1)
#define SBI_SSE_ATTR_INTERRUPTED_FLAGS_HSTATUS_SPV BIT(2)
#define SBI_SSE_ATTR_INTERRUPTED_FLAGS_HSTATUS_SPVP BIT(3)
#define SBI_SSE_ATTR_INTERRUPTED_FLAGS_SSTATUS_SPELP BIT(4)
#define SBI_SSE_ATTR_INTERRUPTED_FLAGS_SSTATUS_SDT BIT(5)

#define SBI_SSE_EVENT_LOCAL_HIGH_PRIO_RAS 0x00000000
#define SBI_SSE_EVENT_LOCAL_DOUBLE_TRAP 0x00000001
#define SBI_SSE_EVENT_GLOBAL_HIGH_PRIO_RAS 0x00008000
#define SBI_SSE_EVENT_LOCAL_PMU_OVERFLOW 0x00010000
#define SBI_SSE_EVENT_LOCAL_LOW_PRIO_RAS 0x00100000
#define SBI_SSE_EVENT_GLOBAL_LOW_PRIO_RAS 0x00108000
#define SBI_SSE_EVENT_LOCAL_SOFTWARE_INJECTED 0xffff0000
#define SBI_SSE_EVENT_GLOBAL_SOFTWARE_INJECTED 0xffff8000

#define SBI_SSE_EVENT_PLATFORM BIT(14)
#define SBI_SSE_EVENT_GLOBAL BIT(15)

/* SBI debug triggers function IDs */
enum sbi_ext_dbtr_fid {
SBI_EXT_DBTR_NUM_TRIGGERS = 0,
Expand Down
7 changes: 7 additions & 0 deletions arch/riscv/include/asm/scs.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
load_per_cpu gp, irq_shadow_call_stack_ptr, \tmp
.endm

/* Load the per-CPU IRQ shadow call stack to gp. */
.macro scs_load_sse_stack reg_evt
REG_L gp, SSE_REG_EVT_SHADOW_STACK(\reg_evt)
.endm

/* Load task_scs_sp(current) to gp. */
.macro scs_load_current
REG_L gp, TASK_TI_SCS_SP(tp)
Expand All @@ -40,6 +45,8 @@
.endm
.macro scs_load_irq_stack tmp
.endm
.macro scs_load_sse_stack reg_evt
.endm
.macro scs_load_current
.endm
.macro scs_load_current_if_task_changed prev
Expand Down
82 changes: 82 additions & 0 deletions arch/riscv/include/asm/sse.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2024 Rivos Inc.
*/
#ifndef __ASM_SSE_H
#define __ASM_SSE_H

#include <linux/printk.h>
#include <linux/types.h>

#include <asm/sbi.h>

static inline bool riscv_sse_available(void)
{
#ifdef CONFIG_RISCV_SBI
return sbi_probe_extension(SBI_EXT_SSE) > 0;
#else
return false;
#endif
}

static inline void riscv_sse_mask_current_hart(void)
{
#ifdef CONFIG_RISCV_SBI
struct sbiret ret;

if (!riscv_sse_available())
return;

ret = sbi_ecall(SBI_EXT_SSE, SBI_SSE_HART_MASK, 0, 0, 0, 0, 0, 0);
if (ret.error && ret.error != SBI_ERR_ALREADY_STOPPED)
pr_emerg("SSE hart mask failed: %ld\n", ret.error);
#endif
}

#ifdef CONFIG_RISCV_SBI_SSE

struct sse_event_interrupted_state {
unsigned long a6;
unsigned long a7;
};

struct sse_event_arch_data {
void *stack;
void *shadow_stack;
unsigned long tmp;
struct sse_event_interrupted_state *interrupted;
phys_addr_t interrupted_phys;
u32 evt_id;
unsigned long hart_id;
unsigned int cpu_id;
};

struct riscv_sse_interrupted_context {
struct pt_regs *regs;
unsigned long hstatus;
};

static inline bool sse_event_is_global(u32 evt)
{
return !!(evt & SBI_SSE_EVENT_GLOBAL);
}

void arch_sse_event_update_cpu(struct sse_event_arch_data *arch_evt, int cpu);
int arch_sse_init_event(struct sse_event_arch_data *arch_evt, u32 evt_id,
int cpu);
void arch_sse_free_event(struct sse_event_arch_data *arch_evt);
int arch_sse_register_event(struct sse_event_arch_data *arch_evt);
void arch_sse_init_cpu(void);

void sse_handle_event(struct sse_event_arch_data *arch_evt,
struct pt_regs *regs);
asmlinkage void handle_sse(void);
asmlinkage void noinstr do_sse(struct sse_event_arch_data *arch_evt,
struct pt_regs *regs, unsigned long hstatus);

const struct riscv_sse_interrupted_context *
riscv_sse_get_interrupted_context(void);

#endif

#endif
1 change: 1 addition & 0 deletions arch/riscv/include/asm/thread_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#define OVERFLOW_STACK_SIZE SZ_4K

#define IRQ_STACK_SIZE THREAD_SIZE
#define SSE_STACK_SIZE THREAD_SIZE

#ifndef __ASSEMBLER__

Expand Down
1 change: 1 addition & 0 deletions arch/riscv/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ obj-$(CONFIG_DYNAMIC_FTRACE) += mcount-dyn.o
obj-$(CONFIG_PERF_EVENTS) += perf_callchain.o
obj-$(CONFIG_HAVE_PERF_REGS) += perf_regs.o
obj-$(CONFIG_RISCV_SBI) += sbi.o sbi_ecall.o
obj-$(CONFIG_RISCV_SBI_SSE) += sbi_sse.o sbi_sse_entry.o
ifeq ($(CONFIG_RISCV_SBI), y)
obj-$(CONFIG_SMP) += sbi-ipi.o
obj-$(CONFIG_SMP) += cpu_ops_sbi.o
Expand Down
14 changes: 14 additions & 0 deletions arch/riscv/kernel/asm-offsets.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include <asm/ptrace.h>
#include <asm/cpu_ops_sbi.h>
#include <asm/stacktrace.h>
#include <asm/sbi.h>
#include <asm/sse.h>
#include <asm/suspend.h>

void asm_offsets(void);
Expand Down Expand Up @@ -533,6 +535,18 @@ void asm_offsets(void)
DEFINE(FREGS_A6, offsetof(struct __arch_ftrace_regs, a6));
DEFINE(FREGS_A7, offsetof(struct __arch_ftrace_regs, a7));
#endif

#ifdef CONFIG_RISCV_SBI_SSE
OFFSET(SSE_REG_EVT_STACK, sse_event_arch_data, stack);
OFFSET(SSE_REG_EVT_SHADOW_STACK, sse_event_arch_data, shadow_stack);
OFFSET(SSE_REG_EVT_TMP, sse_event_arch_data, tmp);
OFFSET(SSE_REG_HART_ID, sse_event_arch_data, hart_id);
OFFSET(SSE_REG_CPU_ID, sse_event_arch_data, cpu_id);

DEFINE(SBI_EXT_SSE, SBI_EXT_SSE);
DEFINE(SBI_SSE_EVENT_COMPLETE, SBI_SSE_EVENT_COMPLETE);
DEFINE(ASM_NR_CPUS, CONFIG_NR_CPUS);
#endif
#ifdef CONFIG_RISCV_SBI
DEFINE(SBI_EXT_FWFT, SBI_EXT_FWFT);
DEFINE(SBI_EXT_FWFT_SET, SBI_EXT_FWFT_SET);
Expand Down
Loading
Loading