1 /* $OpenBSD: ipifuncs.c,v 1.22 2024/04/14 19:08:09 miod Exp $ */
2 /* $NetBSD: ipifuncs.c,v 1.8 2006/10/07 18:11:36 rjs Exp $ */
3
4 /*-
5 * Copyright (c) 2004 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/proc.h>
33
34 #include <uvm/uvm_extern.h>
35
36 #include <machine/cpu.h>
37 #include <machine/ctlreg.h>
38 #include <machine/hypervisor.h>
39 #include <machine/pte.h>
40 #include <machine/sparc64.h>
41
42 #define SPARC64_IPI_RETRIES 10000
43
44 void sun4u_send_ipi(int, void (*)(void), u_int64_t, u_int64_t);
45 void sun4u_broadcast_ipi(void (*)(void), u_int64_t, u_int64_t);
46 void sun4v_send_ipi(int, void (*)(void), u_int64_t, u_int64_t);
47 void sun4v_broadcast_ipi(void (*)(void), u_int64_t, u_int64_t);
48
49 /*
50 * These are the "function" entry points in locore.s to handle IPI's.
51 */
52 void sun4u_ipi_tlb_page_demap(void);
53 void sun4u_ipi_tlb_context_demap(void);
54 void sun4v_ipi_tlb_page_demap(void);
55 void sun4v_ipi_tlb_context_demap(void);
56 void ipi_softint(void);
57
58 /*
59 * Send an interprocessor interrupt.
60 */
61 void
sparc64_send_ipi(int itid,void (* func)(void),u_int64_t arg0,u_int64_t arg1)62 sparc64_send_ipi(int itid, void (*func)(void), u_int64_t arg0, u_int64_t arg1)
63 {
64 if (CPU_ISSUN4V)
65 sun4v_send_ipi(itid, func, arg0, arg1);
66 else
67 sun4u_send_ipi(itid, func, arg0, arg1);
68 }
69
70 void
sun4u_send_ipi(int itid,void (* func)(void),u_int64_t arg0,u_int64_t arg1)71 sun4u_send_ipi(int itid, void (*func)(void), u_int64_t arg0, u_int64_t arg1)
72 {
73 int i, j, shift = 0;
74
75 KASSERT((u_int64_t)func > MAXINTNUM);
76
77 /*
78 * UltraSPARC-IIIi CPUs select the BUSY/NACK pair based on the
79 * lower two bits of the ITID.
80 */
81 if (((getver() & VER_IMPL) >> VER_IMPL_SHIFT) == IMPL_JALAPENO)
82 shift = (itid & 0x3) * 2;
83
84 if (ldxa(0, ASR_IDSR) & (IDSR_BUSY << shift)) {
85 __asm volatile("ta 1; nop");
86 }
87
88 /* Schedule an interrupt. */
89 for (i = 0; i < SPARC64_IPI_RETRIES; i++) {
90 u_int64_t s = intr_disable();
91
92 stxa(IDDR_0H, ASI_INTERRUPT_DISPATCH, (u_int64_t)func);
93 stxa(IDDR_1H, ASI_INTERRUPT_DISPATCH, arg0);
94 stxa(IDDR_2H, ASI_INTERRUPT_DISPATCH, arg1);
95 stxa(IDCR(itid), ASI_INTERRUPT_DISPATCH, 0);
96 membar_sync();
97
98 for (j = 0; j < 1000000; j++) {
99 if (ldxa(0, ASR_IDSR) & (IDSR_BUSY << shift))
100 continue;
101 else
102 break;
103 }
104 intr_restore(s);
105
106 if (j == 1000000)
107 break;
108
109 if ((ldxa(0, ASR_IDSR) & (IDSR_NACK << shift)) == 0)
110 return;
111 }
112
113 #if 1
114 if (db_active || panicstr != NULL)
115 printf("ipi_send: couldn't send ipi to module %u\n", itid);
116 else
117 panic("ipi_send: couldn't send ipi");
118 #else
119 __asm volatile("ta 1; nop" : :);
120 #endif
121 }
122
123 void
sun4v_send_ipi(int itid,void (* func)(void),u_int64_t arg0,u_int64_t arg1)124 sun4v_send_ipi(int itid, void (*func)(void), u_int64_t arg0, u_int64_t arg1)
125 {
126 struct cpu_info *ci = curcpu();
127 u_int64_t s;
128 int err, i;
129
130 s = intr_disable();
131
132 stha(ci->ci_cpuset, ASI_PHYS_CACHED, itid);
133 stxa(ci->ci_mondo, ASI_PHYS_CACHED, (vaddr_t)func);
134 stxa(ci->ci_mondo + 8, ASI_PHYS_CACHED, arg0);
135 stxa(ci->ci_mondo + 16, ASI_PHYS_CACHED, arg1);
136
137 for (i = 0; i < SPARC64_IPI_RETRIES; i++) {
138 err = hv_cpu_mondo_send(1, ci->ci_cpuset, ci->ci_mondo);
139 if (err != H_EWOULDBLOCK)
140 break;
141 delay(10);
142 }
143
144 intr_restore(s);
145
146 if (err != H_EOK)
147 panic("Unable to send mondo %llx to cpu %d: %d",
148 (u_int64_t)func, itid, err);
149 }
150
151 /*
152 * Broadcast an IPI to all but ourselves.
153 */
154 void
sparc64_broadcast_ipi(void (* func)(void),u_int64_t arg0,u_int64_t arg1)155 sparc64_broadcast_ipi(void (*func)(void), u_int64_t arg0, u_int64_t arg1)
156 {
157 if (CPU_ISSUN4V)
158 sun4v_broadcast_ipi(func, arg0, arg1);
159 else
160 sun4u_broadcast_ipi(func, arg0, arg1);
161 }
162
163 void
sun4u_broadcast_ipi(void (* func)(void),u_int64_t arg0,u_int64_t arg1)164 sun4u_broadcast_ipi(void (*func)(void), u_int64_t arg0, u_int64_t arg1)
165 {
166 struct cpu_info *ci;
167
168 for (ci = cpus; ci != NULL; ci = ci->ci_next) {
169 if (ci->ci_cpuid == cpu_number())
170 continue;
171 if ((ci->ci_flags & CPUF_RUNNING) == 0)
172 continue;
173 sun4u_send_ipi(ci->ci_itid, func, arg0, arg1);
174 }
175 }
176
177 void
sun4v_broadcast_ipi(void (* func)(void),u_int64_t arg0,u_int64_t arg1)178 sun4v_broadcast_ipi(void (*func)(void), u_int64_t arg0, u_int64_t arg1)
179 {
180 struct cpu_info *ci = curcpu();
181 paddr_t cpuset = ci->ci_cpuset;
182 int err, i, ncpus = 0;
183
184 for (ci = cpus; ci != NULL; ci = ci->ci_next) {
185 if (ci->ci_cpuid == cpu_number())
186 continue;
187 if ((ci->ci_flags & CPUF_RUNNING) == 0)
188 continue;
189 stha(cpuset, ASI_PHYS_CACHED, ci->ci_itid);
190 cpuset += sizeof(int16_t);
191 ncpus++;
192 }
193
194 if (ncpus == 0)
195 return;
196
197 ci = curcpu();
198 stxa(ci->ci_mondo, ASI_PHYS_CACHED, (vaddr_t)func);
199 stxa(ci->ci_mondo + 8, ASI_PHYS_CACHED, arg0);
200 stxa(ci->ci_mondo + 16, ASI_PHYS_CACHED, arg1);
201
202 for (i = 0; i < SPARC64_IPI_RETRIES; i++) {
203 err = hv_cpu_mondo_send(ncpus, ci->ci_cpuset, ci->ci_mondo);
204 if (err != H_EWOULDBLOCK)
205 break;
206 delay(10);
207 }
208 if (err != H_EOK)
209 panic("Unable to broadcast mondo %llx: %d",
210 (u_int64_t)func, err);
211 }
212
213 void
smp_tlb_flush_pte(vaddr_t va,uint64_t ctx)214 smp_tlb_flush_pte(vaddr_t va, uint64_t ctx)
215 {
216 (*sp_tlb_flush_pte)(va, ctx);
217
218 if (db_active)
219 return;
220
221 if (CPU_ISSUN4V)
222 sun4v_broadcast_ipi(sun4v_ipi_tlb_page_demap, va, ctx);
223 else
224 sun4u_broadcast_ipi(sun4u_ipi_tlb_page_demap, va, ctx);
225 }
226
227 void
smp_tlb_flush_ctx(uint64_t ctx)228 smp_tlb_flush_ctx(uint64_t ctx)
229 {
230 (*sp_tlb_flush_ctx)(ctx);
231
232 if (db_active)
233 return;
234
235 if (CPU_ISSUN4V)
236 sun4v_broadcast_ipi(sun4v_ipi_tlb_context_demap, ctx, 0);
237 else
238 sun4u_broadcast_ipi(sun4u_ipi_tlb_context_demap, ctx, 0);
239 }
240
241 void
cpu_unidle(struct cpu_info * ci)242 cpu_unidle(struct cpu_info *ci)
243 {
244 if (ci == curcpu() || db_active || ((ci->ci_flags & CPUF_RUNNING) == 0))
245 return;
246
247 if (CPU_ISSUN4V)
248 sun4v_send_ipi(ci->ci_itid, ipi_softint, 1 << IPL_SOFTINT, 0);
249 else
250 sun4u_send_ipi(ci->ci_itid, ipi_softint, 1 << IPL_SOFTINT, 0);
251 }
252