| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * Boot secondary CPUs | |
| * | |
| * Copyright (C) 2023, Ventana Micro Systems Inc., Andrew Jones <ajones@ventanamicro.com> | |
| */ | |
| #include <libcflat.h> | |
| #include <alloc_page.h> | |
| #include <cpumask.h> | |
| #include <asm/csr.h> | |
| #include <asm/io.h> | |
| #include <asm/mmu.h> | |
| #include <asm/page.h> | |
| #include <asm/processor.h> | |
| #include <asm/sbi.h> | |
| #include <asm/smp.h> | |
| cpumask_t cpu_present_mask; | |
| cpumask_t cpu_online_mask; | |
| cpumask_t cpu_idle_mask; | |
| secondary_func_t secondary_cinit(struct secondary_data *data) | |
| { | |
| struct thread_info *info; | |
| __mmu_enable(data->satp); | |
| thread_info_init(); | |
| local_hart_init(); | |
| info = current_thread_info(); | |
| set_cpu_online(info->cpu, true); | |
| smp_send_event(); | |
| return data->func; | |
| } | |
| static void __smp_boot_secondary(int cpu, secondary_func_t func) | |
| { | |
| void *sp_mem = __va(cpus[cpu].sp); | |
| struct secondary_data *data; | |
| struct sbiret ret; | |
| if (!sp_mem) { | |
| phys_addr_t sp_phys; | |
| sp_mem = alloc_pages(1) + SZ_8K - 16; | |
| sp_phys = virt_to_phys(sp_mem); | |
| cpus[cpu].sp = __pa(sp_phys); | |
| assert(__va(cpus[cpu].sp) == sp_mem); | |
| } | |
| sp_mem -= sizeof(struct secondary_data); | |
| data = (struct secondary_data *)sp_mem; | |
| data->satp = csr_read(CSR_SATP); | |
| data->stvec = csr_read(CSR_STVEC); | |
| data->func = func; | |
| ret = sbi_hart_start(cpus[cpu].hartid, __pa(secondary_entry), cpus[cpu].sp); | |
| assert(ret.error == SBI_SUCCESS); | |
| } | |
| void smp_boot_secondary(int cpu, void (*func)(void)) | |
| { | |
| struct sbiret ret; | |
| do { | |
| ret = sbi_hart_get_status(cpus[cpu].hartid); | |
| assert(!ret.error); | |
| } while (ret.value == SBI_EXT_HSM_STOP_PENDING); | |
| assert_msg(ret.value == SBI_EXT_HSM_STOPPED, "CPU%d is not stopped", cpu); | |
| __smp_boot_secondary(cpu, func); | |
| while (!cpu_online(cpu)) | |
| smp_wait_for_event(); | |
| } | |
| void smp_boot_secondary_nofail(int cpu, void (*func)(void)) | |
| { | |
| struct sbiret ret; | |
| do { | |
| ret = sbi_hart_get_status(cpus[cpu].hartid); | |
| assert(!ret.error); | |
| } while (ret.value == SBI_EXT_HSM_STOP_PENDING); | |
| if (ret.value == SBI_EXT_HSM_STOPPED) | |
| __smp_boot_secondary(cpu, func); | |
| else | |
| assert_msg(ret.value == SBI_EXT_HSM_START_PENDING || ret.value == SBI_EXT_HSM_STARTED, | |
| "CPU%d is in an unexpected state %ld", cpu, ret.value); | |
| while (!cpu_online(cpu)) | |
| smp_wait_for_event(); | |
| } |