Commit 58d4fafd authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'riscv/for-v5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/riscv/linux

Pull RISC-V updates from Paul Walmsley:
 "Add the following new features:

   - Generic CPU topology description support for DT-based platforms,
     including ARM64, ARM and RISC-V.

   - Sparsemem support

   - Perf callchain support

   - SiFive PLIC irqchip modifications, in preparation for M-mode Linux

  and clean up the code base:

   - Clean up chip-specific register (CSR) manipulation code, IPIs, TLB
     flushing, and the RISC-V CPU-local timer code

   - Kbuild cleanup from one of the Kbuild maintainers"

[ The CPU topology parts came in through the arm64 tree with a shared
  branch   - Linus ]

* tag 'riscv/for-v5.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/riscv/linux:
  irqchip/sifive-plic: set max threshold for ignored handlers
  riscv: move the TLB flush logic out of line
  riscv: don't use the rdtime(h) pseudo-instructions
  riscv: cleanup riscv_cpuid_to_hartid_mask
  riscv: optimize send_ipi_single
  riscv: cleanup send_ipi_mask
  riscv: refactor the IPI code
  riscv: Add support for libdw
  riscv: Add support for perf registers sampling
  riscv: Add perf callchain support
  riscv: add arch/riscv/Kbuild
  RISC-V: Implement sparsemem
  riscv: Using CSR numbers to access CSRs
