1 /* $NetBSD: machdep.c,v 1.154 2026/04/28 03:29:11 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 1988 University of Utah. 5 * Copyright (c) 1982, 1986, 1990, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * the Systems Programming Group of the University of Utah Computer 10 * Science Department. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * from: Utah Hdr: machdep.c 1.74 92/12/20 37 * from: @(#)machdep.c 8.10 (Berkeley) 4/20/94 38 */ 39 40 #include <sys/cdefs.h> 41 __KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.154 2026/04/28 03:29:11 thorpej Exp $"); 42 43 #include "opt_ddb.h" 44 #include "opt_kgdb.h" 45 #include "opt_modular.h" 46 47 #include <sys/param.h> 48 #include <sys/systm.h> 49 #include <sys/kernel.h> 50 #include <sys/proc.h> 51 #include <sys/buf.h> 52 #include <sys/reboot.h> 53 #include <sys/conf.h> 54 #include <sys/file.h> 55 #include <sys/device.h> 56 #include <sys/mbuf.h> 57 #include <sys/msgbuf.h> 58 #include <sys/ioctl.h> 59 #include <sys/tty.h> 60 #include <sys/mount.h> 61 #include <sys/exec.h> 62 #include <sys/core.h> 63 #include <sys/kcore.h> 64 #include <sys/vnode.h> 65 #include <sys/syscallargs.h> 66 #include <sys/ksyms.h> 67 #include <sys/module.h> 68 #include <sys/cpu.h> 69 #ifdef KGDB 70 #include <sys/kgdb.h> 71 #endif 72 73 #include <uvm/uvm_extern.h> 74 75 #include <sys/sysctl.h> 76 77 #include <dev/cons.h> 78 #include <dev/mm.h> 79 80 #include <machine/cpu.h> 81 #include <machine/dvma.h> 82 #include <machine/idprom.h> 83 #include <machine/kcore.h> 84 #include <machine/reg.h> 85 #include <machine/pcb.h> 86 #include <machine/psl.h> 87 #include <machine/pte.h> 88 #include <machine/mon.h> 89 90 #if defined(DDB) 91 #include <machine/db_machdep.h> 92 #include <ddb/db_sym.h> 93 #include <ddb/db_extern.h> 94 #endif 95 96 #include <sun3/sun3/machdep.h> 97 98 #include "ksyms.h" 99 100 /* Defined in locore.s */ 101 extern char kernel_text[]; 102 /* Defined by the linker */ 103 extern char etext[]; 104 105 /* kernel_arch specific values required by module(9) */ 106 const vaddr_t kernbase = KERNBASE3X; 107 const vaddr_t kern_end = KERN_END3X; 108 109 extern void *msgbufaddr; 110 111 /* Virtual page frame for /dev/mem (see mem.c) */ 112 vaddr_t vmmap; 113 114 u_char cpu_machine_id = 0; 115 const char *cpu_string = NULL; 116 int cpu_has_vme = 0; 117 int has_iocache = 0; 118 119 vaddr_t dumppage; 120 121 int delay_divisor = delay_divisor_est(33); /* assume fastest 33Hz */ 122 char kernel_arch[16] = "sun3x"; /* XXX needs a sysctl node */ 123 124 static void initcpu(void); 125 126 /* 127 * Console initialization: called early on from main, 128 * before vm init or cpu_startup. This system is able 129 * to use the console for output immediately (via PROM) 130 * but can not use it for input until after this point. 131 */ 132 void 133 consinit(void) 134 { 135 136 /* 137 * Switch from the PROM console (output only) 138 * to our own console driver. 139 */ 140 cninit(); 141 142 #if NKSYMS || defined(DDB) || defined(MODULAR) 143 { 144 extern int nsym; 145 extern char *ssym, *esym; 146 147 ksyms_addsyms_elf(nsym, ssym, esym); 148 } 149 #endif /* DDB */ 150 151 /* 152 * Now that the console can do input as well as 153 * output, consider stopping for a debugger. 154 */ 155 if (boothowto & RB_KDB) { 156 #ifdef KGDB 157 /* XXX - Ask on console for kgdb_dev? */ 158 /* Note: this will just return if kgdb_dev==NODEV */ 159 kgdb_connect(1); 160 #else /* KGDB */ 161 /* Either DDB or no debugger (just PROM). */ 162 Debugger(); 163 #endif /* KGDB */ 164 } 165 } 166 167 /* 168 * cpu_startup: allocate memory for variable-sized tables, 169 * initialize CPU, and do autoconfiguration. 170 * 171 * This is called early in init_main.c:main(), after the 172 * kernel memory allocator is ready for use, but before 173 * the creation of processes 1,2, and mountroot, etc. 174 */ 175 void 176 cpu_startup(void) 177 { 178 /* Set the FPU bit in the "system enable register" */ 179 enable_fpu(1); 180 181 /* 182 * Get scratch page for dumpsys(). 183 */ 184 dumppage = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, UVM_KMF_WIRED); 185 if (dumppage == 0) 186 panic("startup: alloc dumppage"); 187 188 cpu_startup_common(); 189 190 /* 191 * Allocate a virtual page (for use by /dev/mem) 192 * This page is handed to pmap_enter() therefore 193 * it has to be in the normal kernel VA range. 194 */ 195 vmmap = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, 196 UVM_KMF_VAONLY | UVM_KMF_WAITVA); 197 198 /* 199 * Create the DVMA maps. 200 */ 201 dvma_init(); 202 203 /* 204 * Set up CPU-specific registers, cache, etc. 205 */ 206 initcpu(); 207 208 if (fputype == FPU_NONE) { 209 /* Might as well turn the enable bit back off. */ 210 enable_fpu(0); 211 } 212 } 213 214 void 215 machine_set_model(void) 216 { 217 u_char machtype = identity_prom.idp_machtype; 218 if ((machtype & IDM_ARCH_MASK) != IDM_ARCH_SUN3X) { 219 printf("Bad IDPROM arch!\n"); 220 sunmon_abort(); 221 } 222 223 cpu_machine_id = machtype; 224 switch (cpu_machine_id) { 225 226 case ID_SUN3X_80: 227 cpu_string = "80"; /* Hydra */ 228 cpuspeed_khz = 20000; 229 delay_divisor = delay_divisor_est(20); 230 cpu_has_vme = false; 231 break; 232 233 case ID_SUN3X_470: 234 cpu_string = "470"; /* Pegasus */ 235 cpuspeed_khz = 33000; 236 delay_divisor = delay_divisor_est(33); 237 cpu_has_vme = true; 238 break; 239 240 default: 241 printf("unknown sun3x model\n"); 242 sunmon_abort(); 243 } 244 245 sun68k_set_model(); 246 } 247 248 /* 249 * machine dependent system variables. 250 */ 251 #if 0 /* XXX - Not yet... */ 252 static int 253 sysctl_machdep_root_device(SYSCTLFN_ARGS) 254 { 255 struct sysctlnode node = *rnode; 256 257 node.sysctl_data = some permutation on root_device; 258 node.sysctl_size = strlen(root_device) + 1; 259 return (sysctl_lookup(SYSCTLFN_CALL(&node))); 260 } 261 262 static int 263 sysctl_machdep_booted_kernel(SYSCTLFN_ARGS) 264 { 265 struct sysctlnode node = *rnode; 266 267 node.sysctl_data = some permutation on booted_kernel; 268 node.sysctl_size = strlen(booted_kernel) + 1; 269 return (sysctl_lookup(SYSCTLFN_CALL(&node))); 270 } 271 #endif 272 273 SYSCTL_SETUP(sysctl_machdep_setup, "sysctl machdep subtree setup") 274 { 275 276 sysctl_createv(clog, 0, NULL, NULL, 277 CTLFLAG_PERMANENT, 278 CTLTYPE_NODE, "machdep", NULL, 279 NULL, 0, NULL, 0, 280 CTL_MACHDEP, CTL_EOL); 281 282 sysctl_createv(clog, 0, NULL, NULL, 283 CTLFLAG_PERMANENT, 284 CTLTYPE_STRUCT, "console_device", NULL, 285 sysctl_consdev, 0, NULL, sizeof(dev_t), 286 CTL_MACHDEP, CPU_CONSDEV, CTL_EOL); 287 #if 0 /* XXX - Not yet... */ 288 sysctl_createv(clog, 0, NULL, NULL, 289 CTLFLAG_PERMANENT, 290 CTLTYPE_STRING, "root_device", NULL, 291 sysctl_machdep_root_device, 0, NULL, 0, 292 CTL_MACHDEP, CPU_ROOT_DEVICE, CTL_EOL); 293 sysctl_createv(clog, 0, NULL, NULL, 294 CTLFLAG_PERMANENT, 295 CTLTYPE_STRING, "booted_kernel", NULL, 296 sysctl_machdep_booted_kernel, 0, NULL, 0, 297 CTL_MACHDEP, CPU_BOOTED_KERNEL, CTL_EOL); 298 #endif 299 } 300 301 void 302 machine_halt(void) 303 { 304 sunmon_halt(); 305 } 306 307 void 308 machine_reboot(int howto, char *user_bootstr) 309 { 310 static char bootstr[128]; /* N.B. MUST BE STATIC */ 311 char *p; 312 313 if (user_bootstr != NULL) { 314 strlcpy(bootstr, user_bootstr, sizeof(bootstr)); 315 } else { 316 /* 317 * Build our own boot string with an empty 318 * boot device/file and (maybe) some flags. 319 * The PROM will supply the device/file name. 320 */ 321 p = bootstr; 322 *p = '\0'; 323 324 if (howto & (RB_KDB|RB_ASKNAME|RB_SINGLE)) { 325 /* Append the boot flags. */ 326 *p++ = ' '; 327 *p++ = '-'; 328 if (howto & RB_KDB) 329 *p++ = 'd'; 330 if (howto & RB_ASKNAME) 331 *p++ = 'a'; 332 if (howto & RB_SINGLE) 333 *p++ = 's'; 334 *p = '\0'; 335 } 336 } 337 sunmon_reboot(bootstr); 338 } 339 340 /* 341 * These variables are needed by /sbin/savecore 342 */ 343 uint32_t dumpmag = 0x8fca0101; /* magic number */ 344 int dumpsize = 0; /* pages */ 345 long dumplo = 0; /* blocks */ 346 347 #define DUMP_EXTRA 1 /* CPU-dependent extra pages */ 348 349 /* 350 * This is called by main to set dumplo, dumpsize. 351 * Dumps always skip the first PAGE_SIZE of disk space 352 * in case there might be a disk label stored there. 353 * If there is extra space, put dump at the end to 354 * reduce the chance that swapping trashes it. 355 */ 356 void 357 cpu_dumpconf(void) 358 { 359 int devblks; /* size of dump device in blocks */ 360 int dumpblks; /* size of dump image in blocks */ 361 362 if (dumpdev == NODEV) 363 return; 364 365 devblks = bdev_size(dumpdev); 366 if (devblks <= ctod(1)) 367 return; 368 devblks &= ~(ctod(1) - 1); 369 370 /* 371 * Note: savecore expects dumpsize to be the 372 * number of pages AFTER the dump header. 373 */ 374 dumpsize = physmem; /* pages */ 375 376 /* Position dump image near end of space, page aligned. */ 377 dumpblks = ctod(physmem + DUMP_EXTRA); 378 dumplo = devblks - dumpblks; 379 380 /* If it does not fit, truncate it by moving dumplo. */ 381 /* Note: Must force signed comparison. */ 382 if (dumplo < ((long)ctod(1))) { 383 dumplo = ctod(1); 384 dumpsize = dtoc(devblks - dumplo) - DUMP_EXTRA; 385 } 386 } 387 388 /* Note: gdb looks for "dumppcb" in a kernel crash dump. */ 389 struct pcb dumppcb; 390 391 /* 392 * Write a crash dump. The format while in swap is: 393 * kcore_seg_t cpu_hdr; 394 * cpu_kcore_hdr_t cpu_data; 395 * padding (PAGE_SIZE-sizeof(kcore_seg_t)) 396 * pagemap (2*PAGE_SIZE) 397 * physical memory... 398 */ 399 void 400 dumpsys(void) 401 { 402 const struct bdevsw *dsw; 403 kcore_seg_t *kseg_p; 404 cpu_kcore_hdr_t *chdr_p; 405 struct sun3x_kcore_hdr *sh; 406 phys_ram_seg_t *crs_p; 407 char *vaddr; 408 paddr_t paddr; 409 int psize, todo, seg, segsz; 410 daddr_t blkno; 411 int error = 0; 412 413 if (dumpdev == NODEV) 414 return; 415 dsw = bdevsw_lookup(dumpdev); 416 if (dsw == NULL || dsw->d_psize == NULL) 417 return; 418 419 /* 420 * For dumps during autoconfiguration, 421 * if dump device has already configured... 422 */ 423 if (dumpsize == 0) 424 cpu_dumpconf(); 425 if (dumplo <= 0) { 426 printf("\ndump to dev %u,%u not possible\n", 427 major(dumpdev), minor(dumpdev)); 428 return; 429 } 430 savectx(&dumppcb); 431 432 psize = bdev_size(dumpdev); 433 if (psize == -1) { 434 printf("dump area unavailable\n"); 435 return; 436 } 437 438 printf("\ndumping to dev %u,%u offset %ld\n", 439 major(dumpdev), minor(dumpdev), dumplo); 440 441 /* 442 * Prepare the dump header 443 */ 444 blkno = dumplo; 445 todo = dumpsize; /* pages */ 446 vaddr = (char *)dumppage; 447 memset(vaddr, 0, PAGE_SIZE); 448 449 /* Set pointers to all three parts. */ 450 kseg_p = (kcore_seg_t *)vaddr; 451 chdr_p = (cpu_kcore_hdr_t *)(kseg_p + 1); 452 sh = &chdr_p->un._sun3x; 453 454 /* Fill in kcore_seg_t part. */ 455 CORE_SETMAGIC(*kseg_p, KCORE_MAGIC, MID_MACHINE, CORE_CPU); 456 kseg_p->c_size = (ctob(DUMP_EXTRA) - sizeof(*kseg_p)); 457 458 /* Fill in cpu_kcore_hdr_t part. */ 459 strncpy(chdr_p->name, kernel_arch, sizeof(chdr_p->name)); 460 chdr_p->page_size = PAGE_SIZE; 461 chdr_p->kernbase = KERNBASE3X; 462 463 /* Fill in the sun3x_kcore_hdr part. */ 464 pmap_kcore_hdr(sh); 465 466 /* Write out the dump header. */ 467 error = (*dsw->d_dump)(dumpdev, blkno, vaddr, PAGE_SIZE); 468 if (error) 469 goto fail; 470 blkno += btodb(PAGE_SIZE); 471 472 /* 473 * Now dump physical memory. Note that physical memory 474 * might NOT be contiguous, so do it by segments. 475 */ 476 477 vaddr = (char *)vmmap; /* Borrow /dev/mem VA */ 478 479 for (seg = 0; seg < SUN3X_NPHYS_RAM_SEGS; seg++) { 480 crs_p = &sh->ram_segs[seg]; 481 paddr = crs_p->start; 482 segsz = crs_p->size; 483 484 while (todo && (segsz > 0)) { 485 486 /* Print pages left after every 16. */ 487 if ((todo & 0xf) == 0) 488 printf_nolog("\r%4d", todo); 489 490 /* Make a temporary mapping for the page. */ 491 pmap_kenter_pa(vmmap, paddr | PMAP_NC, VM_PROT_READ, 0); 492 pmap_update(pmap_kernel()); 493 error = (*dsw->d_dump)(dumpdev, blkno, vaddr, 494 PAGE_SIZE); 495 pmap_kremove(vmmap, PAGE_SIZE); 496 pmap_update(pmap_kernel()); 497 if (error) 498 goto fail; 499 paddr += PAGE_SIZE; 500 segsz -= PAGE_SIZE; 501 blkno += btodb(PAGE_SIZE); 502 todo--; 503 } 504 } 505 printf("\rdump succeeded\n"); 506 return; 507 fail: 508 printf(" dump error=%d\n", error); 509 } 510 511 static void 512 initcpu(void) 513 { 514 /* XXX: Enable RAM parity/ECC checking? */ 515 /* XXX: parityenable(); */ 516 517 #ifdef HAVECACHE 518 cache_enable(); 519 #endif 520 } 521 522 /* straptrap() in trap.c */ 523 524 /* from hp300: badaddr() */ 525 /* peek_byte(), peek_word() moved to bus_subr.c */ 526 527 /* XXX: parityenable() ? */ 528 /* regdump() moved to regdump.c */ 529 530 bool 531 mm_md_direct_mapped_phys(paddr_t paddr, vaddr_t *vaddr) 532 { 533 extern paddr_t avail_start; 534 535 if (paddr >= avail_start) 536 return false; 537 *vaddr = KERNBASE3X + paddr; 538 return true; 539 } 540 541 /* 542 * Allow access to the PROM mapping similar to uvm_kernacc(). 543 */ 544 int 545 mm_md_kernacc(void *ptr, vm_prot_t prot, bool *handled) 546 { 547 548 if ((vaddr_t)ptr < SUN3X_PROM_BASE || (vaddr_t)ptr > SUN3X_MONEND) { 549 *handled = false; 550 return 0; 551 } 552 553 *handled = true; 554 /* Read in the PROM itself is OK. */ 555 if ((prot & VM_PROT_WRITE) == 0) 556 return 0; 557 558 /* PROM data page is OK for read/write. */ 559 if ((vaddr_t)ptr >= SUN3X_MONDATA && 560 (vaddr_t)ptr < SUN3X_MONDATA + PAGE_SIZE) 561 return 0; 562 return EFAULT; 563 } 564