| // Copyright 2020 The Fuchsia Authors |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| #include <assert.h> |
| #include <platform.h> |
| #include <trace.h> |
| #include <zircon/errors.h> |
| #include <zircon/types.h> |
| |
| #include <arch/mp.h> |
| #include <arch/ops.h> |
| #include <dev/interrupt.h> |
| #include <kernel/event.h> |
| #include <arch/riscv64/sbi.h> |
| |
| #define LOCAL_TRACE 0 |
| |
| // mapping of cpu -> hart |
| uint64_t cpu_to_hart_map[SMP_MAX_CPUS] = {0}; |
| |
| // list of IPIs queued per cpu |
| static volatile ktl::atomic<int> ipi_data[SMP_MAX_CPUS]; |
| |
| // total number of detected cpus |
| uint riscv64_num_cpus = 1; |
| |
| // per cpu structures, each cpu will point to theirs using the fixed register |
| riscv64_percpu riscv64_percpu_array[SMP_MAX_CPUS]; |
| |
| void arch_register_hart(uint cpu_num, uint64_t hart_id) { |
| cpu_to_hart_map[cpu_num] = hart_id; |
| } |
| |
| // software triggered exceptions, used for cross-cpu calls |
| void riscv64_software_exception(void) { |
| uint current_hart = riscv64_curr_hart_id(); |
| |
| sbi_clear_ipi(); |
| |
| rmb(); |
| int reason = ipi_data[current_hart].exchange(0); |
| LTRACEF("current_hart %u reason %#x\n", current_hart, reason); |
| |
| if (reason & (1u << MP_IPI_RESCHEDULE)) { |
| mp_mbx_reschedule_irq(nullptr); |
| reason &= ~(1u << MP_IPI_RESCHEDULE); |
| } |
| if (reason & (1u << MP_IPI_GENERIC)) { |
| mp_mbx_generic_irq(nullptr); |
| reason &= ~(1u << MP_IPI_GENERIC); |
| } |
| if (reason & (1u << MP_IPI_INTERRUPT)) { |
| mp_mbx_interrupt_irq(nullptr); |
| panic("unimplemented MP_IPI_INTERRUPT\n"); |
| reason &= ~(1u << MP_IPI_INTERRUPT); |
| } |
| if (reason & (1u << MP_IPI_HALT)) { |
| panic("unimplemented MP_IPI_HALT\n"); |
| reason &= ~(1u << MP_IPI_HALT); |
| } |
| |
| if (unlikely(reason)) { |
| TRACEF("unhandled ipi cause %#x, hartid %#x\n", reason, current_hart); |
| panic("stopping"); |
| } |
| } |
| |
| void arch_prepare_current_cpu_idle_state(bool idle) { |
| // no-op |
| } |
| |
| void arch_mp_reschedule(cpu_mask_t mask) { |
| arch_mp_send_ipi(MP_IPI_TARGET_MASK, mask, MP_IPI_RESCHEDULE); |
| } |
| |
| void arch_mp_send_ipi(mp_ipi_target_t target, cpu_mask_t mask, mp_ipi_t ipi) { |
| LTRACEF("target %d mask %#x, ipi %d\n", target, mask, ipi); |
| |
| ulong hart_mask = 0; |
| |
| // translate the high level target + mask mechanism into just a mask |
| switch (target) { |
| case MP_IPI_TARGET_ALL: |
| hart_mask = (1ul << SMP_MAX_CPUS) - 1; |
| break; |
| case MP_IPI_TARGET_ALL_BUT_LOCAL: |
| hart_mask = mask_all_but_one(riscv64_curr_hart_id()); |
| break; |
| case MP_IPI_TARGET_MASK: |
| for (uint cpu = 0; cpu < SMP_MAX_CPUS && mask; cpu++, mask >>= 1) { |
| if (mask & 1) { |
| uint64_t hart = cpu_to_hart_map[cpu]; |
| LTRACEF("cpu %u hart %lu mask %#x\n", cpu, hart, mask); |
| |
| // record a pending hart to notify |
| hart_mask |= (1ul << hart); |
| |
| // set the ipi_data based on the incoming ipi |
| ipi_data[hart].fetch_or(1u << ipi); |
| } |
| } |
| break; |
| } |
| |
| mb(); |
| LTRACEF("sending to hart_mask %#lx\n", hart_mask); |
| sbi_send_ipis(&hart_mask); |
| } |
| |
| void riscv64_init_percpu_early(uint hart_id, uint cpu_num) { |
| // basic bootstrapping of this cpu |
| riscv64_percpu_array[cpu_num].cpu_num = cpu_num; |
| riscv64_percpu_array[cpu_num].hart_id = hart_id; |
| riscv64_set_percpu(&riscv64_percpu_array[cpu_num]); |
| arch_register_hart(cpu_num, hart_id); |
| wmb(); |
| } |
| |
| void arch_mp_init_percpu(void) { interrupt_init_percpu(); } |
| |
| void arch_flush_state_and_halt(Event* flush_done) { |
| DEBUG_ASSERT(arch_ints_disabled()); |
| Thread::Current::Get()->preemption_state().PreemptDisable(); |
| flush_done->Signal(); |
| platform_halt_cpu(); |
| panic("control should never reach here\n"); |
| } |
| |
| zx_status_t arch_mp_prep_cpu_unplug(uint cpu_id) { |
| if (cpu_id == 0 || cpu_id >= riscv64_num_cpus) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t arch_mp_cpu_unplug(uint cpu_id) { |
| // we do not allow unplugging the bootstrap processor |
| if (cpu_id == 0 || cpu_id >= riscv64_num_cpus) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t arch_mp_cpu_hotplug(cpu_num_t cpu_id) { return ZX_ERR_NOT_SUPPORTED; } |
| |
| void arch_setup_percpu(cpu_num_t cpu_num, struct percpu* percpu) { |
| riscv64_percpu* arch_percpu = &riscv64_percpu_array[cpu_num]; |
| DEBUG_ASSERT(arch_percpu->high_level_percpu == nullptr); |
| arch_percpu->high_level_percpu = percpu; |
| } |
| |