1 /*
2 * Copyright (c) 2019 Elliot Berman
3 * Copyright (c) 2020 Travis Geiselbrecht
4 *
5 * Use of this source code is governed by a MIT-style
6 * license that can be found in the LICENSE file or at
7 * https://opensource.org/licenses/MIT
8 */
9
10 #include <arch/riscv.h>
11
12 #include <lk/reg.h>
13 #include <lk/compiler.h>
14 #include <lk/debug.h>
15 #include <lk/trace.h>
16 #include <lk/err.h>
17 #include <lk/init.h>
18 #include <lk/main.h>
19 #include <stdlib.h>
20 #include <string.h>
21
22 #include <arch/atomic.h>
23 #include <arch/ops.h>
24 #include <arch/mp.h>
25 #include <arch/riscv/clint.h>
26
27 #include "riscv_priv.h"
28
29 #if WITH_SMP
30
31 #define LOCAL_TRACE 0
32
33 // mapping of cpu -> hart
34 static uint cpu_to_hart_map[SMP_MAX_CPUS];
35
36 // list of IPIs queued per cpu
37 static volatile int ipi_data[SMP_MAX_CPUS];
38
39 static spin_lock_t boot_cpu_lock = 1;
40
41 // list of cpus to boot, passed from platform
42 static uint harts_to_boot[SMP_MAX_CPUS];
43 static uint harts_to_boot_count = 0;
44
45 // modified in start.S to save the physical address of _start as the first cpu boots
46 uintptr_t _start_physical;
47
arch_mp_send_ipi(mp_cpu_mask_t target,mp_ipi_t ipi)48 status_t arch_mp_send_ipi(mp_cpu_mask_t target, mp_ipi_t ipi) {
49 LTRACEF("target 0x%x, ipi %u\n", target, ipi);
50
51 mp_cpu_mask_t m = target;
52 ulong hart_mask = 0;
53 for (uint c = 0; c < SMP_MAX_CPUS && m; c++, m >>= 1) {
54 if (m & 1) {
55 int h = cpu_to_hart_map[c];
56 LTRACEF("c %u h %d m %#x\n", c, h, m);
57
58 // record a pending hart to notify
59 hart_mask |= (1ul << h);
60
61 // set the ipi_data based on the incoming ipi
62 atomic_or(&ipi_data[h], (1u << ipi));
63 }
64 }
65
66 mb();
67 #if RISCV_M_MODE
68 clint_send_ipis(&hart_mask);
69 #else
70 sbi_send_ipis(&hart_mask);
71 #endif
72
73 return NO_ERROR;
74 }
75
76 // software triggered exceptions, used for cross-cpu calls
riscv_software_exception(void)77 enum handler_return riscv_software_exception(void) {
78 uint curr_cpu = arch_curr_cpu_num();
79
80 #if RISCV_M_MODE
81 uint ch = riscv_current_hart();
82 clint_ipi_clear(ch);
83 #else
84 sbi_clear_ipi();
85 #endif
86
87 rmb();
88 int reason = atomic_swap(&ipi_data[curr_cpu], 0);
89 LTRACEF("cpu %u reason %#x\n", curr_cpu, reason);
90
91 enum handler_return ret = INT_NO_RESCHEDULE;
92 if (reason & (1u << MP_IPI_RESCHEDULE)) {
93 ret = mp_mbx_reschedule_irq();
94 reason &= ~(1u << MP_IPI_RESCHEDULE);
95 }
96 if (reason & (1u << MP_IPI_GENERIC)) {
97 panic("unimplemented MP_IPI_GENERIC\n");
98 reason &= ~(1u << MP_IPI_GENERIC);
99 }
100
101 if (unlikely(reason)) {
102 TRACEF("unhandled ipi cause %#x, cpu %u\n", reason, curr_cpu);
103 panic("stopping");
104 }
105
106 return ret;
107 }
108
109 // called in very early percpu bringup
riscv_configure_percpu_mp_early(uint hart_id,uint cpu_num)110 void riscv_configure_percpu_mp_early(uint hart_id, uint cpu_num) {
111 cpu_to_hart_map[cpu_num] = hart_id;
112 wmb();
113 }
114
115 // called from assembly
116 void riscv_secondary_entry(uint hart_id, uint __unused, uint cpu_id);
riscv_secondary_entry(uint hart_id,uint __unused,uint cpu_id)117 void riscv_secondary_entry(uint hart_id, uint __unused, uint cpu_id) {
118 // basic bootstrapping of this cpu
119 riscv_early_init_percpu();
120
121 if (unlikely(arch_curr_cpu_num() >= SMP_MAX_CPUS)) {
122 while (1) {
123 arch_idle();
124 }
125 }
126
127 // spin here waiting for the main cpu to release us
128 spin_lock(&boot_cpu_lock);
129 spin_unlock(&boot_cpu_lock);
130
131 #if RISCV_MMU
132 // let the mmu code configure per cpu bits
133 riscv_mmu_init_secondaries();
134 #endif
135
136 // run early secondary cpu init routines up to the threading level
137 lk_init_level(LK_INIT_FLAG_SECONDARY_CPUS, LK_INIT_LEVEL_EARLIEST, LK_INIT_LEVEL_THREADING - 1);
138
139 // run threading level initialization on this cpu
140 riscv_init_percpu();
141
142 dprintf(INFO, "RISCV: secondary hart coming up: mvendorid %#lx marchid %#lx mimpid %#lx mhartid %#x\n",
143 riscv_get_mvendorid(), riscv_get_marchid(),
144 riscv_get_mimpid(), riscv_current_hart());
145
146 lk_secondary_cpu_entry();
147 }
148
149 // platform hands the arch layer a list of harts to start, these will be
150 // started later in riscv_boot_secondaries()
riscv_set_secondary_harts_to_start(const uint * harts,size_t count)151 void riscv_set_secondary_harts_to_start(const uint *harts, size_t count) {
152 harts_to_boot_count = MIN(count, SMP_MAX_CPUS);
153 memcpy(harts_to_boot, harts, harts_to_boot_count * sizeof(harts[0]));
154 }
155
156 // start any secondary cpus we are set to start. called on the boot processor
riscv_boot_secondaries(void)157 void riscv_boot_secondaries(void) {
158 // if theres nothing to start, abort here
159 if (harts_to_boot_count == 0) {
160 dprintf(INFO, "RISCV: No secondary harts to start\n");
161 return;
162 }
163
164 lk_init_secondary_cpus(harts_to_boot_count);
165
166 #if RISCV_M_MODE
167 dprintf(INFO, "RISCV: Releasing all secondary harts from purgatory\n");
168
169 // For machine mode, we simply unblock all the trapped cpus in start.S by releasing
170 // the boot_cpu_lock. Via some mechanism or other, all of the cpus should have already entered
171 // start.S via the main entry point for this to work. This is the default behavior when
172 // running on QEMU, for example.
173 spin_unlock(&boot_cpu_lock);
174 #else
175 dprintf(INFO, "RISCV: Going to try to start %d secondary harts\n", harts_to_boot_count);
176
177 // May as well release the boot cpu lock now, since there's no reason to hold back secondaries we
178 // haven't started yet.
179 spin_unlock(&boot_cpu_lock);
180
181 // use SBI HSM to boot the secondaries
182 uint boot_hart = riscv_current_hart();
183 for (uint i = 0; i < harts_to_boot_count; i++) {
184 // skip the boot hart
185 if (harts_to_boot[i] != boot_hart) {
186 dprintf(INFO, "RISCV: using SBI to start hart %u at %#lx\n", harts_to_boot[i], _start_physical);
187 sbi_boot_hart(harts_to_boot[i], _start_physical, 0);
188
189 // Pause a little bit here to give the secondary an opportunity to start. Not strictly
190 // needed but helps the boot output flow without much of a boot penalty. Remove
191 // if it's a problem.
192 thread_sleep(50);
193 }
194 }
195 #endif
196 }
197
198
199 #endif
200