1/* $NetBSD: cpu.c,v 1.94 2011/07/28 03:21:14 uebayasi Exp $ */
2
3/*-
4 * Copyright (c) 1998, 1999, 2000, 2001 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9 * NASA Ames Research Center.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33/*
34 * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University.
35 * All rights reserved.
36 *
37 * Author: Chris G. Demetriou
38 *
39 * Permission to use, copy, modify and distribute this software and
40 * its documentation is hereby granted, provided that both the copyright
41 * notice and this permission notice appear in all copies of the
42 * software, derivative works or modified versions, and any portions
43 * thereof, and that both notices appear in supporting documentation.
44 *
45 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
46 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
47 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
48 *
49 * Carnegie Mellon requests users of this software to return to
50 *
51 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
52 *  School of Computer Science
53 *  Carnegie Mellon University
54 *  Pittsburgh PA 15213-3890
55 *
56 * any improvements or extensions that they make and grant Carnegie the
57 * rights to redistribute these changes.
58 */
59
60#include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
61
62__KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.94 2011/07/28 03:21:14 uebayasi Exp $");
63
64#include "opt_ddb.h"
65#include "opt_multiprocessor.h"
66
67#include <sys/param.h>
68#include <sys/systm.h>
69#include <sys/device.h>
70#include <sys/kmem.h>
71#include <sys/proc.h>
72#include <sys/atomic.h>
73#include <sys/cpu.h>
74
75#include <uvm/uvm_extern.h>
76
77#include <machine/autoconf.h>
78#include <machine/cpuvar.h>
79#include <machine/rpb.h>
80#include <machine/prom.h>
81#include <machine/alpha.h>
82
83struct cpu_info cpu_info_primary = {
84	.ci_curlwp = &lwp0
85};
86struct cpu_info *cpu_info_list = &cpu_info_primary;
87
88#if defined(MULTIPROCESSOR)
89/*
90 * Array of CPU info structures.  Must be statically-allocated because
91 * curproc, etc. are used early.
92 */
93struct cpu_info *cpu_info[ALPHA_MAXPROCS];
94
95/* Bitmask of CPUs booted, currently running, and paused. */
96volatile u_long cpus_booted;
97volatile u_long cpus_running;
98volatile u_long cpus_paused;
99
100void	cpu_boot_secondary(struct cpu_info *);
101#endif /* MULTIPROCESSOR */
102
103/*
104 * The Implementation Version and the Architecture Mask must be
105 * consistent across all CPUs in the system, so we set it for the
106 * primary and announce the AMASK extensions if they exist.
107 *
108 * Note, we invert the AMASK so that if a bit is set, it means "has
109 * extension".
110 */
111u_long	cpu_implver, cpu_amask;
112
113/* Definition of the driver for autoconfig. */
114static int	cpumatch(device_t, cfdata_t, void *);
115static void	cpuattach(device_t, device_t, void *);
116
117CFATTACH_DECL_NEW(cpu, sizeof(struct cpu_softc),
118    cpumatch, cpuattach, NULL, NULL);
119
120static void	cpu_announce_extensions(struct cpu_info *);
121
122extern struct cfdriver cpu_cd;
123
124static const char * const lcaminor[] = {
125	"",
126	"21066", "21066",
127	"21068", "21068",
128	"21066A", "21068A", 0
129};
130
131const struct cputable_struct {
132	int	cpu_major_code;
133	const char *cpu_major_name;
134	const char * const *cpu_minor_names;
135} cpunametable[] = {
136	{ PCS_PROC_EV3,		"EV3",		NULL		},
137	{ PCS_PROC_EV4,		"21064",	NULL		},
138	{ PCS_PROC_SIMULATION,	"Sim",		NULL		},
139	{ PCS_PROC_LCA4,	"LCA",		lcaminor	},
140	{ PCS_PROC_EV5,		"21164",	NULL		},
141	{ PCS_PROC_EV45,	"21064A",	NULL		},
142	{ PCS_PROC_EV56,	"21164A",	NULL		},
143	{ PCS_PROC_EV6,		"21264",	NULL		},
144	{ PCS_PROC_PCA56,	"PCA56",	NULL		},
145	{ PCS_PROC_PCA57,	"PCA57",	NULL		},
146	{ PCS_PROC_EV67,	"21264A",	NULL		},
147	{ PCS_PROC_EV68CB,	"21264C",	NULL		},
148	{ PCS_PROC_EV68AL,	"21264B",	NULL		},
149	{ PCS_PROC_EV68CX,	"21264D",	NULL		},
150};
151
152/*
153 * The following is an attempt to map out how booting secondary CPUs
154 * works.
155 *
156 * As we find processors during the autoconfiguration sequence, all
157 * processors have idle stacks and PCBs created for them, including
158 * the primary (although the primary idles on lwp0's PCB until its
159 * idle PCB is created).
160 *
161 * Right before calling uvm_scheduler(), main() calls, on lwp0's
162 * context, cpu_boot_secondary_processors().  This is our key to
163 * actually spin up the additional processor's we've found.  We
164 * run through our cpu_info[] array looking for secondary processors
165 * with idle PCBs, and spin them up.
166 *
167 * The spinup involves switching the secondary processor to the
168 * OSF/1 PALcode, setting the entry point to cpu_spinup_trampoline(),
169 * and sending a "START" message to the secondary's console.
170 *
171 * Upon successful processor bootup, the cpu_spinup_trampoline will call
172 * cpu_hatch(), which will print a message indicating that the processor
173 * is running, and will set the "hatched" flag in its softc.  At the end
174 * of cpu_hatch() is a spin-forever loop; we do not yet attempt to schedule
175 * anything on secondary CPUs.
176 */
177
178static int
179cpumatch(device_t parent, cfdata_t cfdata, void *aux)
180{
181	struct mainbus_attach_args *ma = aux;
182
183	/* make sure that we're looking for a CPU. */
184	if (strcmp(ma->ma_name, cpu_cd.cd_name) != 0)
185		return (0);
186
187	/* XXX CHECK SLOT? */
188	/* XXX CHECK PRIMARY? */
189
190	return (1);
191}
192
193static void
194cpuattach(device_t parent, device_t self, void *aux)
195{
196	struct cpu_softc * const sc = device_private(self);
197	struct mainbus_attach_args *ma = aux;
198	int i;
199	const char * const *s;
200	struct pcs *p;
201	uint32_t major, minor;
202	struct cpu_info *ci;
203
204	sc->sc_dev = self;
205
206	p = LOCATE_PCS(hwrpb, ma->ma_slot);
207	major = PCS_CPU_MAJORTYPE(p);
208	minor = PCS_CPU_MINORTYPE(p);
209
210	aprint_normal(": ID %d%s, ", ma->ma_slot,
211	    ma->ma_slot == hwrpb->rpb_primary_cpu_id ? " (primary)" : "");
212
213	for(i = 0; i < __arraycount(cpunametable); ++i) {
214		if (cpunametable[i].cpu_major_code == major) {
215			aprint_normal("%s-%d",
216			    cpunametable[i].cpu_major_name, minor);
217			s = cpunametable[i].cpu_minor_names;
218			for(i = 0; s && s[i]; ++i) {
219				if (i == minor && strlen(s[i]) != 0) {
220					aprint_normal(" (%s)\n", s[i]);
221					goto recognized;
222				}
223			}
224			goto recognized;
225		}
226	}
227	aprint_error("UNKNOWN CPU TYPE (%d:%d)", major, minor);
228
229recognized:
230	aprint_naive("\n");
231	aprint_normal("\n");
232
233#ifdef DEBUG
234	if (p->pcs_proc_var != 0) {
235		bool needcomma = false;
236		const char *vaxfp = "";
237		const char *ieeefp = "";
238		const char *pe = "";
239
240		if (p->pcs_proc_var & PCS_VAR_VAXFP) {
241			vaxfp = "VAX FP support";
242			needcomma = true;
243		}
244		if (p->pcs_proc_var & PCS_VAR_IEEEFP) {
245			ieeefp = ", IEEE FP support";
246			if (!needcomma)
247				ieeefp += 2;
248			needcomma = true;
249		}
250		if (p->pcs_proc_var & PCS_VAR_PE) {
251			pe = ", Primary Eligible";
252			if (!needcomma)
253				pe += 2;
254			needcomma = true;
255		}
256		aprint_debug_dev(sc->sc_dev, "%s%s%s", vaxfp, ieeefp, pe);
257		if (p->pcs_proc_var & PCS_VAR_RESERVED)
258			aprint_debug("%sreserved bits: %#lx",
259			    needcomma ? ", " : "",
260			    p->pcs_proc_var & PCS_VAR_RESERVED);
261		aprint_debug("\n");
262	}
263#endif
264
265	if (ma->ma_slot > ALPHA_WHAMI_MAXID) {
266		if (ma->ma_slot == hwrpb->rpb_primary_cpu_id)
267			panic("cpu_attach: primary CPU ID too large");
268		aprint_error_dev(sc->sc_dev,
269		    "processor ID too large, ignoring\n");
270		return;
271	}
272
273	if (ma->ma_slot == hwrpb->rpb_primary_cpu_id)
274		ci = &cpu_info_primary;
275	else {
276		ci = kmem_zalloc(sizeof(*ci), KM_SLEEP);
277	}
278#if defined(MULTIPROCESSOR)
279	cpu_info[ma->ma_slot] = ci;
280#endif
281	ci->ci_cpuid = ma->ma_slot;
282	ci->ci_softc = sc;
283	ci->ci_pcc_freq = hwrpb->rpb_cc_freq;
284
285	/*
286	 * Though we could (should?) attach the LCA cpus' PCI
287	 * bus here there is no good reason to do so, and
288	 * the bus attachment code is easier to understand
289	 * and more compact if done the 'normal' way.
290	 */
291
292#if defined(MULTIPROCESSOR)
293	/*
294	 * Make sure the processor is available for use.
295	 */
296	if ((p->pcs_flags & PCS_PA) == 0) {
297		if (ma->ma_slot == hwrpb->rpb_primary_cpu_id)
298			panic("cpu_attach: primary not available?!");
299		aprint_normal_dev(sc->sc_dev,
300		    "processor not available for use\n");
301		return;
302	}
303
304	/* Make sure the processor has valid PALcode. */
305	if ((p->pcs_flags & PCS_PV) == 0) {
306		if (ma->ma_slot == hwrpb->rpb_primary_cpu_id)
307			panic("cpu_attach: primary has invalid PALcode?!");
308		aprint_error_dev(sc->sc_dev, "PALcode not valid\n");
309		return;
310	}
311#endif /* MULTIPROCESSOR */
312
313	/*
314	 * If we're the primary CPU, no more work to do; we're already
315	 * running!
316	 */
317	if (ma->ma_slot == hwrpb->rpb_primary_cpu_id) {
318		cpu_announce_extensions(ci);
319#if defined(MULTIPROCESSOR)
320		ci->ci_flags |= CPUF_PRIMARY|CPUF_RUNNING;
321		atomic_or_ulong(&cpus_booted, (1UL << ma->ma_slot));
322		atomic_or_ulong(&cpus_running, (1UL << ma->ma_slot));
323#endif /* MULTIPROCESSOR */
324	} else {
325#if defined(MULTIPROCESSOR)
326		int error;
327
328		error = mi_cpu_attach(ci);
329		if (error != 0) {
330			aprint_error_dev(sc->sc_dev,
331			    "mi_cpu_attach failed with %d\n", error);
332			return;
333		}
334
335		/*
336		 * Boot the secondary processor.  It will announce its
337		 * extensions, and then spin until we tell it to go
338		 * on its merry way.
339		 */
340		cpu_boot_secondary(ci);
341
342		/*
343		 * Link the processor into the list.
344		 */
345		ci->ci_next = cpu_info_list->ci_next;
346		cpu_info_list->ci_next = ci;
347#else /* ! MULTIPROCESSOR */
348		aprint_normal_dev(sc->sc_dev, "processor off-line; "
349		    "multiprocessor support not present in kernel\n");
350#endif /* MULTIPROCESSOR */
351	}
352
353	evcnt_attach_dynamic(&sc->sc_evcnt_clock, EVCNT_TYPE_INTR,
354	    NULL, device_xname(sc->sc_dev), "clock");
355	evcnt_attach_dynamic(&sc->sc_evcnt_device, EVCNT_TYPE_INTR,
356	    NULL, device_xname(sc->sc_dev), "device");
357#if defined(MULTIPROCESSOR)
358	alpha_ipi_init(ci);
359#endif
360}
361
362static void
363cpu_announce_extensions(struct cpu_info *ci)
364{
365	u_long implver, amask = 0;
366	char bits[64];
367
368	implver = alpha_implver();
369	if (implver >= ALPHA_IMPLVER_EV5)
370		amask = (~alpha_amask(ALPHA_AMASK_ALL)) & ALPHA_AMASK_ALL;
371
372	if (ci->ci_cpuid == hwrpb->rpb_primary_cpu_id) {
373		cpu_implver = implver;
374		cpu_amask = amask;
375	} else {
376		if (implver < cpu_implver)
377			aprint_error_dev(ci->ci_softc->sc_dev,
378			    "WARNING: IMPLVER %lu < %lu\n",
379			    implver, cpu_implver);
380
381		/*
382		 * Cap the system architecture mask to the intersection
383		 * of features supported by all processors in the system.
384		 */
385		cpu_amask &= amask;
386	}
387
388	if (amask) {
389		snprintb(bits, sizeof(bits),
390		    ALPHA_AMASK_BITS, cpu_amask);
391		aprint_normal_dev(ci->ci_softc->sc_dev,
392		    "Architecture extensions: %s\n", bits);
393	}
394}
395
396#if defined(MULTIPROCESSOR)
397void
398cpu_boot_secondary_processors(void)
399{
400	struct cpu_info *ci;
401	u_long i;
402	bool did_patch = false;
403
404	for (i = 0; i < ALPHA_MAXPROCS; i++) {
405		ci = cpu_info[i];
406		if (ci == NULL || ci->ci_data.cpu_idlelwp == NULL)
407			continue;
408		if (ci->ci_flags & CPUF_PRIMARY)
409			continue;
410		if ((cpus_booted & (1UL << i)) == 0)
411			continue;
412
413		/* Patch MP-criticial kernel routines. */
414		if (did_patch == false) {
415			alpha_patch(true);
416			did_patch = true;
417		}
418
419		/*
420		 * Launch the processor.
421		 */
422		atomic_or_ulong(&ci->ci_flags, CPUF_RUNNING);
423		atomic_or_ulong(&cpus_running, (1U << i));
424	}
425}
426
427void
428cpu_boot_secondary(struct cpu_info *ci)
429{
430	long timeout;
431	struct pcs *pcsp, *primary_pcsp;
432	struct pcb *pcb;
433	u_long cpumask;
434
435	pcb = lwp_getpcb(ci->ci_data.cpu_idlelwp);
436	primary_pcsp = LOCATE_PCS(hwrpb, hwrpb->rpb_primary_cpu_id);
437	pcsp = LOCATE_PCS(hwrpb, ci->ci_cpuid);
438	cpumask = (1UL << ci->ci_cpuid);
439
440	/*
441	 * Set up the PCS's HWPCB to match ours.
442	 */
443	memcpy(pcsp->pcs_hwpcb, &pcb->pcb_hw, sizeof(pcb->pcb_hw));
444
445	/*
446	 * Set up the HWRPB to restart the secondary processor
447	 * with our spin-up trampoline.
448	 */
449	hwrpb->rpb_restart = (uint64_t) cpu_spinup_trampoline;
450	hwrpb->rpb_restart_val = (uint64_t) ci;
451	hwrpb->rpb_checksum = hwrpb_checksum();
452
453	/*
454	 * Configure the CPU to start in OSF/1 PALcode by copying
455	 * the primary CPU's PALcode revision info to the secondary
456	 * CPUs PCS.
457	 */
458	memcpy(&pcsp->pcs_pal_rev, &primary_pcsp->pcs_pal_rev,
459	    sizeof(pcsp->pcs_pal_rev));
460	pcsp->pcs_flags |= (PCS_CV|PCS_RC);
461	pcsp->pcs_flags &= ~PCS_BIP;
462
463	/* Make sure the secondary console sees all this. */
464	alpha_mb();
465
466	/* Send a "START" command to the secondary CPU's console. */
467	if (cpu_iccb_send(ci->ci_cpuid, "START\r\n")) {
468		aprint_error_dev(ci->ci_softc->sc_dev,
469		    "unable to issue `START' command\n");
470		return;
471	}
472
473	/* Wait for the processor to boot. */
474	for (timeout = 10000; timeout != 0; timeout--) {
475		alpha_mb();
476		if (pcsp->pcs_flags & PCS_BIP)
477			break;
478		delay(1000);
479	}
480	if (timeout == 0)
481		aprint_error_dev(ci->ci_softc->sc_dev,
482		    "processor failed to boot\n");
483
484	/*
485	 * ...and now wait for verification that it's running kernel
486	 * code.
487	 */
488	for (timeout = 10000; timeout != 0; timeout--) {
489		alpha_mb();
490		if (cpus_booted & cpumask)
491			break;
492		delay(1000);
493	}
494	if (timeout == 0)
495		aprint_error_dev(ci->ci_softc->sc_dev,
496		    "processor failed to hatch\n");
497}
498
499void
500cpu_pause_resume(u_long cpu_id, int pause)
501{
502	u_long cpu_mask = (1UL << cpu_id);
503
504	if (pause) {
505		atomic_or_ulong(&cpus_paused, cpu_mask);
506		alpha_send_ipi(cpu_id, ALPHA_IPI_PAUSE);
507	} else
508		atomic_and_ulong(&cpus_paused, ~cpu_mask);
509}
510
511void
512cpu_pause_resume_all(int pause)
513{
514	struct cpu_info *ci, *self = curcpu();
515	CPU_INFO_ITERATOR cii;
516
517	for (CPU_INFO_FOREACH(cii, ci)) {
518		if (ci == self)
519			continue;
520		cpu_pause_resume(ci->ci_cpuid, pause);
521	}
522}
523
524void
525cpu_halt(void)
526{
527	struct cpu_info *ci = curcpu();
528	u_long cpu_id = cpu_number();
529	struct pcs *pcsp = LOCATE_PCS(hwrpb, cpu_id);
530
531	aprint_normal_dev(ci->ci_softc->sc_dev, "shutting down...\n");
532
533	pcsp->pcs_flags &= ~(PCS_RC | PCS_HALT_REQ);
534	pcsp->pcs_flags |= PCS_HALT_STAY_HALTED;
535
536	atomic_and_ulong(&cpus_running, ~(1UL << cpu_id));
537	atomic_and_ulong(&cpus_booted, ~(1U << cpu_id));
538
539	alpha_pal_halt();
540	/* NOTREACHED */
541}
542
543void
544cpu_hatch(struct cpu_info *ci)
545{
546	u_long cpu_id = cpu_number();
547	u_long cpumask = (1UL << cpu_id);
548
549	/* Mark the kernel pmap active on this processor. */
550	atomic_or_ulong(&pmap_kernel()->pm_cpus, cpumask);
551
552	/* Initialize trap vectors for this processor. */
553	trap_init();
554
555	/* Yahoo!  We're running kernel code!  Announce it! */
556	cpu_announce_extensions(ci);
557
558	atomic_or_ulong(&cpus_booted, cpumask);
559
560	/*
561	 * Spin here until we're told we can start.
562	 */
563	while ((cpus_running & cpumask) == 0)
564		/* spin */ ;
565
566	/*
567	 * Invalidate the TLB and sync the I-stream before we
568	 * jump into the kernel proper.  We have to do this
569	 * beacause we haven't been getting IPIs while we've
570	 * been spinning.
571	 */
572	ALPHA_TBIA();
573	alpha_pal_imb();
574
575	cc_calibrate_cpu(ci);
576}
577
578int
579cpu_iccb_send(long cpu_id, const char *msg)
580{
581	struct pcs *pcsp = LOCATE_PCS(hwrpb, cpu_id);
582	int timeout;
583	u_long cpumask = (1UL << cpu_id);
584
585	/* Wait for the ICCB to become available. */
586	for (timeout = 10000; timeout != 0; timeout--) {
587		alpha_mb();
588		if ((hwrpb->rpb_rxrdy & cpumask) == 0)
589			break;
590		delay(1000);
591	}
592	if (timeout == 0)
593		return (EIO);
594
595	/*
596	 * Copy the message into the ICCB, and tell the secondary console
597	 * that it's there.
598	 */
599	strcpy(pcsp->pcs_iccb.iccb_rxbuf, msg);
600	pcsp->pcs_iccb.iccb_rxlen = strlen(msg);
601	atomic_or_ulong(&hwrpb->rpb_rxrdy, cpumask);
602	membar_sync();
603
604	/* Wait for the message to be received. */
605	for (timeout = 10000; timeout != 0; timeout--) {
606		alpha_mb();
607		if ((hwrpb->rpb_rxrdy & cpumask) == 0)
608			break;
609		delay(1000);
610	}
611	if (timeout == 0)
612		return (EIO);
613
614	return (0);
615}
616
617void
618cpu_iccb_receive(void)
619{
620#if 0	/* Don't bother... we don't get any important messages anyhow. */
621	uint64_t txrdy;
622	char *cp1, *cp2, buf[80];
623	struct pcs *pcsp;
624	u_int cnt;
625	long cpu_id;
626
627	txrdy = hwrpb->rpb_txrdy;
628
629	for (cpu_id = 0; cpu_id < hwrpb->rpb_pcs_cnt; cpu_id++) {
630		if (txrdy & (1UL << cpu_id)) {
631			pcsp = LOCATE_PCS(hwrpb, cpu_id);
632			printf("Inter-console message from CPU %lu "
633			    "HALT REASON = 0x%lx, FLAGS = 0x%lx\n",
634			    cpu_id, pcsp->pcs_halt_reason, pcsp->pcs_flags);
635
636			cnt = pcsp->pcs_iccb.iccb_txlen;
637			if (cnt >= 80) {
638				printf("Malformed inter-console message\n");
639				continue;
640			}
641			cp1 = pcsp->pcs_iccb.iccb_txbuf;
642			cp2 = buf;
643			while (cnt--) {
644				if (*cp1 != '\r' && *cp1 != '\n')
645					*cp2++ = *cp1;
646				cp1++;
647			}
648			*cp2 = '\0';
649			printf("Message from CPU %lu: %s\n", cpu_id, buf);
650		}
651	}
652#endif /* 0 */
653	hwrpb->rpb_txrdy = 0;
654	alpha_mb();
655}
656
657#if defined(DDB)
658
659#include <ddb/db_output.h>
660#include <machine/db_machdep.h>
661
662/*
663 * Dump CPU information from DDB.
664 */
665void
666cpu_debug_dump(void)
667{
668	struct cpu_info *ci;
669	CPU_INFO_ITERATOR cii;
670
671	db_printf("addr		dev	id	flags	ipis	curproc\n");
672	for (CPU_INFO_FOREACH(cii, ci)) {
673		db_printf("%p	%s	%lu	%lx	%lx	%p\n",
674		    ci,
675		    device_xname(ci->ci_softc->sc_dev),
676		    ci->ci_cpuid,
677		    ci->ci_flags,
678		    ci->ci_ipis,
679		    ci->ci_curlwp);
680	}
681}
682
683#endif /* DDB */
684
685#endif /* MULTIPROCESSOR */
686