]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/ia64/ia64/mp_machdep.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / ia64 / ia64 / mp_machdep.c
1 /*-
2  * Copyright (c) 2001-2005 Marcel Moolenaar
3  * Copyright (c) 2000 Doug Rabson
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include "opt_kstack_pages.h"
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/ktr.h>
36 #include <sys/proc.h>
37 #include <sys/bus.h>
38 #include <sys/lock.h>
39 #include <sys/malloc.h>
40 #include <sys/mutex.h>
41 #include <sys/kernel.h>
42 #include <sys/pcpu.h>
43 #include <sys/sched.h>
44 #include <sys/smp.h>
45 #include <sys/sysctl.h>
46 #include <sys/uuid.h>
47
48 #include <vm/vm.h>
49 #include <vm/pmap.h>
50 #include <vm/vm_extern.h>
51 #include <vm/vm_kern.h>
52
53 #include <machine/atomic.h>
54 #include <machine/cpu.h>
55 #include <machine/fpu.h>
56 #include <machine/intr.h>
57 #include <machine/mca.h>
58 #include <machine/md_var.h>
59 #include <machine/pal.h>
60 #include <machine/pcb.h>
61 #include <machine/pmap.h>
62 #include <machine/sal.h>
63 #include <machine/smp.h>
64 #include <i386/include/specialreg.h>
65
66 MALLOC_DECLARE(M_PMAP);
67
68 void ia64_ap_startup(void);
69
70 #define LID_SAPIC_ID(x)         ((int)((x) >> 24) & 0xff)
71 #define LID_SAPIC_EID(x)        ((int)((x) >> 16) & 0xff)
72 #define LID_SAPIC_SET(id,eid)   (((id & 0xff) << 8 | (eid & 0xff)) << 16);
73 #define LID_SAPIC_MASK          0xffff0000UL
74
75 int     mp_ipi_test = 0;
76
77 /* Variables used by os_boot_rendez and ia64_ap_startup */
78 struct pcpu *ap_pcpu;
79 void *ap_stack;
80 uint64_t ap_vhpt;
81 volatile int ap_delay;
82 volatile int ap_awake;
83 volatile int ap_spin;
84
85 static void cpu_mp_unleash(void *);
86
87 void
88 ia64_ap_startup(void)
89 {
90         volatile struct ia64_interrupt_block *ib = IA64_INTERRUPT_BLOCK;
91         int vector;
92
93         pcpup = ap_pcpu;
94         ia64_set_k4((intptr_t)pcpup);
95
96         map_vhpt(ap_vhpt);
97         ia64_set_pta(ap_vhpt + (1 << 8) + (pmap_vhpt_log2size << 2) + 1);
98         ia64_srlz_i();
99
100         ap_awake = 1;
101         ap_delay = 0;
102
103         map_pal_code();
104         map_gateway_page();
105
106         ia64_set_fpsr(IA64_FPSR_DEFAULT);
107
108         /* Wait until it's time for us to be unleashed */
109         while (ap_spin)
110                 cpu_spinwait();
111
112         /* Initialize curthread. */
113         KASSERT(PCPU_GET(idlethread) != NULL, ("no idle thread"));
114         PCPU_SET(curthread, PCPU_GET(idlethread));
115
116         /*
117          * Get and save the CPU specific MCA records. Should we get the
118          * MCA state for each processor, or just the CMC state?
119          */
120         ia64_mca_save_state(SAL_INFO_MCA);
121         ia64_mca_save_state(SAL_INFO_CMC);
122
123         ap_awake++;
124         while (!smp_started)
125                 cpu_spinwait();
126
127         CTR1(KTR_SMP, "SMP: cpu%d launched", PCPU_GET(cpuid));
128
129         /* Acknowledge and EOI all interrupts. */
130         vector = ia64_get_ivr();
131         while (vector != 15) {
132                 ia64_srlz_d();
133                 if (vector == 0)
134                         vector = (int)ib->ib_inta;
135                 ia64_set_eoi(0);
136                 ia64_srlz_d();
137                 vector = ia64_get_ivr();
138         }
139         ia64_srlz_d();
140
141         /* kick off the clock on this AP */
142         pcpu_initclock();
143
144         ia64_set_tpr(0);
145         ia64_srlz_d();
146         enable_intr();
147
148         sched_throw(NULL);
149         /* NOTREACHED */
150 }
151
152 void
153 cpu_mp_setmaxid(void)
154 {
155
156         /*
157          * Count the number of processors in the system by walking the ACPI
158          * tables. Note that we record the actual number of processors, even
159          * if this is larger than MAXCPU. We only activate MAXCPU processors.
160          */
161         mp_ncpus = ia64_count_cpus();
162
163         /*
164          * Set the largest cpuid we're going to use. This is necessary for
165          * VM initialization.
166          */
167         mp_maxid = min(mp_ncpus, MAXCPU) - 1;
168 }
169
170 int
171 cpu_mp_probe(void)
172 {
173
174         /*
175          * If there's only 1 processor, or we don't have a wake-up vector,
176          * we're not going to enable SMP. Note that no wake-up vector can
177          * also mean that the wake-up mechanism is not supported. In this
178          * case we can have multiple processors, but we simply can't wake
179          * them up...
180          */
181         return (mp_ncpus > 1 && ipi_vector[IPI_AP_WAKEUP] != 0);
182 }
183
184 void
185 cpu_mp_add(u_int acpiid, u_int apicid, u_int apiceid)
186 {
187         struct pcpu *pc;
188         u_int64_t lid;
189
190         /* Ignore any processor numbers outside our range */
191         if (acpiid > mp_maxid)
192                 return;
193
194         KASSERT((all_cpus & (1UL << acpiid)) == 0,
195             ("%s: cpu%d already in CPU map", __func__, acpiid));
196
197         lid = LID_SAPIC_SET(apicid, apiceid);
198
199         if ((ia64_get_lid() & LID_SAPIC_MASK) == lid) {
200                 KASSERT(acpiid == 0,
201                     ("%s: the BSP must be cpu0", __func__));
202         }
203
204         if (acpiid != 0) {
205                 pc = (struct pcpu *)malloc(sizeof(*pc), M_PMAP, M_WAITOK);
206                 pcpu_init(pc, acpiid, sizeof(*pc));
207         } else
208                 pc = pcpup;
209
210         pc->pc_lid = lid;
211         all_cpus |= (1UL << acpiid);
212 }
213
214 void
215 cpu_mp_announce()
216 {
217         struct pcpu *pc;
218         int i;
219
220         for (i = 0; i <= mp_maxid; i++) {
221                 pc = pcpu_find(i);
222                 if (pc != NULL) {
223                         printf("cpu%d: SAPIC Id=%x, SAPIC Eid=%x", i,
224                             LID_SAPIC_ID(pc->pc_lid),
225                             LID_SAPIC_EID(pc->pc_lid));
226                         if (i == 0)
227                                 printf(" (BSP)\n");
228                         else
229                                 printf("\n");
230                 }
231         }
232 }
233
234 void
235 cpu_mp_start()
236 {
237         struct pcpu *pc;
238
239         ap_spin = 1;
240
241         SLIST_FOREACH(pc, &cpuhead, pc_allcpu) {
242                 pc->pc_current_pmap = kernel_pmap;
243                 pc->pc_other_cpus = all_cpus & ~pc->pc_cpumask;
244                 if (pc->pc_cpuid > 0) {
245                         ap_pcpu = pc;
246                         ap_stack = malloc(KSTACK_PAGES * PAGE_SIZE, M_PMAP,
247                             M_WAITOK);
248                         ap_vhpt = pmap_vhpt_base[pc->pc_cpuid];
249                         ap_delay = 2000;
250                         ap_awake = 0;
251
252                         if (bootverbose)
253                                 printf("SMP: waking up cpu%d\n", pc->pc_cpuid);
254
255                         ipi_send(pc, IPI_AP_WAKEUP);
256
257                         do {
258                                 DELAY(1000);
259                         } while (--ap_delay > 0);
260                         pc->pc_awake = ap_awake;
261
262                         if (!ap_awake)
263                                 printf("SMP: WARNING: cpu%d did not wake up\n",
264                                     pc->pc_cpuid);
265                 } else {
266                         pc->pc_awake = 1;
267                         ipi_self(IPI_TEST);
268                 }
269         }
270 }
271
272 static void
273 cpu_mp_unleash(void *dummy)
274 {
275         struct pcpu *pc;
276         int cpus;
277
278         if (mp_ncpus <= 1)
279                 return;
280
281         if (mp_ipi_test != 1)
282                 printf("SMP: WARNING: sending of a test IPI failed\n");
283
284         cpus = 0;
285         smp_cpus = 0;
286         SLIST_FOREACH(pc, &cpuhead, pc_allcpu) {
287                 cpus++;
288                 if (pc->pc_awake)
289                         smp_cpus++;
290         }
291
292         ap_awake = 1;
293         ap_spin = 0;
294
295         while (ap_awake != smp_cpus)
296                 cpu_spinwait();
297
298         if (smp_cpus != cpus || cpus != mp_ncpus) {
299                 printf("SMP: %d CPUs found; %d CPUs usable; %d CPUs woken\n",
300                     mp_ncpus, cpus, smp_cpus);
301         }
302
303         smp_active = 1;
304         smp_started = 1;
305 }
306
307 /*
308  * send an IPI to a set of cpus.
309  */
310 void
311 ipi_selected(cpumask_t cpus, int ipi)
312 {
313         struct pcpu *pc;
314
315         SLIST_FOREACH(pc, &cpuhead, pc_allcpu) {
316                 if (cpus & pc->pc_cpumask)
317                         ipi_send(pc, ipi);
318         }
319 }
320
321 /*
322  * send an IPI to all CPUs, including myself.
323  */
324 void
325 ipi_all(int ipi)
326 {
327         struct pcpu *pc;
328
329         SLIST_FOREACH(pc, &cpuhead, pc_allcpu) {
330                 ipi_send(pc, ipi);
331         }
332 }
333
334 /*
335  * send an IPI to all CPUs EXCEPT myself.
336  */
337 void
338 ipi_all_but_self(int ipi)
339 {
340         struct pcpu *pc;
341
342         SLIST_FOREACH(pc, &cpuhead, pc_allcpu) {
343                 if (pc != pcpup)
344                         ipi_send(pc, ipi);
345         }
346 }
347
348 /*
349  * send an IPI to myself.
350  */
351 void
352 ipi_self(int ipi)
353 {
354
355         ipi_send(pcpup, ipi);
356 }
357
358 /*
359  * Send an IPI to the specified processor. The lid parameter holds the
360  * cr.lid (CR64) contents of the target processor. Only the id and eid
361  * fields are used here.
362  */
363 void
364 ipi_send(struct pcpu *cpu, int ipi)
365 {
366         volatile uint64_t *pipi;
367         uint64_t vector;
368
369         pipi = __MEMIO_ADDR(ia64_lapic_address |
370             ((cpu->pc_lid & LID_SAPIC_MASK) >> 12));
371         vector = (uint64_t)(ipi_vector[ipi] & 0xff);
372         KASSERT(vector != 0, ("IPI %d is not assigned a vector", ipi));
373         *pipi = vector;
374         CTR3(KTR_SMP, "ipi_send(%p, %ld), cpuid=%d", pipi, vector,
375             PCPU_GET(cpuid));
376 }
377
378 SYSINIT(start_aps, SI_SUB_SMP, SI_ORDER_FIRST, cpu_mp_unleash, NULL);