| 1 | |
| 2 | |
| 3 | * SMP initialisation and IPI support
|
| 4 | * Based on arch/arm64/kernel/smp.c
|
| 5 | *
|
| 6 | * Copyright (C) 2012 ARM Ltd.
|
| 7 | * Copyright (C) 2015 Regents of the University of California
|
| 8 | * Copyright (C) 2017 SiFive
|
| 9 |
|
| 10 |
|
| 11 | #include <linux/acpi.h>
|
| 12 | #include <linux/arch_topology.h>
|
| 13 | #include <linux/module.h>
|
| 14 | #include <linux/init.h>
|
| 15 | #include <linux/kernel.h>
|
| 16 | #include <linux/mm.h>
|
| 17 | #include <linux/sched.h>
|
| 18 | #include <linux/kernel_stat.h>
|
| 19 | #include <linux/notifier.h>
|
| 20 | #include <linux/cpu.h>
|
| 21 | #include <linux/percpu.h>
|
| 22 | #include <linux/delay.h>
|
| 23 | #include <linux/err.h>
|
| 24 | #include <linux/irq.h>
|
| 25 | #include <linux/of.h>
|
| 26 | #include <linux/sched/task_stack.h>
|
| 27 | #include <linux/sched/mm.h>
|
| 28 |
|
| 29 | #include <asm/cacheflush.h>
|
| 30 | #include <asm/cpu_ops.h>
|
| 31 | #include <asm/irq.h>
|
| 32 | #include <asm/mmu_context.h>
|
| 33 | #include <asm/numa.h>
|
| 34 | #include <asm/tlbflush.h>
|
| 35 | #include <asm/sections.h>
|
| 36 | #include <asm/smp.h>
|
| 37 | #include <uapi/asm/hwcap.h>
|
| 38 | #include <asm/vector.h>
|
| 39 |
|
| 40 | #include "head.h"
|
| 41 |
|
| 42 | #ifndef CONFIG_HOTPLUG_PARALLEL
|
| 43 | static DECLARE_COMPLETION(cpu_running);
|
| 44 | #endif
|
| 45 |
|
| 46 | void __init smp_prepare_cpus(unsigned int max_cpus)
|
| 47 | {
|
| 48 | int cpuid;
|
| 49 | unsigned int curr_cpuid;
|
| 50 |
|
| 51 | init_cpu_topology();
|
| 52 |
|
| 53 | curr_cpuid = smp_processor_id();
|
| 54 | store_cpu_topology(curr_cpuid);
|
| 55 | numa_store_cpu_info(curr_cpuid);
|
| 56 | numa_add_cpu(curr_cpuid);
|
| 57 |
|
| 58 |
|
| 59 | if (max_cpus == 0)
|
| 60 | return;
|
| 61 |
|
| 62 | for_each_possible_cpu(cpuid) {
|
| 63 | if (cpuid == curr_cpuid)
|
| 64 | continue;
|
| 65 | set_cpu_present(cpuid, true);
|
| 66 | numa_store_cpu_info(cpuid);
|
| 67 | }
|
| 68 | }
|
| 69 |
|
| 70 | #ifdef CONFIG_ACPI
|
| 71 | static unsigned int cpu_count = 1;
|
| 72 |
|
| 73 | static int __init acpi_parse_rintc(union acpi_subtable_headers *header, const unsigned long end)
|
| 74 | {
|
| 75 | unsigned long hart;
|
| 76 | static bool found_boot_cpu;
|
| 77 | struct acpi_madt_rintc *processor = (struct acpi_madt_rintc *)header;
|
| 78 |
|
| 79 | |
| 80 | * Each RINTC structure in MADT will have a flag. If ACPI_MADT_ENABLED
|
| 81 | * bit in the flag is not enabled, it means OS should not try to enable
|
| 82 | * the cpu to which RINTC belongs.
|
| 83 |
|
| 84 | if (!(processor->flags & ACPI_MADT_ENABLED))
|
| 85 | return 0;
|
| 86 |
|
| 87 | if (BAD_MADT_ENTRY(processor, end))
|
| 88 | return -EINVAL;
|
| 89 |
|
| 90 | acpi_table_print_madt_entry(&header->common);
|
| 91 |
|
| 92 | hart = processor->hart_id;
|
| 93 | if (hart == INVALID_HARTID) {
|
| 94 | pr_warn("Invalid hartid\n");
|
| 95 | return 0;
|
| 96 | }
|
| 97 |
|
| 98 | if (hart == cpuid_to_hartid_map(0)) {
|
| 99 | BUG_ON(found_boot_cpu);
|
| 100 | found_boot_cpu = true;
|
| 101 | return 0;
|
| 102 | }
|
| 103 |
|
| 104 | if (cpu_count >= NR_CPUS) {
|
| 105 | pr_warn("NR_CPUS is too small for the number of ACPI tables.\n");
|
| 106 | return 0;
|
| 107 | }
|
| 108 |
|
| 109 | cpuid_to_hartid_map(cpu_count) = hart;
|
| 110 | cpu_count++;
|
| 111 |
|
| 112 | return 0;
|
| 113 | }
|
| 114 |
|
| 115 | static void __init acpi_parse_and_init_cpus(void)
|
| 116 | {
|
| 117 | acpi_table_parse_madt(ACPI_MADT_TYPE_RINTC, acpi_parse_rintc, 0);
|
| 118 | }
|
| 119 | #else
|
| 120 | #define acpi_parse_and_init_cpus(...) do { } while (0)
|
| 121 | #endif
|
| 122 |
|
| 123 | static void __init of_parse_and_init_cpus(void)
|
| 124 | {
|
| 125 | struct device_node *dn;
|
| 126 | unsigned long hart;
|
| 127 | bool found_boot_cpu = false;
|
| 128 | int cpuid = 1;
|
| 129 | int rc;
|
| 130 |
|
| 131 | for_each_of_cpu_node(dn) {
|
| 132 | rc = riscv_early_of_processor_hartid(dn, &hart);
|
| 133 | if (rc < 0)
|
| 134 | continue;
|
| 135 |
|
| 136 | if (hart == cpuid_to_hartid_map(0)) {
|
| 137 | BUG_ON(found_boot_cpu);
|
| 138 | found_boot_cpu = 1;
|
| 139 | early_map_cpu_to_node(0, of_node_to_nid(dn));
|
| 140 | continue;
|
| 141 | }
|
| 142 | if (cpuid >= NR_CPUS) {
|
| 143 | pr_warn("Invalid cpuid [%d] for hartid [%lu]\n",
|
| 144 | cpuid, hart);
|
| 145 | continue;
|
| 146 | }
|
| 147 |
|
| 148 | cpuid_to_hartid_map(cpuid) = hart;
|
| 149 | early_map_cpu_to_node(cpuid, of_node_to_nid(dn));
|
| 150 | cpuid++;
|
| 151 | }
|
| 152 |
|
| 153 | BUG_ON(!found_boot_cpu);
|
| 154 |
|
| 155 | if (cpuid > nr_cpu_ids)
|
| 156 | pr_warn("Total number of cpus [%d] is greater than nr_cpus option value [%d]\n",
|
| 157 | cpuid, nr_cpu_ids);
|
| 158 | }
|
| 159 |
|
| 160 | void __init setup_smp(void)
|
| 161 | {
|
| 162 | int cpuid;
|
| 163 |
|
| 164 | cpu_set_ops();
|
| 165 |
|
| 166 | if (acpi_disabled)
|
| 167 | of_parse_and_init_cpus();
|
| 168 | else
|
| 169 | acpi_parse_and_init_cpus();
|
| 170 |
|
| 171 | for (cpuid = 1; cpuid < nr_cpu_ids; cpuid++)
|
| 172 | if (cpuid_to_hartid_map(cpuid) != INVALID_HARTID)
|
| 173 | set_cpu_possible(cpuid, true);
|
| 174 | }
|
| 175 |
|
| 176 | static int start_secondary_cpu(int cpu, struct task_struct *tidle)
|
| 177 | {
|
| 178 | if (cpu_ops->cpu_start)
|
| 179 | return cpu_ops->cpu_start(cpu, tidle);
|
| 180 |
|
| 181 | return -EOPNOTSUPP;
|
| 182 | }
|
| 183 |
|
| 184 | #ifdef CONFIG_HOTPLUG_PARALLEL
|
| 185 | int arch_cpuhp_kick_ap_alive(unsigned int cpu, struct task_struct *tidle)
|
| 186 | {
|
| 187 | return start_secondary_cpu(cpu, tidle);
|
| 188 | }
|
| 189 | #else
|
| 190 | int __cpu_up(unsigned int cpu, struct task_struct *tidle)
|
| 191 | {
|
| 192 | int ret;
|
| 193 | tidle->thread_info.cpu = cpu;
|
| 194 |
|
| 195 | ret = start_secondary_cpu(cpu, tidle);
|
| 196 | if (!ret) {
|
| 197 | wait_for_completion_timeout(&cpu_running, secs_to_jiffies(1));
|
| 198 |
|
| 199 | if (!cpu_online(cpu)) {
|
| 200 | pr_crit("CPU%u: failed to come online\n", cpu);
|
| 201 | ret = -EIO;
|
| 202 | }
|
| 203 | } else {
|
| 204 | pr_crit("CPU%u: failed to start\n", cpu);
|
| 205 | }
|
| 206 |
|
| 207 | return ret;
|
| 208 | }
|
| 209 | #endif
|
| 210 |
|
| 211 | void __init smp_cpus_done(unsigned int max_cpus)
|
| 212 | {
|
| 213 | }
|
| 214 |
|
| 215 | |
| 216 | * C entry point for a secondary processor.
|
| 217 |
|
| 218 | asmlinkage __visible void smp_callin(void)
|
| 219 | {
|
| 220 | struct mm_struct *mm = &init_mm;
|
| 221 | unsigned int curr_cpuid = smp_processor_id();
|
| 222 |
|
| 223 | if (has_vector()) {
|
| 224 | |
| 225 | * Return as early as possible so the hart with a mismatching
|
| 226 | * vlen won't boot.
|
| 227 |
|
| 228 | if (riscv_v_setup_vsize())
|
| 229 | return;
|
| 230 | }
|
| 231 |
|
| 232 |
|
| 233 | mmgrab(mm);
|
| 234 | current->active_mm = mm;
|
| 235 |
|
| 236 | #ifdef CONFIG_HOTPLUG_PARALLEL
|
| 237 | cpuhp_ap_sync_alive();
|
| 238 | #endif
|
| 239 |
|
| 240 | store_cpu_topology(curr_cpuid);
|
| 241 | notify_cpu_starting(curr_cpuid);
|
| 242 |
|
| 243 | riscv_ipi_enable();
|
| 244 |
|
| 245 | numa_add_cpu(curr_cpuid);
|
| 246 |
|
| 247 | riscv_clear_hypervisor_csr();
|
| 248 |
|
| 249 | pr_debug("CPU%u: Booted secondary hartid %lu\n", curr_cpuid,
|
| 250 | cpuid_to_hartid_map(curr_cpuid));
|
| 251 |
|
| 252 | set_cpu_online(curr_cpuid, true);
|
| 253 |
|
| 254 | |
| 255 | * Remote instruction cache and TLB flushes are ignored while the CPU
|
| 256 | * is offline, so flush them both right now just in case.
|
| 257 |
|
| 258 | local_flush_icache_all();
|
| 259 | local_flush_tlb_all();
|
| 260 | #ifndef CONFIG_HOTPLUG_PARALLEL
|
| 261 | complete(&cpu_running);
|
| 262 | #endif
|
| 263 | local_irq_enable();
|
| 264 | cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
|
| 265 | }
|