lkml.org 
[lkml]   [2020]   [Mar]   [17]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v11 10/11] RISC-V: Add supported for ordered booting method using HSM
    Date
    Currently, all harts have to jump Linux in RISC-V. This complicates the
    multi-stage boot process as every transient stage also has to ensure all
    harts enter to that stage and jump to Linux afterwards. It also obstructs
    a clean Kexec implementation.

    SBI HSM extension provides alternate solutions where only a single hart
    need to boot and enter Linux. The booting hart can bring up secondary
    harts one by one afterwards.

    Add SBI HSM based cpu_ops that implements an ordered booting method in
    RISC-V. This change is also backward compatible with older firmware not
    implementing HSM extension. If a latest kernel is used with older
    firmware, it will continue to use the default spinning booting method.

    Signed-off-by: Atish Patra <atish.patra@wdc.com>
    Reviewed-by: Anup Patel <anup@brainfault.org>
    ---
    arch/riscv/kernel/Makefile | 3 ++
    arch/riscv/kernel/cpu_ops.c | 10 +++-
    arch/riscv/kernel/cpu_ops_sbi.c | 81 +++++++++++++++++++++++++++++++++
    arch/riscv/kernel/head.S | 26 +++++++++++
    arch/riscv/kernel/smpboot.c | 2 +-
    arch/riscv/kernel/traps.c | 2 +-
    6 files changed, 121 insertions(+), 3 deletions(-)
    create mode 100644 arch/riscv/kernel/cpu_ops_sbi.c

    diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile
    index f81a6ff88005..a0be34b96846 100644
    --- a/arch/riscv/kernel/Makefile
    +++ b/arch/riscv/kernel/Makefile
    @@ -44,5 +44,8 @@ obj-$(CONFIG_PERF_EVENTS) += perf_event.o
    obj-$(CONFIG_PERF_EVENTS) += perf_callchain.o
    obj-$(CONFIG_HAVE_PERF_REGS) += perf_regs.o
    obj-$(CONFIG_RISCV_SBI) += sbi.o
    +ifeq ($(CONFIG_RISCV_SBI), y)
    +obj-$(CONFIG_SMP) += cpu_ops_sbi.o
    +endif

    clean:
    diff --git a/arch/riscv/kernel/cpu_ops.c b/arch/riscv/kernel/cpu_ops.c
    index 62705908eee5..c4c33bf02369 100644
    --- a/arch/riscv/kernel/cpu_ops.c
    +++ b/arch/riscv/kernel/cpu_ops.c
    @@ -18,6 +18,7 @@ const struct cpu_operations *cpu_ops[NR_CPUS] __ro_after_init;
    void *__cpu_up_stack_pointer[NR_CPUS];
    void *__cpu_up_task_pointer[NR_CPUS];

    +extern const struct cpu_operations cpu_ops_sbi;
    extern const struct cpu_operations cpu_ops_spinwait;

    void cpu_update_secondary_bootdata(unsigned int cpuid,
    @@ -34,5 +35,12 @@ void cpu_update_secondary_bootdata(unsigned int cpuid,

    void __init cpu_set_ops(int cpuid)
    {
    - cpu_ops[cpuid] = &cpu_ops_spinwait;
    +#if IS_ENABLED(CONFIG_RISCV_SBI)
    + if (sbi_probe_extension(SBI_EXT_HSM) > 0) {
    + if (!cpuid)
    + pr_info("SBI v0.2 HSM extension detected\n");
    + cpu_ops[cpuid] = &cpu_ops_sbi;
    + } else
    +#endif
    + cpu_ops[cpuid] = &cpu_ops_spinwait;
    }
    diff --git a/arch/riscv/kernel/cpu_ops_sbi.c b/arch/riscv/kernel/cpu_ops_sbi.c
    new file mode 100644
    index 000000000000..66f3cded91f5
    --- /dev/null
    +++ b/arch/riscv/kernel/cpu_ops_sbi.c
    @@ -0,0 +1,81 @@
    +// SPDX-License-Identifier: GPL-2.0-only
    +/*
    + * HSM extension and cpu_ops implementation.
    + *
    + * Copyright (c) 2020 Western Digital Corporation or its affiliates.
    + */
    +
    +#include <linux/init.h>
    +#include <linux/mm.h>
    +#include <asm/cpu_ops.h>
    +#include <asm/sbi.h>
    +#include <asm/smp.h>
    +
    +extern char secondary_start_sbi[];
    +const struct cpu_operations cpu_ops_sbi;
    +
    +static int sbi_hsm_hart_start(unsigned long hartid, unsigned long saddr,
    + unsigned long priv)
    +{
    + struct sbiret ret;
    +
    + ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_START,
    + hartid, saddr, priv, 0, 0, 0);
    + if (ret.error)
    + return sbi_err_map_linux_errno(ret.error);
    + else
    + return 0;
    +}
    +
    +#ifdef CONFIG_HOTPLUG_CPU
    +static int sbi_hsm_hart_stop(void)
    +{
    + struct sbiret ret;
    +
    + ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_STOP, 0, 0, 0, 0, 0, 0);
    +
    + if (ret.error)
    + return sbi_err_map_linux_errno(ret.error);
    + else
    + return 0;
    +}
    +
    +static int sbi_hsm_hart_get_status(unsigned long hartid)
    +{
    + struct sbiret ret;
    +
    + ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_STATUS,
    + hartid, 0, 0, 0, 0, 0);
    + if (ret.error)
    + return sbi_err_map_linux_errno(ret.error);
    + else
    + return ret.value;
    +}
    +#endif
    +
    +static int sbi_cpu_start(unsigned int cpuid, struct task_struct *tidle)
    +{
    + int rc;
    + unsigned long boot_addr = __pa_symbol(secondary_start_sbi);
    + int hartid = cpuid_to_hartid_map(cpuid);
    +
    + cpu_update_secondary_bootdata(cpuid, tidle);
    + rc = sbi_hsm_hart_start(hartid, boot_addr, 0);
    +
    + return rc;
    +}
    +
    +static int sbi_cpu_prepare(unsigned int cpuid)
    +{
    + if (!cpu_ops_sbi.cpu_start) {
    + pr_err("cpu start method not defined for CPU [%d]\n", cpuid);
    + return -ENODEV;
    + }
    + return 0;
    +}
    +
    +const struct cpu_operations cpu_ops_sbi = {
    + .name = "sbi",
    + .cpu_prepare = sbi_cpu_prepare,
    + .cpu_start = sbi_cpu_start,
    +};
    diff --git a/arch/riscv/kernel/head.S b/arch/riscv/kernel/head.S
    index 173507395a6b..e5115d5e0b3a 100644
    --- a/arch/riscv/kernel/head.S
    +++ b/arch/riscv/kernel/head.S
    @@ -99,11 +99,37 @@ relocate:
    ret
    #endif /* CONFIG_MMU */
    #ifdef CONFIG_SMP
    + .global secondary_start_sbi
    +secondary_start_sbi:
    + /* Mask all interrupts */
    + csrw CSR_IE, zero
    + csrw CSR_IP, zero
    +
    + /* Load the global pointer */
    + .option push
    + .option norelax
    + la gp, __global_pointer$
    + .option pop
    +
    + /*
    + * Disable FPU to detect illegal usage of
    + * floating point in kernel space
    + */
    + li t0, SR_FS
    + csrc CSR_STATUS, t0
    +
    /* Set trap vector to spin forever to help debug */
    la a3, .Lsecondary_park
    csrw CSR_TVEC, a3

    slli a3, a0, LGREG
    + la a4, __cpu_up_stack_pointer
    + la a5, __cpu_up_task_pointer
    + add a4, a3, a4
    + add a5, a3, a5
    + REG_L sp, (a4)
    + REG_L tp, (a5)
    +
    .global secondary_start_common
    secondary_start_common:

    diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c
    index e89396a2a1af..4e9922790f6e 100644
    --- a/arch/riscv/kernel/smpboot.c
    +++ b/arch/riscv/kernel/smpboot.c
    @@ -143,7 +143,7 @@ void __init smp_cpus_done(unsigned int max_cpus)
    /*
    * C entry point for a secondary processor.
    */
    -asmlinkage __visible void __init smp_callin(void)
    +asmlinkage __visible void smp_callin(void)
    {
    struct mm_struct *mm = &init_mm;

    diff --git a/arch/riscv/kernel/traps.c b/arch/riscv/kernel/traps.c
    index ffb3d94bf0cc..8e13ad45ccaa 100644
    --- a/arch/riscv/kernel/traps.c
    +++ b/arch/riscv/kernel/traps.c
    @@ -147,7 +147,7 @@ int is_valid_bugaddr(unsigned long pc)
    }
    #endif /* CONFIG_GENERIC_BUG */

    -void __init trap_init(void)
    +void trap_init(void)
    {
    /*
    * Set sup0 scratch register to 0, indicating to exception vector
    --
    2.25.1
    \
     
     \ /
      Last update: 2020-03-18 02:13    [W:6.512 / U:0.092 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site