1 /* 2 * SMP initialisation and IPI support 3 * Based on arch/arm64/kernel/smp.c 4 * 5 * Copyright (C) 2012 ARM Ltd. 6 * Copyright (C) 2015 Regents of the University of California 7 * Copyright (C) 2017 SiFive 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License version 2 as 11 * published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 */ 18 19 #include <linux/module.h> 20 #include <linux/init.h> 21 #include <linux/kernel.h> 22 #include <linux/mm.h> 23 #include <linux/sched.h> 24 #include <linux/kernel_stat.h> 25 #include <linux/notifier.h> 26 #include <linux/cpu.h> 27 #include <linux/percpu.h> 28 #include <linux/delay.h> 29 #include <linux/err.h> 30 #include <linux/irq.h> 31 #include <linux/of.h> 32 #include <linux/sched/task_stack.h> 33 #include <linux/sched/mm.h> 34 #include <asm/irq.h> 35 #include <asm/mmu_context.h> 36 #include <asm/tlbflush.h> 37 #include <asm/sections.h> 38 #include <asm/sbi.h> 39 40 void *__cpu_up_stack_pointer[NR_CPUS]; 41 void *__cpu_up_task_pointer[NR_CPUS]; 42 static DECLARE_COMPLETION(cpu_running); 43 44 void __init smp_prepare_boot_cpu(void) 45 { 46 } 47 48 void __init smp_prepare_cpus(unsigned int max_cpus) 49 { 50 int cpuid; 51 52 /* This covers non-smp usecase mandated by "nosmp" option */ 53 if (max_cpus == 0) 54 return; 55 56 for_each_possible_cpu(cpuid) { 57 if (cpuid == smp_processor_id()) 58 continue; 59 set_cpu_present(cpuid, true); 60 } 61 } 62 63 void __init setup_smp(void) 64 { 65 struct device_node *dn; 66 int hart; 67 bool found_boot_cpu = false; 68 int cpuid = 1; 69 70 for_each_of_cpu_node(dn) { 71 hart = riscv_of_processor_hartid(dn); 72 if (hart < 0) 73 continue; 74 75 if (hart == cpuid_to_hartid_map(0)) { 76 BUG_ON(found_boot_cpu); 77 found_boot_cpu = 1; 78 continue; 79 } 80 if (cpuid >= NR_CPUS) { 81 pr_warn("Invalid cpuid [%d] for hartid [%d]\n", 82 cpuid, hart); 83 break; 84 } 85 86 cpuid_to_hartid_map(cpuid) = hart; 87 cpuid++; 88 } 89 90 BUG_ON(!found_boot_cpu); 91 92 if (cpuid > nr_cpu_ids) 93 pr_warn("Total number of cpus [%d] is greater than nr_cpus option value [%d]\n", 94 cpuid, nr_cpu_ids); 95 96 for (cpuid = 1; cpuid < nr_cpu_ids; cpuid++) { 97 if (cpuid_to_hartid_map(cpuid) != INVALID_HARTID) 98 set_cpu_possible(cpuid, true); 99 } 100 } 101 102 int __cpu_up(unsigned int cpu, struct task_struct *tidle) 103 { 104 int ret = 0; 105 int hartid = cpuid_to_hartid_map(cpu); 106 tidle->thread_info.cpu = cpu; 107 108 /* 109 * On RISC-V systems, all harts boot on their own accord. Our _start 110 * selects the first hart to boot the kernel and causes the remainder 111 * of the harts to spin in a loop waiting for their stack pointer to be 112 * setup by that main hart. Writing __cpu_up_stack_pointer signals to 113 * the spinning harts that they can continue the boot process. 114 */ 115 smp_mb(); 116 WRITE_ONCE(__cpu_up_stack_pointer[hartid], 117 task_stack_page(tidle) + THREAD_SIZE); 118 WRITE_ONCE(__cpu_up_task_pointer[hartid], tidle); 119 120 lockdep_assert_held(&cpu_running); 121 wait_for_completion_timeout(&cpu_running, 122 msecs_to_jiffies(1000)); 123 124 if (!cpu_online(cpu)) { 125 pr_crit("CPU%u: failed to come online\n", cpu); 126 ret = -EIO; 127 } 128 129 return ret; 130 } 131 132 void __init smp_cpus_done(unsigned int max_cpus) 133 { 134 } 135 136 /* 137 * C entry point for a secondary processor. 138 */ 139 asmlinkage void __init smp_callin(void) 140 { 141 struct mm_struct *mm = &init_mm; 142 143 /* All kernel threads share the same mm context. */ 144 mmgrab(mm); 145 current->active_mm = mm; 146 147 trap_init(); 148 notify_cpu_starting(smp_processor_id()); 149 set_cpu_online(smp_processor_id(), 1); 150 /* 151 * Remote TLB flushes are ignored while the CPU is offline, so emit 152 * a local TLB flush right now just in case. 153 */ 154 local_flush_tlb_all(); 155 complete(&cpu_running); 156 /* 157 * Disable preemption before enabling interrupts, so we don't try to 158 * schedule a CPU that hasn't actually started yet. 159 */ 160 preempt_disable(); 161 local_irq_enable(); 162 cpu_startup_entry(CPUHP_AP_ONLINE_IDLE); 163 } 164