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 43 void __init smp_prepare_boot_cpu(void) 44 { 45 } 46 47 void __init smp_prepare_cpus(unsigned int max_cpus) 48 { 49 } 50 51 void __init setup_smp(void) 52 { 53 struct device_node *dn = NULL; 54 int hart; 55 bool found_boot_cpu = false; 56 int cpuid = 1; 57 58 while ((dn = of_find_node_by_type(dn, "cpu"))) { 59 hart = riscv_of_processor_hartid(dn); 60 if (hart < 0) { 61 of_node_put(dn); 62 continue; 63 } 64 65 if (hart == cpuid_to_hartid_map(0)) { 66 BUG_ON(found_boot_cpu); 67 found_boot_cpu = 1; 68 of_node_put(dn); 69 continue; 70 } 71 72 cpuid_to_hartid_map(cpuid) = hart; 73 set_cpu_possible(cpuid, true); 74 set_cpu_present(cpuid, true); 75 cpuid++; 76 of_node_put(dn); 77 } 78 79 BUG_ON(!found_boot_cpu); 80 } 81 82 int __cpu_up(unsigned int cpu, struct task_struct *tidle) 83 { 84 int hartid = cpuid_to_hartid_map(cpu); 85 tidle->thread_info.cpu = cpu; 86 87 /* 88 * On RISC-V systems, all harts boot on their own accord. Our _start 89 * selects the first hart to boot the kernel and causes the remainder 90 * of the harts to spin in a loop waiting for their stack pointer to be 91 * setup by that main hart. Writing __cpu_up_stack_pointer signals to 92 * the spinning harts that they can continue the boot process. 93 */ 94 smp_mb(); 95 WRITE_ONCE(__cpu_up_stack_pointer[hartid], 96 task_stack_page(tidle) + THREAD_SIZE); 97 WRITE_ONCE(__cpu_up_task_pointer[hartid], tidle); 98 99 while (!cpu_online(cpu)) 100 cpu_relax(); 101 102 return 0; 103 } 104 105 void __init smp_cpus_done(unsigned int max_cpus) 106 { 107 } 108 109 /* 110 * C entry point for a secondary processor. 111 */ 112 asmlinkage void __init smp_callin(void) 113 { 114 struct mm_struct *mm = &init_mm; 115 116 /* All kernel threads share the same mm context. */ 117 mmgrab(mm); 118 current->active_mm = mm; 119 120 trap_init(); 121 notify_cpu_starting(smp_processor_id()); 122 set_cpu_online(smp_processor_id(), 1); 123 /* 124 * Remote TLB flushes are ignored while the CPU is offline, so emit 125 * a local TLB flush right now just in case. 126 */ 127 local_flush_tlb_all(); 128 /* 129 * Disable preemption before enabling interrupts, so we don't try to 130 * schedule a CPU that hasn't actually started yet. 131 */ 132 preempt_disable(); 133 local_irq_enable(); 134 cpu_startup_entry(CPUHP_AP_ONLINE_IDLE); 135 } 136