개념 설명 전체 · v6.18.37 / arch/riscv/kernel/cpu_ops_sbi.c

    1 // SPDX-License-Identifier: GPL-2.0-only
    2 /*
    3  * HSM extension and cpu_ops implementation.
    4  *
    5  * Copyright (c) 2020 Western Digital Corporation or its affiliates.
    6  */
    7 
    8 #include <linux/init.h>
    9 #include <linux/mm.h>
   10 #include <linux/sched/task_stack.h>
   11 #include <asm/cpu_ops.h>
   12 #include <asm/cpu_ops_sbi.h>
   13 #include <asm/sbi.h>
   14 #include <asm/smp.h>
   15 
   16 extern char secondary_start_sbi[];
   17 const struct cpu_operations cpu_ops_sbi;
   18 
   19 /*
   20  * Ordered booting via HSM brings one cpu at a time. However, cpu hotplug can
   21  * be invoked from multiple threads in parallel. Define an array of boot data
   22  * to handle that.
   23  */
   24 static struct sbi_hart_boot_data boot_data[NR_CPUS];
   25 
   26 static int sbi_hsm_hart_start(unsigned long hartid, unsigned long saddr,
   27 			      unsigned long priv)
   28 {
   29 	struct sbiret ret;
   30 
   31 	ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_START,
   32 			hartid, saddr, priv, 0, 0, 0);
   33 	if (ret.error)
   34 		return sbi_err_map_linux_errno(ret.error);
   35 	else
   36 		return 0;
   37 }
   38 
   39 #ifdef CONFIG_HOTPLUG_CPU
   40 static int sbi_hsm_hart_stop(void)
   41 {
   42 	struct sbiret ret;
   43 
   44 	ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_STOP, 0, 0, 0, 0, 0, 0);
   45 
   46 	if (ret.error)
   47 		return sbi_err_map_linux_errno(ret.error);
   48 	else
   49 		return 0;
   50 }
   51 
   52 static int sbi_hsm_hart_get_status(unsigned long hartid)
   53 {
   54 	struct sbiret ret;
   55 
   56 	ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_STATUS,
   57 			hartid, 0, 0, 0, 0, 0);
   58 	if (ret.error)
   59 		return sbi_err_map_linux_errno(ret.error);
   60 	else
   61 		return ret.value;
   62 }
   63 #endif
   64 
   65 static int sbi_cpu_start(unsigned int cpuid, struct task_struct *tidle)
   66 {
   67 	unsigned long boot_addr = __pa_symbol(secondary_start_sbi);
   68 	unsigned long hartid = cpuid_to_hartid_map(cpuid);
   69 	unsigned long hsm_data;
   70 	struct sbi_hart_boot_data *bdata = &boot_data[cpuid];
   71 
   72 	/* Make sure tidle is updated */
   73 	smp_mb();
   74 	bdata->task_ptr = tidle;
   75 	bdata->stack_ptr = task_pt_regs(tidle);
   76 	/* Make sure boot data is updated */
   77 	smp_mb();
   78 	hsm_data = __pa(bdata);
   79 	return sbi_hsm_hart_start(hartid, boot_addr, hsm_data);
   80 }
   81 
   82 #ifdef CONFIG_HOTPLUG_CPU
   83 static void sbi_cpu_stop(void)
   84 {
   85 	int ret;
   86 
   87 	ret = sbi_hsm_hart_stop();
   88 	pr_crit("Unable to stop the cpu %u (%d)\n", smp_processor_id(), ret);
   89 }
   90 
   91 static int sbi_cpu_is_stopped(unsigned int cpuid)
   92 {
   93 	int rc;
   94 	unsigned long hartid = cpuid_to_hartid_map(cpuid);
   95 
   96 	rc = sbi_hsm_hart_get_status(hartid);
   97 
   98 	if (rc == SBI_HSM_STATE_STOPPED)
   99 		return 0;
  100 	return rc;
  101 }
  102 #endif
  103 
  104 const struct cpu_operations cpu_ops_sbi = {
  105 	.cpu_start	= sbi_cpu_start,
  106 #ifdef CONFIG_HOTPLUG_CPU
  107 	.cpu_stop	= sbi_cpu_stop,
  108 	.cpu_is_stopped	= sbi_cpu_is_stopped,
  109 #endif
  110 };