]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - 6/sys/ia64/ia64/mp_machdep.c
Clone Kip's Xen on stable/6 tree so that I can work on improving FreeBSD/amd64
[FreeBSD/FreeBSD.git] / 6 / 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/lock.h>
38 #include <sys/malloc.h>
39 #include <sys/mutex.h>
40 #include <sys/kernel.h>
41 #include <sys/pcpu.h>
42 #include <sys/smp.h>
43 #include <sys/sysctl.h>
44 #include <sys/uuid.h>
45
46 #include <vm/vm.h>
47 #include <vm/pmap.h>
48 #include <vm/vm_extern.h>
49 #include <vm/vm_kern.h>
50
51 #include <machine/atomic.h>
52 #include <machine/clock.h>
53 #include <machine/fpu.h>
54 #include <machine/mca.h>
55 #include <machine/md_var.h>
56 #include <machine/pal.h>
57 #include <machine/pcb.h>
58 #include <machine/pmap.h>
59 #include <machine/sal.h>
60 #include <machine/smp.h>
61 #include <i386/include/specialreg.h>
62
63 MALLOC_DECLARE(M_PMAP);
64
65 void ia64_ap_startup(void);
66
67 extern uint64_t ia64_lapic_address;
68
69 #define LID_SAPIC_ID(x)         ((int)((x) >> 24) & 0xff)
70 #define LID_SAPIC_EID(x)        ((int)((x) >> 16) & 0xff)
71 #define LID_SAPIC_SET(id,eid)   (((id & 0xff) << 8 | (eid & 0xff)) << 16);
72 #define LID_SAPIC_MASK          0xffff0000UL
73
74 int     mp_ipi_test = 0;
75
76 /* Variables used by os_boot_rendez and ia64_ap_startup */
77 struct pcpu *ap_pcpu;
78 void *ap_stack;
79 uint64_t ap_vhpt;
80 volatile int ap_delay;
81 volatile int ap_awake;
82 volatile int ap_spin;
83
84 static void cpu_mp_unleash(void *);
85
86 void
87 ia64_ap_startup(void)
88 {
89
90         pcpup = ap_pcpu;
91         ia64_set_k4((intptr_t)pcpup);
92
93         __asm __volatile("mov cr.pta=%0;; srlz.i;;" ::
94             "r" (ap_vhpt + (1<<8) + (pmap_vhpt_log2size<<2) + 1));
95
96         ap_awake = 1;
97         ap_delay = 0;
98
99         map_pal_code();
100         map_gateway_page();
101
102         ia64_set_fpsr(IA64_FPSR_DEFAULT);
103
104         /* Wait until it's time for us to be unleashed */
105         while (ap_spin)
106                 DELAY(0);
107
108         __asm __volatile("ssm psr.i;; srlz.d;;");
109
110         /* Initialize curthread. */
111         KASSERT(PCPU_GET(idlethread) != NULL, ("no idle thread"));
112         PCPU_SET(curthread, PCPU_GET(idlethread));
113
114         /*
115          * Correct spinlock nesting.  The idle thread context that we are
116          * borrowing was created so that it would start out with a single
117          * spin lock (sched_lock) held in fork_trampoline().  Since we
118          * don't have any locks and explicitly acquire locks when we need
119          * to, the nesting count will be off by 1.
120          */
121         curthread->td_md.md_spinlock_count = 0;
122         critical_exit();
123
124         /*
125          * Get and save the CPU specific MCA records. Should we get the
126          * MCA state for each processor, or just the CMC state?
127          */
128         ia64_mca_save_state(SAL_INFO_MCA);
129         ia64_mca_save_state(SAL_INFO_CMC);
130
131         ap_awake++;
132         while (!smp_started)
133                 DELAY(0);
134
135         CTR1(KTR_SMP, "SMP: cpu%d launched", PCPU_GET(cpuid));
136
137         mtx_lock_spin(&sched_lock);
138
139         binuptime(PCPU_PTR(switchtime));
140         PCPU_SET(switchticks, ticks);
141
142         ia64_set_tpr(0);
143
144         /* kick off the clock on this AP */
145         pcpu_initclock();
146
147         cpu_throw(NULL, choosethread());
148         /* NOTREACHED */
149 }
150
151 void
152 cpu_mp_setmaxid(void)
153 {
154
155         /*
156          * Count the number of processors in the system by walking the ACPI
157          * tables. Note that we record the actual number of processors, even
158          * if this is larger than MAXCPU. We only activate MAXCPU processors.
159          */
160         mp_ncpus = ia64_count_cpus();
161
162         /*
163          * Set the largest cpuid we're going to use. This is necessary for
164          * VM initialization.
165          */
166         mp_maxid = min(mp_ncpus, MAXCPU) - 1;
167 }
168
169 int
170 cpu_mp_probe(void)
171 {
172
173         /*
174          * If there's only 1 processor, or we don't have a wake-up vector,
175          * we're not going to enable SMP. Note that no wake-up vector can
176          * also mean that the wake-up mechanism is not supported. In this
177          * case we can have multiple processors, but we simply can't wake
178          * them up...
179          */
180         return (mp_ncpus > 1 && ipi_vector[IPI_AP_WAKEUP] != 0);
181 }
182
183 void
184 cpu_mp_add(u_int acpiid, u_int apicid, u_int apiceid)
185 {
186         struct pcpu *pc;
187         u_int64_t lid;
188
189         /* Ignore any processor numbers outside our range */
190         if (acpiid > mp_maxid)
191                 return;
192
193         KASSERT((all_cpus & (1UL << acpiid)) == 0,
194             ("%s: cpu%d already in CPU map", __func__, acpiid));
195
196         lid = LID_SAPIC_SET(apicid, apiceid);
197
198         if ((ia64_get_lid() & LID_SAPIC_MASK) == lid) {
199                 KASSERT(acpiid == 0,
200                     ("%s: the BSP must be cpu0", __func__));
201         }
202
203         if (acpiid != 0) {
204                 pc = (struct pcpu *)kmem_alloc(kernel_map, PAGE_SIZE);
205                 pcpu_init(pc, acpiid, PAGE_SIZE);
206         } else
207                 pc = pcpup;
208
209         pc->pc_lid = lid;
210         all_cpus |= (1UL << acpiid);
211 }
212
213 void
214 cpu_mp_announce()
215 {
216         struct pcpu *pc;
217         int i;
218
219         for (i = 0; i <= mp_maxid; i++) {
220                 pc = pcpu_find(i);
221                 if (pc != NULL) {
222                         printf("cpu%d: SAPIC Id=%x, SAPIC Eid=%x", i,
223                             LID_SAPIC_ID(pc->pc_lid),
224                             LID_SAPIC_EID(pc->pc_lid));
225                         if (i == 0)
226                                 printf(" (BSP)\n");
227                         else
228                                 printf("\n");
229                 }
230         }
231 }
232
233 void
234 cpu_mp_start()
235 {
236         struct pcpu *pc;
237
238         ap_spin = 1;
239
240         SLIST_FOREACH(pc, &cpuhead, pc_allcpu) {
241                 pc->pc_current_pmap = kernel_pmap;
242                 pc->pc_other_cpus = all_cpus & ~pc->pc_cpumask;
243                 if (pc->pc_cpuid > 0) {
244                         ap_pcpu = pc;
245                         ap_stack = malloc(KSTACK_PAGES * PAGE_SIZE, M_PMAP,
246                             M_WAITOK);
247                         ap_vhpt = pmap_vhpt_base[pc->pc_cpuid];
248                         ap_delay = 2000;
249                         ap_awake = 0;
250
251                         if (bootverbose)
252                                 printf("SMP: waking up cpu%d\n", pc->pc_cpuid);
253
254                         ipi_send(pc, IPI_AP_WAKEUP);
255
256                         do {
257                                 DELAY(1000);
258                         } while (--ap_delay > 0);
259                         pc->pc_awake = ap_awake;
260
261                         if (!ap_awake)
262                                 printf("SMP: WARNING: cpu%d did not wake up\n",
263                                     pc->pc_cpuid);
264                 } else {
265                         pc->pc_awake = 1;
266                         ipi_self(IPI_TEST);
267                 }
268         }
269 }
270
271 static void
272 cpu_mp_unleash(void *dummy)
273 {
274         struct pcpu *pc;
275         int cpus;
276
277         if (mp_ncpus <= 1)
278                 return;
279
280         if (mp_ipi_test != 1)
281                 printf("SMP: WARNING: sending of a test IPI failed\n");
282
283         cpus = 0;
284         smp_cpus = 0;
285         SLIST_FOREACH(pc, &cpuhead, pc_allcpu) {
286                 cpus++;
287                 if (pc->pc_awake)
288                         smp_cpus++;
289         }
290
291         ap_awake = 1;
292         ap_spin = 0;
293
294         while (ap_awake != smp_cpus)
295                 DELAY(0);
296
297         if (smp_cpus != cpus || cpus != mp_ncpus) {
298                 printf("SMP: %d CPUs found; %d CPUs usable; %d CPUs woken\n",
299                     mp_ncpus, cpus, smp_cpus);
300         }
301
302         smp_active = 1;
303         smp_started = 1;
304 }
305
306 /*
307  * send an IPI to a set of cpus.
308  */
309 void
310 ipi_selected(cpumask_t cpus, int ipi)
311 {
312         struct pcpu *pc;
313
314         SLIST_FOREACH(pc, &cpuhead, pc_allcpu) {
315                 if (cpus & pc->pc_cpumask)
316                         ipi_send(pc, ipi);
317         }
318 }
319
320 /*
321  * send an IPI to all CPUs, including myself.
322  */
323 void
324 ipi_all(int ipi)
325 {
326         struct pcpu *pc;
327
328         SLIST_FOREACH(pc, &cpuhead, pc_allcpu) {
329                 ipi_send(pc, ipi);
330         }
331 }
332
333 /*
334  * send an IPI to all CPUs EXCEPT myself.
335  */
336 void
337 ipi_all_but_self(int ipi)
338 {
339         struct pcpu *pc;
340
341         SLIST_FOREACH(pc, &cpuhead, pc_allcpu) {
342                 if (pc != pcpup)
343                         ipi_send(pc, ipi);
344         }
345 }
346
347 /*
348  * send an IPI to myself.
349  */
350 void
351 ipi_self(int ipi)
352 {
353
354         ipi_send(pcpup, ipi);
355 }
356
357 /*
358  * Send an IPI to the specified processor. The lid parameter holds the
359  * cr.lid (CR64) contents of the target processor. Only the id and eid
360  * fields are used here.
361  */
362 void
363 ipi_send(struct pcpu *cpu, int ipi)
364 {
365         volatile uint64_t *pipi;
366         uint64_t vector;
367
368         pipi = __MEMIO_ADDR(ia64_lapic_address |
369             ((cpu->pc_lid & LID_SAPIC_MASK) >> 12));
370         vector = (uint64_t)(ipi_vector[ipi] & 0xff);
371         KASSERT(vector != 0, ("IPI %d is not assigned a vector", ipi));
372         *pipi = vector;
373         CTR3(KTR_SMP, "ipi_send(%p, %ld), cpuid=%d", pipi, vector,
374             PCPU_GET(cpuid));
375 }
376
377 SYSINIT(start_aps, SI_SUB_SMP, SI_ORDER_FIRST, cpu_mp_unleash, NULL);