1 /* $NetBSD: cpu.c,v 1.90 2010/12/17 02:36:35 joerg 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.90 2010/12/17 02:36:35 joerg 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/malloc.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 83 struct cpu_info cpu_info_primary = { 84 .ci_curlwp = &lwp0 85 }; 86 struct 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 */ 93 struct cpu_info *cpu_info[ALPHA_MAXPROCS]; 94 95 /* Bitmask of CPUs booted, currently running, and paused. */ 96 volatile u_long cpus_booted; 97 volatile u_long cpus_running; 98 volatile u_long cpus_paused; 99 100 void 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 */ 111 u_long cpu_implver, cpu_amask; 112 113 /* Definition of the driver for autoconfig. */ 114 static int cpumatch(struct device *, struct cfdata *, void *); 115 static void cpuattach(struct device *, struct device *, void *); 116 117 CFATTACH_DECL(cpu, sizeof(struct cpu_softc), 118 cpumatch, cpuattach, NULL, NULL); 119 120 static void cpu_announce_extensions(struct cpu_info *); 121 122 extern struct cfdriver cpu_cd; 123 124 static const char *lcaminor[] = { 125 "", 126 "21066", "21066", 127 "21068", "21068", 128 "21066A", "21068A", 0 129 }; 130 131 struct cputable_struct { 132 int cpu_major_code; 133 const char *cpu_major_name; 134 const char **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 178 static int 179 cpumatch(struct device *parent, struct cfdata *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 193 static void 194 cpuattach(struct device *parent, struct device *self, void *aux) 195 { 196 struct cpu_softc *sc = (void *) self; 197 struct mainbus_attach_args *ma = aux; 198 int i; 199 const char **s; 200 struct pcs *p; 201 #ifdef DEBUG 202 int needcomma; 203 #endif 204 u_int32_t major, minor; 205 struct cpu_info *ci; 206 207 p = LOCATE_PCS(hwrpb, ma->ma_slot); 208 major = PCS_CPU_MAJORTYPE(p); 209 minor = PCS_CPU_MINORTYPE(p); 210 211 printf(": ID %d%s, ", ma->ma_slot, 212 ma->ma_slot == hwrpb->rpb_primary_cpu_id ? " (primary)" : ""); 213 214 for(i = 0; i < sizeof cpunametable / sizeof cpunametable[0]; ++i) { 215 if (cpunametable[i].cpu_major_code == major) { 216 printf("%s-%d", 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 printf(" (%s)\n", s[i]); 221 goto recognized; 222 } 223 } 224 goto recognized; 225 } 226 } 227 printf("UNKNOWN CPU TYPE (%d:%d)", major, minor); 228 229 recognized: 230 printf("\n"); 231 232 #ifdef DEBUG 233 if (p->pcs_proc_var != 0) { 234 printf("%s: ", sc->sc_dev.dv_xname); 235 236 needcomma = 0; 237 if (p->pcs_proc_var & PCS_VAR_VAXFP) { 238 printf("VAX FP support"); 239 needcomma = 1; 240 } 241 if (p->pcs_proc_var & PCS_VAR_IEEEFP) { 242 printf("%sIEEE FP support", needcomma ? ", " : ""); 243 needcomma = 1; 244 } 245 if (p->pcs_proc_var & PCS_VAR_PE) { 246 printf("%sPrimary Eligible", needcomma ? ", " : ""); 247 needcomma = 1; 248 } 249 if (p->pcs_proc_var & PCS_VAR_RESERVED) 250 printf("%sreserved bits: 0x%lx", needcomma ? ", " : "", 251 p->pcs_proc_var & PCS_VAR_RESERVED); 252 printf("\n"); 253 } 254 #endif 255 256 if (ma->ma_slot > ALPHA_WHAMI_MAXID) { 257 if (ma->ma_slot == hwrpb->rpb_primary_cpu_id) 258 panic("cpu_attach: primary CPU ID too large"); 259 printf("%s: procssor ID too large, ignoring\n", 260 sc->sc_dev.dv_xname); 261 return; 262 } 263 264 if (ma->ma_slot == hwrpb->rpb_primary_cpu_id) 265 ci = &cpu_info_primary; 266 else { 267 ci = malloc(sizeof(*ci), M_DEVBUF, M_WAITOK); 268 memset(ci, 0, sizeof(*ci)); 269 } 270 #if defined(MULTIPROCESSOR) 271 cpu_info[ma->ma_slot] = ci; 272 #endif 273 ci->ci_cpuid = ma->ma_slot; 274 ci->ci_softc = sc; 275 ci->ci_pcc_freq = hwrpb->rpb_cc_freq; 276 277 /* 278 * Though we could (should?) attach the LCA cpus' PCI 279 * bus here there is no good reason to do so, and 280 * the bus attachment code is easier to understand 281 * and more compact if done the 'normal' way. 282 */ 283 284 #if defined(MULTIPROCESSOR) 285 /* 286 * Make sure the processor is available for use. 287 */ 288 if ((p->pcs_flags & PCS_PA) == 0) { 289 if (ma->ma_slot == hwrpb->rpb_primary_cpu_id) 290 panic("cpu_attach: primary not available?!"); 291 printf("%s: processor not available for use\n", 292 sc->sc_dev.dv_xname); 293 return; 294 } 295 296 /* Make sure the processor has valid PALcode. */ 297 if ((p->pcs_flags & PCS_PV) == 0) { 298 if (ma->ma_slot == hwrpb->rpb_primary_cpu_id) 299 panic("cpu_attach: primary has invalid PALcode?!"); 300 printf("%s: PALcode not valid\n", sc->sc_dev.dv_xname); 301 return; 302 } 303 #endif /* MULTIPROCESSOR */ 304 305 /* 306 * If we're the primary CPU, no more work to do; we're already 307 * running! 308 */ 309 if (ma->ma_slot == hwrpb->rpb_primary_cpu_id) { 310 cpu_announce_extensions(ci); 311 #if defined(MULTIPROCESSOR) 312 ci->ci_flags |= CPUF_PRIMARY|CPUF_RUNNING; 313 atomic_or_ulong(&cpus_booted, (1UL << ma->ma_slot)); 314 atomic_or_ulong(&cpus_running, (1UL << ma->ma_slot)); 315 #endif /* MULTIPROCESSOR */ 316 } else { 317 #if defined(MULTIPROCESSOR) 318 int error; 319 320 error = mi_cpu_attach(ci); 321 if (error != 0) { 322 aprint_error("%s: mi_cpu_attach failed with %d\n", 323 sc->sc_dev.dv_xname, error); 324 return; 325 } 326 327 /* 328 * Boot the secondary processor. It will announce its 329 * extensions, and then spin until we tell it to go 330 * on its merry way. 331 */ 332 cpu_boot_secondary(ci); 333 334 /* 335 * Link the processor into the list. 336 */ 337 ci->ci_next = cpu_info_list->ci_next; 338 cpu_info_list->ci_next = ci; 339 #else /* ! MULTIPROCESSOR */ 340 printf("%s: processor off-line; multiprocessor support " 341 "not present in kernel\n", sc->sc_dev.dv_xname); 342 #endif /* MULTIPROCESSOR */ 343 } 344 345 evcnt_attach_dynamic(&sc->sc_evcnt_clock, EVCNT_TYPE_INTR, 346 NULL, sc->sc_dev.dv_xname, "clock"); 347 evcnt_attach_dynamic(&sc->sc_evcnt_device, EVCNT_TYPE_INTR, 348 NULL, sc->sc_dev.dv_xname, "device"); 349 #if defined(MULTIPROCESSOR) 350 alpha_ipi_init(ci); 351 #endif 352 } 353 354 static void 355 cpu_announce_extensions(struct cpu_info *ci) 356 { 357 u_long implver, amask = 0; 358 char bits[64]; 359 360 implver = alpha_implver(); 361 if (implver >= ALPHA_IMPLVER_EV5) 362 amask = (~alpha_amask(ALPHA_AMASK_ALL)) & ALPHA_AMASK_ALL; 363 364 if (ci->ci_cpuid == hwrpb->rpb_primary_cpu_id) { 365 cpu_implver = implver; 366 cpu_amask = amask; 367 } else { 368 if (implver < cpu_implver) 369 printf("%s: WARNING: IMPLVER %lu < %lu\n", 370 ci->ci_softc->sc_dev.dv_xname, 371 implver, cpu_implver); 372 373 /* 374 * Cap the system architecture mask to the intersection 375 * of features supported by all processors in the system. 376 */ 377 cpu_amask &= amask; 378 } 379 380 if (amask) { 381 snprintb(bits, sizeof(bits), 382 ALPHA_AMASK_BITS, cpu_amask); 383 printf("%s: Architecture extensions: %s\n", 384 ci->ci_softc->sc_dev.dv_xname, bits); 385 } 386 } 387 388 #if defined(MULTIPROCESSOR) 389 void 390 cpu_boot_secondary_processors(void) 391 { 392 struct cpu_info *ci; 393 u_long i; 394 bool did_patch = false; 395 396 for (i = 0; i < ALPHA_MAXPROCS; i++) { 397 ci = cpu_info[i]; 398 if (ci == NULL || ci->ci_data.cpu_idlelwp == NULL) 399 continue; 400 if (ci->ci_flags & CPUF_PRIMARY) 401 continue; 402 if ((cpus_booted & (1UL << i)) == 0) 403 continue; 404 405 /* Patch MP-criticial kernel routines. */ 406 if (did_patch == false) { 407 alpha_patch(true); 408 did_patch = true; 409 } 410 411 /* 412 * Launch the processor. 413 */ 414 atomic_or_ulong(&ci->ci_flags, CPUF_RUNNING); 415 atomic_or_ulong(&cpus_running, (1U << i)); 416 } 417 } 418 419 void 420 cpu_boot_secondary(struct cpu_info *ci) 421 { 422 long timeout; 423 struct pcs *pcsp, *primary_pcsp; 424 struct pcb *pcb; 425 u_long cpumask; 426 427 pcb = lwp_getpcb(ci->ci_data.cpu_idlelwp); 428 primary_pcsp = LOCATE_PCS(hwrpb, hwrpb->rpb_primary_cpu_id); 429 pcsp = LOCATE_PCS(hwrpb, ci->ci_cpuid); 430 cpumask = (1UL << ci->ci_cpuid); 431 432 /* 433 * Set up the PCS's HWPCB to match ours. 434 */ 435 memcpy(pcsp->pcs_hwpcb, &pcb->pcb_hw, sizeof(pcb->pcb_hw)); 436 437 /* 438 * Set up the HWRPB to restart the secondary processor 439 * with our spin-up trampoline. 440 */ 441 hwrpb->rpb_restart = (u_int64_t) cpu_spinup_trampoline; 442 hwrpb->rpb_restart_val = (u_int64_t) ci; 443 hwrpb->rpb_checksum = hwrpb_checksum(); 444 445 /* 446 * Configure the CPU to start in OSF/1 PALcode by copying 447 * the primary CPU's PALcode revision info to the secondary 448 * CPUs PCS. 449 */ 450 memcpy(&pcsp->pcs_pal_rev, &primary_pcsp->pcs_pal_rev, 451 sizeof(pcsp->pcs_pal_rev)); 452 pcsp->pcs_flags |= (PCS_CV|PCS_RC); 453 pcsp->pcs_flags &= ~PCS_BIP; 454 455 /* Make sure the secondary console sees all this. */ 456 alpha_mb(); 457 458 /* Send a "START" command to the secondary CPU's console. */ 459 if (cpu_iccb_send(ci->ci_cpuid, "START\r\n")) { 460 printf("%s: unable to issue `START' command\n", 461 ci->ci_softc->sc_dev.dv_xname); 462 return; 463 } 464 465 /* Wait for the processor to boot. */ 466 for (timeout = 10000; timeout != 0; timeout--) { 467 alpha_mb(); 468 if (pcsp->pcs_flags & PCS_BIP) 469 break; 470 delay(1000); 471 } 472 if (timeout == 0) 473 printf("%s: processor failed to boot\n", 474 ci->ci_softc->sc_dev.dv_xname); 475 476 /* 477 * ...and now wait for verification that it's running kernel 478 * code. 479 */ 480 for (timeout = 10000; timeout != 0; timeout--) { 481 alpha_mb(); 482 if (cpus_booted & cpumask) 483 break; 484 delay(1000); 485 } 486 if (timeout == 0) 487 printf("%s: processor failed to hatch\n", 488 ci->ci_softc->sc_dev.dv_xname); 489 } 490 491 void 492 cpu_pause_resume(u_long cpu_id, int pause) 493 { 494 u_long cpu_mask = (1UL << cpu_id); 495 496 if (pause) { 497 atomic_or_ulong(&cpus_paused, cpu_mask); 498 alpha_send_ipi(cpu_id, ALPHA_IPI_PAUSE); 499 } else 500 atomic_and_ulong(&cpus_paused, ~cpu_mask); 501 } 502 503 void 504 cpu_pause_resume_all(int pause) 505 { 506 struct cpu_info *ci, *self = curcpu(); 507 CPU_INFO_ITERATOR cii; 508 509 for (CPU_INFO_FOREACH(cii, ci)) { 510 if (ci == self) 511 continue; 512 cpu_pause_resume(ci->ci_cpuid, pause); 513 } 514 } 515 516 void 517 cpu_halt(void) 518 { 519 struct cpu_info *ci = curcpu(); 520 u_long cpu_id = cpu_number(); 521 struct pcs *pcsp = LOCATE_PCS(hwrpb, cpu_id); 522 523 printf("%s: shutting down...\n", ci->ci_softc->sc_dev.dv_xname); 524 525 pcsp->pcs_flags &= ~(PCS_RC | PCS_HALT_REQ); 526 pcsp->pcs_flags |= PCS_HALT_STAY_HALTED; 527 528 atomic_and_ulong(&cpus_running, ~(1UL << cpu_id)); 529 atomic_and_ulong(&cpus_booted, ~(1U << cpu_id)); 530 531 alpha_pal_halt(); 532 /* NOTREACHED */ 533 } 534 535 void 536 cpu_hatch(struct cpu_info *ci) 537 { 538 u_long cpu_id = cpu_number(); 539 u_long cpumask = (1UL << cpu_id); 540 541 /* Mark the kernel pmap active on this processor. */ 542 atomic_or_ulong(&pmap_kernel()->pm_cpus, cpumask); 543 544 /* Initialize trap vectors for this processor. */ 545 trap_init(); 546 547 /* Yahoo! We're running kernel code! Announce it! */ 548 cpu_announce_extensions(ci); 549 550 atomic_or_ulong(&cpus_booted, cpumask); 551 552 /* 553 * Spin here until we're told we can start. 554 */ 555 while ((cpus_running & cpumask) == 0) 556 /* spin */ ; 557 558 /* 559 * Invalidate the TLB and sync the I-stream before we 560 * jump into the kernel proper. We have to do this 561 * beacause we haven't been getting IPIs while we've 562 * been spinning. 563 */ 564 ALPHA_TBIA(); 565 alpha_pal_imb(); 566 567 cc_calibrate_cpu(ci); 568 } 569 570 int 571 cpu_iccb_send(long cpu_id, const char *msg) 572 { 573 struct pcs *pcsp = LOCATE_PCS(hwrpb, cpu_id); 574 int timeout; 575 u_long cpumask = (1UL << cpu_id); 576 577 /* Wait for the ICCB to become available. */ 578 for (timeout = 10000; timeout != 0; timeout--) { 579 alpha_mb(); 580 if ((hwrpb->rpb_rxrdy & cpumask) == 0) 581 break; 582 delay(1000); 583 } 584 if (timeout == 0) 585 return (EIO); 586 587 /* 588 * Copy the message into the ICCB, and tell the secondary console 589 * that it's there. 590 */ 591 strcpy(pcsp->pcs_iccb.iccb_rxbuf, msg); 592 pcsp->pcs_iccb.iccb_rxlen = strlen(msg); 593 atomic_or_ulong(&hwrpb->rpb_rxrdy, cpumask); 594 membar_sync(); 595 596 /* Wait for the message to be received. */ 597 for (timeout = 10000; timeout != 0; timeout--) { 598 alpha_mb(); 599 if ((hwrpb->rpb_rxrdy & cpumask) == 0) 600 break; 601 delay(1000); 602 } 603 if (timeout == 0) 604 return (EIO); 605 606 return (0); 607 } 608 609 void 610 cpu_iccb_receive(void) 611 { 612 #if 0 /* Don't bother... we don't get any important messages anyhow. */ 613 u_int64_t txrdy; 614 char *cp1, *cp2, buf[80]; 615 struct pcs *pcsp; 616 u_int cnt; 617 long cpu_id; 618 619 txrdy = hwrpb->rpb_txrdy; 620 621 for (cpu_id = 0; cpu_id < hwrpb->rpb_pcs_cnt; cpu_id++) { 622 if (txrdy & (1UL << cpu_id)) { 623 pcsp = LOCATE_PCS(hwrpb, cpu_id); 624 printf("Inter-console message from CPU %lu " 625 "HALT REASON = 0x%lx, FLAGS = 0x%lx\n", 626 cpu_id, pcsp->pcs_halt_reason, pcsp->pcs_flags); 627 628 cnt = pcsp->pcs_iccb.iccb_txlen; 629 if (cnt >= 80) { 630 printf("Malformed inter-console message\n"); 631 continue; 632 } 633 cp1 = pcsp->pcs_iccb.iccb_txbuf; 634 cp2 = buf; 635 while (cnt--) { 636 if (*cp1 != '\r' && *cp1 != '\n') 637 *cp2++ = *cp1; 638 cp1++; 639 } 640 *cp2 = '\0'; 641 printf("Message from CPU %lu: %s\n", cpu_id, buf); 642 } 643 } 644 #endif /* 0 */ 645 hwrpb->rpb_txrdy = 0; 646 alpha_mb(); 647 } 648 649 #if defined(DDB) 650 651 #include <ddb/db_output.h> 652 #include <machine/db_machdep.h> 653 654 /* 655 * Dump CPU information from DDB. 656 */ 657 void 658 cpu_debug_dump(void) 659 { 660 struct cpu_info *ci; 661 CPU_INFO_ITERATOR cii; 662 663 db_printf("addr dev id flags ipis curproc fpcurproc\n"); 664 for (CPU_INFO_FOREACH(cii, ci)) { 665 db_printf("%p %s %lu %lx %lx %p %p\n", 666 ci, 667 ci->ci_softc->sc_dev.dv_xname, 668 ci->ci_cpuid, 669 ci->ci_flags, 670 ci->ci_ipis, 671 ci->ci_curlwp, 672 ci->ci_fpcurlwp); 673 } 674 } 675 676 #endif /* DDB */ 677 678 #endif /* MULTIPROCESSOR */ 679