]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - sys/ia64/ia64/mp_machdep.c
MFC rev 201269, 201373:
[FreeBSD/stable/8.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/kthread.h>
39 #include <sys/lock.h>
40 #include <sys/malloc.h>
41 #include <sys/mutex.h>
42 #include <sys/kernel.h>
43 #include <sys/pcpu.h>
44 #include <sys/sched.h>
45 #include <sys/smp.h>
46 #include <sys/sysctl.h>
47 #include <sys/uuid.h>
48
49 #include <vm/vm.h>
50 #include <vm/pmap.h>
51 #include <vm/vm_extern.h>
52 #include <vm/vm_kern.h>
53
54 #include <machine/atomic.h>
55 #include <machine/cpu.h>
56 #include <machine/fpu.h>
57 #include <machine/intr.h>
58 #include <machine/mca.h>
59 #include <machine/md_var.h>
60 #include <machine/pal.h>
61 #include <machine/pcb.h>
62 #include <machine/pmap.h>
63 #include <machine/sal.h>
64 #include <machine/smp.h>
65 #include <i386/include/specialreg.h>
66
67 MALLOC_DEFINE(M_SMP, "SMP", "SMP related allocations");
68
69 void ia64_ap_startup(void);
70
71 #define LID_SAPIC_ID(x)         ((int)((x) >> 24) & 0xff)
72 #define LID_SAPIC_EID(x)        ((int)((x) >> 16) & 0xff)
73 #define LID_SAPIC_SET(id,eid)   (((id & 0xff) << 8 | (eid & 0xff)) << 16);
74 #define LID_SAPIC_MASK          0xffff0000UL
75
76 /* Variables used by os_boot_rendez and ia64_ap_startup */
77 struct pcpu *ap_pcpu;
78 void *ap_stack;
79 volatile int ap_delay;
80 volatile int ap_awake;
81 volatile int ap_spin;
82
83 static void cpu_mp_unleash(void *);
84
85 struct cpu_group *
86 cpu_topo(void)
87 {
88
89         return smp_topo_none();
90 }
91
92 static void
93 ia64_store_mca_state(void* arg)
94 {
95         unsigned int ncpu = (unsigned int)(uintptr_t)arg;
96         struct thread* td;
97
98         /* ia64_mca_save_state() is CPU-sensitive, so bind ourself to our target CPU */
99         td = curthread;
100         thread_lock(td);
101         sched_bind(td, ncpu);
102         thread_unlock(td);
103
104         /*
105          * Get and save the CPU specific MCA records. Should we get the
106          * MCA state for each processor, or just the CMC state?
107          */
108         ia64_mca_save_state(SAL_INFO_MCA);
109         ia64_mca_save_state(SAL_INFO_CMC);
110
111         kproc_exit(0);
112 }
113
114 void
115 ia64_ap_startup(void)
116 {
117         volatile struct ia64_interrupt_block *ib = IA64_INTERRUPT_BLOCK;
118         uint64_t vhpt;
119         int vector;
120
121         pcpup = ap_pcpu;
122         ia64_set_k4((intptr_t)pcpup);
123
124         vhpt = PCPU_GET(md.vhpt);
125         map_vhpt(vhpt);
126         ia64_set_pta(vhpt + (1 << 8) + (pmap_vhpt_log2size << 2) + 1);
127         ia64_srlz_i();
128
129         ap_awake = 1;
130         ap_delay = 0;
131
132         map_pal_code();
133         map_gateway_page();
134
135         ia64_set_fpsr(IA64_FPSR_DEFAULT);
136
137         /* Wait until it's time for us to be unleashed */
138         while (ap_spin)
139                 cpu_spinwait();
140
141         /* Initialize curthread. */
142         KASSERT(PCPU_GET(idlethread) != NULL, ("no idle thread"));
143         PCPU_SET(curthread, PCPU_GET(idlethread));
144
145         atomic_add_int(&ap_awake, 1);
146         while (!smp_started)
147                 cpu_spinwait();
148
149         CTR1(KTR_SMP, "SMP: cpu%d launched", PCPU_GET(cpuid));
150
151         /* Acknowledge and EOI all interrupts. */
152         vector = ia64_get_ivr();
153         while (vector != 15) {
154                 ia64_srlz_d();
155                 if (vector == 0)
156                         vector = (int)ib->ib_inta;
157                 ia64_set_eoi(0);
158                 ia64_srlz_d();
159                 vector = ia64_get_ivr();
160         }
161         ia64_srlz_d();
162
163         /* kick off the clock on this AP */
164         pcpu_initclock();
165
166         ia64_set_tpr(0);
167         ia64_srlz_d();
168         enable_intr();
169
170         sched_throw(NULL);
171         /* NOTREACHED */
172 }
173
174 void
175 cpu_mp_setmaxid(void)
176 {
177
178         /*
179          * Count the number of processors in the system by walking the ACPI
180          * tables. Note that we record the actual number of processors, even
181          * if this is larger than MAXCPU. We only activate MAXCPU processors.
182          */
183         mp_ncpus = ia64_count_cpus();
184
185         /*
186          * Set the largest cpuid we're going to use. This is necessary for
187          * VM initialization.
188          */
189         mp_maxid = min(mp_ncpus, MAXCPU) - 1;
190 }
191
192 int
193 cpu_mp_probe(void)
194 {
195
196         /*
197          * If there's only 1 processor, or we don't have a wake-up vector,
198          * we're not going to enable SMP. Note that no wake-up vector can
199          * also mean that the wake-up mechanism is not supported. In this
200          * case we can have multiple processors, but we simply can't wake
201          * them up...
202          */
203         return (mp_ncpus > 1 && ipi_vector[IPI_AP_WAKEUP] != 0);
204 }
205
206 void
207 cpu_mp_add(u_int acpiid, u_int apicid, u_int apiceid)
208 {
209         struct pcpu *pc;
210         u_int64_t lid;
211         void *dpcpu;
212         u_int cpuid;
213
214         lid = LID_SAPIC_SET(apicid, apiceid);
215         cpuid = ((ia64_get_lid() & LID_SAPIC_MASK) == lid) ? 0 : smp_cpus++;
216
217         KASSERT((all_cpus & (1UL << cpuid)) == 0,
218             ("%s: cpu%d already in CPU map", __func__, acpiid));
219
220         if (cpuid != 0) {
221                 pc = (struct pcpu *)malloc(sizeof(*pc), M_SMP, M_WAITOK);
222                 pcpu_init(pc, cpuid, sizeof(*pc));
223                 dpcpu = (void *)kmem_alloc(kernel_map, DPCPU_SIZE);
224                 dpcpu_init(dpcpu, cpuid);
225         } else
226                 pc = pcpup;
227
228         pc->pc_acpi_id = acpiid;
229         pc->pc_md.lid = lid;
230         all_cpus |= (1UL << cpuid);
231 }
232
233 void
234 cpu_mp_announce()
235 {
236         struct pcpu *pc;
237         int i;
238
239         for (i = 0; i <= mp_maxid; i++) {
240                 pc = pcpu_find(i);
241                 if (pc != NULL) {
242                         printf("cpu%d: ACPI Id=%x, SAPIC Id=%x, SAPIC Eid=%x",
243                             i, pc->pc_acpi_id, LID_SAPIC_ID(pc->pc_md.lid),
244                             LID_SAPIC_EID(pc->pc_md.lid));
245                         if (i == 0)
246                                 printf(" (BSP)\n");
247                         else
248                                 printf("\n");
249                 }
250         }
251 }
252
253 void
254 cpu_mp_start()
255 {
256         struct pcpu *pc;
257
258         ap_spin = 1;
259
260         SLIST_FOREACH(pc, &cpuhead, pc_allcpu) {
261                 pc->pc_md.current_pmap = kernel_pmap;
262                 pc->pc_other_cpus = all_cpus & ~pc->pc_cpumask;
263                 if (pc->pc_cpuid > 0) {
264                         ap_pcpu = pc;
265                         pc->pc_md.vhpt = pmap_alloc_vhpt();
266                         if (pc->pc_md.vhpt == 0) {
267                                 printf("SMP: WARNING: unable to allocate VHPT"
268                                     " for cpu%d", pc->pc_cpuid);
269                                 continue;
270                         }
271                         ap_stack = malloc(KSTACK_PAGES * PAGE_SIZE, M_SMP,
272                             M_WAITOK);
273                         ap_delay = 2000;
274                         ap_awake = 0;
275
276                         if (bootverbose)
277                                 printf("SMP: waking up cpu%d\n", pc->pc_cpuid);
278
279                         ipi_send(pc, IPI_AP_WAKEUP);
280
281                         do {
282                                 DELAY(1000);
283                         } while (--ap_delay > 0);
284                         pc->pc_md.awake = ap_awake;
285
286                         if (!ap_awake)
287                                 printf("SMP: WARNING: cpu%d did not wake up\n",
288                                     pc->pc_cpuid);
289                 } else
290                         pc->pc_md.awake = 1;
291         }
292 }
293
294 static void
295 cpu_mp_unleash(void *dummy)
296 {
297         struct pcpu *pc;
298         int cpus;
299
300         if (mp_ncpus <= 1)
301                 return;
302
303         cpus = 0;
304         smp_cpus = 0;
305         SLIST_FOREACH(pc, &cpuhead, pc_allcpu) {
306                 cpus++;
307                 if (pc->pc_md.awake) {
308                         kproc_create(ia64_store_mca_state,
309                             (void*)((uintptr_t)pc->pc_cpuid), NULL, 0, 0,
310                             "mca %u", pc->pc_cpuid);
311                         smp_cpus++;
312                 }
313         }
314
315         ap_awake = 1;
316         ap_spin = 0;
317
318         while (ap_awake != smp_cpus)
319                 cpu_spinwait();
320
321         if (smp_cpus != cpus || cpus != mp_ncpus) {
322                 printf("SMP: %d CPUs found; %d CPUs usable; %d CPUs woken\n",
323                     mp_ncpus, cpus, smp_cpus);
324         }
325
326         smp_active = 1;
327         smp_started = 1;
328 }
329
330 /*
331  * send an IPI to a set of cpus.
332  */
333 void
334 ipi_selected(cpumask_t cpus, int ipi)
335 {
336         struct pcpu *pc;
337
338         SLIST_FOREACH(pc, &cpuhead, pc_allcpu) {
339                 if (cpus & pc->pc_cpumask)
340                         ipi_send(pc, ipi);
341         }
342 }
343
344 /*
345  * send an IPI to all CPUs EXCEPT myself.
346  */
347 void
348 ipi_all_but_self(int ipi)
349 {
350         struct pcpu *pc;
351
352         SLIST_FOREACH(pc, &cpuhead, pc_allcpu) {
353                 if (pc != pcpup)
354                         ipi_send(pc, ipi);
355         }
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 = (void *)IA64_PHYS_TO_RR6(ia64_lapic_address |
370             ((cpu->pc_md.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);