parents dbcda58a 9ce06497
# SPDX-License-Identifier: GPL-2.0-only
obj-y += kernel/ mm/ net/
......@@ -35,6 +35,8 @@ config RISCV
select HAVE_DMA_CONTIGUOUS
select HAVE_FUTEX_CMPXCHG if FUTEX
select HAVE_PERF_EVENTS
select HAVE_PERF_REGS
select HAVE_PERF_USER_STACK_DUMP
select HAVE_SYSCALL_TRACEPOINTS
select IRQ_DOMAIN
select SPARSE_IRQ
......@@ -55,6 +57,7 @@ config RISCV
select EDAC_SUPPORT
select ARCH_HAS_GIGANTIC_PAGE
select ARCH_WANT_HUGE_PMD_SHARE if 64BIT
select SPARSEMEM_STATIC if 32BIT
config MMU
def_bool y
......@@ -63,12 +66,32 @@ config ZONE_DMA32
bool
default y if 64BIT
config VA_BITS
int
default 32 if 32BIT
default 39 if 64BIT
config PA_BITS
int
default 34 if 32BIT
default 56 if 64BIT
config PAGE_OFFSET
hex
default 0xC0000000 if 32BIT && MAXPHYSMEM_2GB
default 0xffffffff80000000 if 64BIT && MAXPHYSMEM_2GB
default 0xffffffe000000000 if 64BIT && MAXPHYSMEM_128GB
config ARCH_FLATMEM_ENABLE
def_bool y
config ARCH_SPARSEMEM_ENABLE
def_bool y
select SPARSEMEM_VMEMMAP_ENABLE
config ARCH_SELECT_MEMORY_MODEL
def_bool ARCH_SPARSEMEM_ENABLE
config ARCH_WANT_GENERAL_HUGETLB
def_bool y
......
......@@ -54,6 +54,9 @@ endif
ifeq ($(CONFIG_MODULE_SECTIONS),y)
KBUILD_LDFLAGS_MODULE += -T $(srctree)/arch/riscv/kernel/module.lds
endif
ifeq ($(CONFIG_PERF_EVENTS),y)
KBUILD_CFLAGS += -fno-omit-frame-pointer
endif
KBUILD_CFLAGS_MODULE += $(call cc-option,-mno-relax)
......@@ -72,7 +75,7 @@ KBUILD_IMAGE := $(boot)/Image.gz
head-y := arch/riscv/kernel/head.o
core-y += arch/riscv/kernel/ arch/riscv/mm/ arch/riscv/net/
core-y += arch/riscv/
libs-y += arch/riscv/lib/
......
......@@ -110,8 +110,10 @@ extern unsigned long min_low_pfn;
#define page_to_bus(page) (page_to_phys(page))
#define phys_to_page(paddr) (pfn_to_page(phys_to_pfn(paddr)))
#ifdef CONFIG_FLATMEM
#define pfn_valid(pfn) \
(((pfn) >= pfn_base) && (((pfn)-pfn_base) < max_mapnr))
#endif
#define ARCH_PFN_OFFSET (pfn_base)
......
......@@ -83,6 +83,19 @@ extern pgd_t swapper_pg_dir[];
#define __S110 PAGE_SHARED_EXEC
#define __S111 PAGE_SHARED_EXEC
/*
* Roughly size the vmemmap space to be large enough to fit enough
* struct pages to map half the virtual address space. Then
* position vmemmap directly below the VMALLOC region.
*/
#define VMEMMAP_SHIFT \
(CONFIG_VA_BITS - PAGE_SHIFT - 1 + STRUCT_PAGE_MAX_SHIFT)
#define VMEMMAP_SIZE BIT(VMEMMAP_SHIFT)
#define VMEMMAP_END (VMALLOC_START - 1)
#define VMEMMAP_START (VMALLOC_START - VMEMMAP_SIZE)
#define vmemmap ((struct page *)VMEMMAP_START)
/*
* ZERO_PAGE is a global shared page that is always zero,
* used for zero-mapped memory areas, etc.
......
......@@ -61,11 +61,5 @@ static inline unsigned long cpuid_to_hartid_map(int cpu)
return boot_cpu_hartid;
}
static inline void riscv_cpuid_to_hartid_mask(const struct cpumask *in,
struct cpumask *out)
{
cpumask_set_cpu(cpuid_to_hartid_map(0), out);
}
#endif /* CONFIG_SMP */
#endif /* _ASM_RISCV_SMP_H */
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ASM_SPARSEMEM_H
#define __ASM_SPARSEMEM_H
#ifdef CONFIG_SPARSEMEM
#define MAX_PHYSMEM_BITS CONFIG_PA_BITS
#define SECTION_SIZE_BITS 27
#endif /* CONFIG_SPARSEMEM */
#endif /* __ASM_SPARSEMEM_H */
......@@ -6,43 +6,41 @@
#ifndef _ASM_RISCV_TIMEX_H
#define _ASM_RISCV_TIMEX_H
#include <asm/param.h>
#include <asm/csr.h>
typedef unsigned long cycles_t;
static inline cycles_t get_cycles_inline(void)
static inline cycles_t get_cycles(void)
{
cycles_t n;
__asm__ __volatile__ (
"rdtime %0"
: "=r" (n));
return n;
return csr_read(CSR_TIME);
}
#define get_cycles get_cycles_inline
#define get_cycles get_cycles
#ifdef CONFIG_64BIT
static inline uint64_t get_cycles64(void)
static inline u64 get_cycles64(void)
{
return get_cycles();
}
#else /* CONFIG_64BIT */
static inline u32 get_cycles_hi(void)
{
return get_cycles();
return csr_read(CSR_TIMEH);
}
#else
static inline uint64_t get_cycles64(void)
static inline u64 get_cycles64(void)
{
u32 lo, hi, tmp;
__asm__ __volatile__ (
"1:\n"
"rdtimeh %0\n"
"rdtime %1\n"
"rdtimeh %2\n"
"bne %0, %2, 1b"
: "=&r" (hi), "=&r" (lo), "=&r" (tmp));
u32 hi, lo;
do {
hi = get_cycles_hi();
lo = get_cycles();
} while (hi != get_cycles_hi());
return ((u64)hi << 32) | lo;
}
#endif
#endif /* CONFIG_64BIT */
#define ARCH_HAS_READ_CURRENT_TIMER
static inline int read_current_timer(unsigned long *timer_val)
{
*timer_val = get_cycles();
......
......@@ -25,8 +25,13 @@ static inline void local_flush_tlb_page(unsigned long addr)
__asm__ __volatile__ ("sfence.vma %0" : : "r" (addr) : "memory");
}
#ifndef CONFIG_SMP
#ifdef CONFIG_SMP
void flush_tlb_all(void);
void flush_tlb_mm(struct mm_struct *mm);
void flush_tlb_page(struct vm_area_struct *vma, unsigned long addr);
void flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
unsigned long end);
#else /* CONFIG_SMP */
#define flush_tlb_all() local_flush_tlb_all()
#define flush_tlb_page(vma, addr) local_flush_tlb_page(addr)
......@@ -37,35 +42,6 @@ static inline void flush_tlb_range(struct vm_area_struct *vma,
}
#define flush_tlb_mm(mm) flush_tlb_all()
#else /* CONFIG_SMP */
#include <asm/sbi.h>
static inline void remote_sfence_vma(struct cpumask *cmask, unsigned long start,
unsigned long size)
{
struct cpumask hmask;
cpumask_clear(&hmask);
riscv_cpuid_to_hartid_mask(cmask, &hmask);
sbi_remote_sfence_vma(hmask.bits, start, size);
}
#define flush_tlb_all() sbi_remote_sfence_vma(NULL, 0, -1)
#define flush_tlb_range(vma, start, end) \
remote_sfence_vma(mm_cpumask((vma)->vm_mm), start, (end) - (start))
static inline void flush_tlb_page(struct vm_area_struct *vma,
unsigned long addr)
{
flush_tlb_range(vma, addr, addr + PAGE_SIZE);
}
#define flush_tlb_mm(mm) \
remote_sfence_vma(mm_cpumask(mm), 0, -1)
#endif /* CONFIG_SMP */
/* Flush a range of kernel pages */
......
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/* Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. */
#ifndef _ASM_RISCV_PERF_REGS_H
#define _ASM_RISCV_PERF_REGS_H
enum perf_event_riscv_regs {
PERF_REG_RISCV_PC,
PERF_REG_RISCV_RA,
PERF_REG_RISCV_SP,
PERF_REG_RISCV_GP,
PERF_REG_RISCV_TP,
PERF_REG_RISCV_T0,
PERF_REG_RISCV_T1,
PERF_REG_RISCV_T2,
PERF_REG_RISCV_S0,
PERF_REG_RISCV_S1,
PERF_REG_RISCV_A0,
PERF_REG_RISCV_A1,
PERF_REG_RISCV_A2,
PERF_REG_RISCV_A3,
PERF_REG_RISCV_A4,
PERF_REG_RISCV_A5,
PERF_REG_RISCV_A6,
PERF_REG_RISCV_A7,
PERF_REG_RISCV_S2,
PERF_REG_RISCV_S3,
PERF_REG_RISCV_S4,
PERF_REG_RISCV_S5,
PERF_REG_RISCV_S6,
PERF_REG_RISCV_S7,
PERF_REG_RISCV_S8,
PERF_REG_RISCV_S9,
PERF_REG_RISCV_S10,
PERF_REG_RISCV_S11,
PERF_REG_RISCV_T3,
PERF_REG_RISCV_T4,
PERF_REG_RISCV_T5,
PERF_REG_RISCV_T6,
PERF_REG_RISCV_MAX,
};
#endif /* _ASM_RISCV_PERF_REGS_H */
......@@ -38,6 +38,8 @@ obj-$(CONFIG_MODULE_SECTIONS) += module-sections.o
obj-$(CONFIG_FUNCTION_TRACER) += mcount.o ftrace.o
obj-$(CONFIG_DYNAMIC_FTRACE) += mcount-dyn.o
obj-$(CONFIG_PERF_EVENTS) += perf_event.o
obj-$(CONFIG_PERF_EVENTS) += perf_event.o
obj-$(CONFIG_PERF_EVENTS) += perf_callchain.o
obj-$(CONFIG_HAVE_PERF_REGS) += perf_regs.o
clean:
......@@ -167,7 +167,7 @@ ENTRY(handle_exception)
tail do_IRQ
1:
/* Exceptions run with interrupts enabled */
csrs sstatus, SR_SIE
csrs CSR_SSTATUS, SR_SIE
/* Handle syscalls */
li t0, EXC_SYSCALL
......@@ -222,7 +222,7 @@ ret_from_syscall:
ret_from_exception:
REG_L s0, PT_SSTATUS(sp)
csrc sstatus, SR_SIE
csrc CSR_SSTATUS, SR_SIE
andi s0, s0, SR_SPP
bnez s0, resume_kernel
......@@ -265,7 +265,7 @@ work_pending:
bnez s1, work_resched
work_notifysig:
/* Handle pending signals and notify-resume requests */
csrs sstatus, SR_SIE /* Enable interrupts for do_notify_resume() */
csrs CSR_SSTATUS, SR_SIE /* Enable interrupts for do_notify_resume() */
move a0, sp /* pt_regs */
move a1, s0 /* current_thread_info->flags */
tail do_notify_resume
......
......@@ -23,7 +23,7 @@ ENTRY(__fstate_save)
li a2, TASK_THREAD_F0
add a0, a0, a2
li t1, SR_FS
csrs sstatus, t1
csrs CSR_SSTATUS, t1
frcsr t0
fsd f0, TASK_THREAD_F0_F0(a0)
fsd f1, TASK_THREAD_F1_F0(a0)
......@@ -58,7 +58,7 @@ ENTRY(__fstate_save)
fsd f30, TASK_THREAD_F30_F0(a0)
fsd f31, TASK_THREAD_F31_F0(a0)
sw t0, TASK_THREAD_FCSR_F0(a0)
csrc sstatus, t1
csrc CSR_SSTATUS, t1
ret
ENDPROC(__fstate_save)
......@@ -67,7 +67,7 @@ ENTRY(__fstate_restore)
add a0, a0, a2
li t1, SR_FS
lw t0, TASK_THREAD_FCSR_F0(a0)
csrs sstatus, t1
csrs CSR_SSTATUS, t1
fld f0, TASK_THREAD_F0_F0(a0)
fld f1, TASK_THREAD_F1_F0(a0)
fld f2, TASK_THREAD_F2_F0(a0)
......@@ -101,6 +101,6 @@ ENTRY(__fstate_restore)
fld f30, TASK_THREAD_F30_F0(a0)
fld f31, TASK_THREAD_F31_F0(a0)
fscsr t0
csrc sstatus, t1
csrc CSR_SSTATUS, t1
ret
ENDPROC(__fstate_restore)
......@@ -61,7 +61,7 @@ _start_kernel:
* floating point in kernel space
*/
li t0, SR_FS
csrc sstatus, t0
csrc CSR_SSTATUS, t0
/* Pick one hart to run the main boot sequence */
la a3, hart_lottery
......
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. */
#include <linux/perf_event.h>
#include <linux/uaccess.h>
/* Kernel callchain */
struct stackframe {
unsigned long fp;
unsigned long ra;
};
/*
* Get the return address for a single stackframe and return a pointer to the
* next frame tail.
*/
static unsigned long user_backtrace(struct perf_callchain_entry_ctx *entry,
unsigned long fp, unsigned long reg_ra)
{
struct stackframe buftail;
unsigned long ra = 0;
unsigned long *user_frame_tail =
(unsigned long *)(fp - sizeof(struct stackframe));
/* Check accessibility of one struct frame_tail beyond */
if (!access_ok(user_frame_tail, sizeof(buftail)))
return 0;
if (__copy_from_user_inatomic(&buftail, user_frame_tail,
sizeof(buftail)))
return 0;
if (reg_ra != 0)
ra = reg_ra;
else
ra = buftail.ra;
fp = buftail.fp;
if (ra != 0)
perf_callchain_store(entry, ra);
else
return 0;
return fp;
}
/*
* This will be called when the target is in user mode
* This function will only be called when we use
* "PERF_SAMPLE_CALLCHAIN" in
* kernel/events/core.c:perf_prepare_sample()
*
* How to trigger perf_callchain_[user/kernel] :
* $ perf record -e cpu-clock --call-graph fp ./program
* $ perf report --call-graph
*
* On RISC-V platform, the program being sampled and the C library
* need to be compiled with -fno-omit-frame-pointer, otherwise
* the user stack will not contain function frame.
*/
void perf_callchain_user(struct perf_callchain_entry_ctx *entry,
struct pt_regs *regs)
{
unsigned long fp = 0;
/* RISC-V does not support perf in guest mode. */
if (perf_guest_cbs && perf_guest_cbs->is_in_guest())
return;
fp = regs->s0;
perf_callchain_store(entry, regs->sepc);
fp = user_backtrace(entry, fp, regs->ra);
while (fp && !(fp & 0x3) && entry->nr < entry->max_stack)
fp = user_backtrace(entry, fp, 0);
}
bool fill_callchain(unsigned long pc, void *entry)
{
return perf_callchain_store(entry, pc);
}
void notrace walk_stackframe(struct task_struct *task,
struct pt_regs *regs, bool (*fn)(unsigned long, void *), void *arg);
void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry,
struct pt_regs *regs)
{
/* RISC-V does not support perf in guest mode. */
if (perf_guest_cbs && perf_guest_cbs->is_in_guest()) {
pr_warn("RISC-V does not support perf in guest mode!");
return;
}
walk_stackframe(NULL, regs, fill_callchain, entry);
}
// SPDX-License-Identifier: GPL-2.0
/* Copyright (C) 2019 Hangzhou C-SKY Microsystems co.,ltd. */
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/perf_event.h>
#include <linux/bug.h>
#include <asm/perf_regs.h>
#include <asm/ptrace.h>
u64 perf_reg_value(struct pt_regs *regs, int idx)
{
if (WARN_ON_ONCE((u32)idx >= PERF_REG_RISCV_MAX))
return 0;
return ((unsigned long *)regs)[idx];
}
#define REG_RESERVED (~((1ULL << PERF_REG_RISCV_MAX) - 1))
int perf_reg_validate(u64 mask)
{
if (!mask || mask & REG_RESERVED)
return -EINVAL;
return 0;
}
u64 perf_reg_abi(struct task_struct *task)
{
#if __riscv_xlen == 64
return PERF_SAMPLE_REGS_ABI_64;
#else
return PERF_SAMPLE_REGS_ABI_32;
#endif
}
void perf_get_regs_user(struct perf_regs *regs_user,
struct pt_regs *regs,
struct pt_regs *regs_user_copy)
{
regs_user->regs = task_pt_regs(current);
regs_user->abi = perf_reg_abi(current);
}
......@@ -56,6 +56,7 @@ void riscv_cpuid_to_hartid_mask(const struct cpumask *in, struct cpumask *out)
{
int cpu;
cpumask_clear(out);
for_each_cpu(cpu, in)
cpumask_set_cpu(cpuid_to_hartid_map(cpu), out);
}
......@@ -78,13 +79,42 @@ static void ipi_stop(void)
wait_for_interrupt();
}
static void send_ipi_mask(const struct cpumask *mask, enum ipi_message_type op)
{
struct cpumask hartid_mask;
int cpu;
smp_mb__before_atomic();
for_each_cpu(cpu, mask)
set_bit(op, &ipi_data[cpu].bits);
smp_mb__after_atomic();
riscv_cpuid_to_hartid_mask(mask, &hartid_mask);
sbi_send_ipi(cpumask_bits(&hartid_mask));
}
static void send_ipi_single(int cpu, enum ipi_message_type op)
{
int hartid = cpuid_to_hartid_map(cpu);
smp_mb__before_atomic();
set_bit(op, &ipi_data[cpu].bits);
smp_mb__after_atomic();
sbi_send_ipi(cpumask_bits(cpumask_of(hartid)));
}
static inline void clear_ipi(void)
{
csr_clear(CSR_SIP, SIE_SSIE);
}
void riscv_software_interrupt(void)
{
unsigned long *pending_ipis = &ipi_data[smp_processor_id()].bits;
unsigned long *stats = ipi_data[smp_processor_id()].stats;
/* Clear pending IPI */
csr_clear(CSR_SIP, SIE_SSIE);
clear_ipi();
while (true) {
unsigned long ops;
......@@ -118,23 +148,6 @@ void riscv_software_interrupt(void)
}
}
static void
send_ipi_message(const struct cpumask *to_whom, enum ipi_message_type operation)
{
int cpuid, hartid;
struct cpumask hartid_mask;
cpumask_clear(&hartid_mask);
mb();
for_each_cpu(cpuid, to_whom) {
set_bit(operation, &ipi_data[cpuid].bits);
hartid = cpuid_to_hartid_map(cpuid);
cpumask_set_cpu(hartid, &hartid_mask);
}
mb();
sbi_send_ipi(cpumask_bits(&hartid_mask));
}
static const char * const ipi_names[] = {
[IPI_RESCHEDULE] = "Rescheduling interrupts",
[IPI_CALL_FUNC] = "Function call interrupts",
......@@ -156,12 +169,12 @@ void show_ipi_stats(struct seq_file *p, int prec)
void arch_send_call_function_ipi_mask(struct cpumask *mask)
{
send_ipi_message(mask, IPI_CALL_FUNC);
send_ipi_mask(mask, IPI_CALL_FUNC);
}
void arch_send_call_function_single_ipi(int cpu)
{
send_ipi_message(cpumask_of(cpu), IPI_CALL_FUNC);
send_ipi_single(cpu, IPI_CALL_FUNC);
}
void smp_send_stop(void)
......@@ -176,7 +189,7 @@ void smp_send_stop(void)
if (system_state <= SYSTEM_RUNNING)
pr_crit("SMP: stopping secondary CPUs\n");
send_ipi_message(&mask, IPI_CPU_STOP);
send_ipi_mask(&mask, IPI_CPU_STOP);
}
/* Wait up to one second for other CPUs to stop */
......@@ -191,6 +204,5 @@ void smp_send_stop(void)
void smp_send_reschedule(int cpu)
{
send_ipi_message(cpumask_of(cpu), IPI_RESCHEDULE);
send_ipi_single(cpu, IPI_RESCHEDULE);
}
......@@ -19,8 +19,8 @@ struct stackframe {
unsigned long ra;
};
static void notrace walk_stackframe(struct task_struct *task,