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