1 /* $NetBSD: machdep.c,v 1.233 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 * 38 * @(#)machdep.c 8.10 (Berkeley) 4/20/94 39 */ 40 41 #include <sys/cdefs.h> 42 __KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.233 2026/04/28 03:29:11 thorpej Exp $"); 43 44 #include "opt_ddb.h" 45 #include "opt_kgdb.h" 46 #include "opt_fpu_emulate.h" 47 #include "opt_m060sp.h" 48 #include "opt_modular.h" 49 #include "opt_panicbutton.h" 50 #include "opt_extmem.h" 51 #include "opt_m68k_arch.h" 52 53 #include <sys/param.h> 54 #include <sys/systm.h> 55 #include <sys/callout.h> 56 #include <sys/signalvar.h> 57 #include <sys/kauth.h> 58 #include <sys/kernel.h> 59 #include <sys/proc.h> 60 #include <sys/buf.h> 61 #include <sys/reboot.h> 62 #include <sys/conf.h> 63 #include <sys/file.h> 64 #include <sys/mbuf.h> 65 #include <sys/msgbuf.h> 66 #include <sys/ioctl.h> 67 #include <sys/tty.h> 68 #include <sys/mount.h> 69 #include <sys/exec.h> 70 #include <sys/vnode.h> 71 #include <sys/syscallargs.h> 72 #include <sys/core.h> 73 #include <sys/kcore.h> 74 #include <sys/ksyms.h> 75 #include <sys/module.h> 76 #include <sys/cpu.h> 77 #include <sys/sysctl.h> 78 #include <sys/device.h> 79 80 #include "ksyms.h" 81 82 #if NKSYMS || defined(DDB) || defined(MODULAR) 83 #include <sys/exec_elf.h> 84 #endif 85 86 #include <machine/db_machdep.h> 87 #include <ddb/db_sym.h> 88 #include <ddb/db_extern.h> 89 90 #include <m68k/cacheops.h> 91 #include <machine/reg.h> 92 #include <machine/pcb.h> 93 #include <machine/psl.h> 94 #include <machine/pte.h> 95 #include <machine/kcore.h> 96 97 #include <m68k/seglist.h> 98 99 #include <dev/cons.h> 100 #include <dev/mm.h> 101 102 #define MAXMEM 64*1024 /* XXX - from cmap.h */ 103 #include <uvm/uvm.h> 104 #include <uvm/uvm_physseg.h> 105 106 #include <machine/bus.h> 107 #include <machine/autoconf.h> 108 #include <arch/x68k/dev/intiovar.h> 109 #include <arch/x68k/x68k/iodevice.h> 110 111 extern void doboot(void) __attribute__((__noreturn__)); 112 113 extern paddr_t avail_start, avail_end; 114 extern u_int lowram; 115 extern int end, *esym; 116 117 int maxmem; /* max memory per process */ 118 119 /* prototypes for local functions */ 120 static int check_emulator(char *, int); 121 122 /* functions called from locore.s */ 123 void machine_init(paddr_t); 124 void nmihand(struct frame); 125 void intrhand(int); 126 127 /* memory probe function called from locore.s */ 128 void setmemrange(void); 129 #ifdef EXTENDED_MEMORY 130 static bool mem_exists(paddr_t, paddr_t); 131 #endif 132 133 static int basemem; 134 135 /* 136 * Assume a standard X68030 with 25MHz CPU. The delay_divisor will 137 * be calibrated later. 138 */ 139 int delay_divisor = delay_divisor_est(25); 140 141 142 /* 143 * machine_bootmap[] is checked in pmap_bootstrap1() of the new m68k pmap 144 * and it allocates kernel address space for intio devices. 145 */ 146 static vaddr_t intiova; 147 #define PMBM_INTIO 0 148 struct pmap_bootmap machine_bootmap[] = { 149 { .pmbm_vaddr_ptr = &intiova, 150 .pmbm_paddr = INTIOBASE, 151 .pmbm_size = INTIOSIZE, 152 .pmbm_flags = PMBM_F_CI }, 153 { .pmbm_vaddr = -1 }, 154 }; 155 156 static callout_t candbtimer_ch; 157 158 void 159 machine_init(paddr_t nextpa) 160 { 161 extern paddr_t msgbufpa; 162 163 #ifdef __HAVE_NEW_PMAP_68K 164 /* load the internal IO space region */ 165 intiobase = (uint8_t *)intiova; 166 #endif 167 168 /* 169 * Most m68k ports allocate msgbuf at the end of available memory 170 * (i.e. just after avail_end), but on x68k we allocate msgbuf 171 * at the end of main memory for compatibility. 172 * 173 * (This means we have to fully initialize the first phys seg 174 * entry.) 175 */ 176 phys_seg_list[0].ps_start = phys_seg_list[0].ps_avail_start = 177 m68k_round_page(phys_seg_list[0].ps_start); 178 phys_seg_list[0].ps_end = phys_seg_list[0].ps_avail_end = 179 m68k_trunc_page(phys_seg_list[0].ps_end); 180 181 phys_seg_list[0].ps_avail_end -= m68k_round_page(MSGBUFSIZE); 182 msgbufpa = phys_seg_list[0].ps_avail_end; 183 184 machine_init_common(nextpa); 185 } 186 187 /* 188 * Console initialization: called early on from main, 189 * before vm init or startup. Do enough configuration 190 * to choose and initialize a console. 191 */ 192 void 193 consinit(void) 194 { 195 196 /* 197 * bring graphics layer up. 198 */ 199 config_console(); 200 201 /* 202 * Initialize the console before we print anything out. 203 */ 204 cninit(); 205 206 #ifdef KGDB 207 zs_kgdb_init(); /* XXX */ 208 #endif 209 #if NKSYMS || defined(DDB) || defined(MODULAR) 210 ksyms_addsyms_elf((int)esym - (int)&end - sizeof(Elf32_Ehdr), 211 (void *)&end, esym); 212 #endif 213 #ifdef DDB 214 if (boothowto & RB_KDB) 215 Debugger(); 216 #endif 217 } 218 219 /* 220 * cpu_startup: allocate memory for variable-sized tables, 221 * initialize cpu, and do autoconfiguration. 222 */ 223 void 224 cpu_startup(void) 225 { 226 cpu_startup_common(); 227 228 callout_init(&candbtimer_ch, 0); 229 } 230 231 void 232 machine_set_model(void) 233 { 234 /* there's alot of XXX in here... */ 235 const char *mach; 236 char emubuf[20]; 237 238 /* 239 * check machine type constant 240 */ 241 switch (intio_get_sysport_mpustat()) { 242 case 0xdc: 243 /* 244 * CPU Type == 68030, Clock == 25MHz 245 */ 246 mach = "030"; 247 break; 248 case 0xfe: 249 /* 250 * CPU Type == 68000, Clock == 16MHz 251 */ 252 mach = "000XVI"; 253 break; 254 case 0xff: 255 /* 256 * CPU Type == 68000, Clock == 10MHz 257 */ 258 mach = "000/ACE/PRO/EXPERT/SUPER"; 259 break; 260 default: 261 /* 262 * unknown type 263 */ 264 mach = "000?(unknown model)"; 265 break; 266 } 267 268 emubuf[0] = '\0'; 269 check_emulator(emubuf, sizeof(emubuf)); 270 271 switch (cputype) { 272 case CPU_68060: 273 /* 274 * This delay_divisor method cannot get accurate cpuspeed 275 * for 68060. 276 */ 277 /* XXX really? */ 278 break; 279 case CPU_68040: 280 cpuspeed_khz = delay_divisor_est40(delay_divisor) * 1000; 281 break; 282 case CPU_68030: 283 cpuspeed_khz = delay_divisor_est(delay_divisor) * 1000; 284 break; 285 case CPU_68020: 286 cpuspeed_khz = delay_divisor_est(delay_divisor) * 1000; 287 break; 288 default: 289 panic("impossible cpu"); 290 } 291 292 cpu_setmodel("X68%s %s%s", 293 mach, emubuf[0] ? " on " : "", emubuf); 294 } 295 296 /* 297 * If it is an emulator, store the name in buf and return 1. 298 * Otherwise return 0. 299 */ 300 static int 301 check_emulator(char *buf, int bufsize) 302 { 303 int xm6major; 304 int xm6minor; 305 int xm6imark; 306 int xm6imajor; 307 int xm6iminor; 308 309 /* XM6 and its family */ 310 intio_set_sysport_sramwp('X'); 311 if (intio_get_sysport_sramwp() == '6') { 312 xm6major = intio_get_sysport_sramwp(); 313 xm6minor = intio_get_sysport_sramwp(); 314 xm6imark = intio_get_sysport_sramwp(); 315 switch (xm6imark) { 316 case 0xff: /* Original XM6 or unknown compatibles */ 317 snprintf(buf, bufsize, "XM6 v%x.%02x", 318 xm6major, xm6minor); 319 break; 320 321 case 'i': /* XM6i */ 322 xm6imajor = intio_get_sysport_sramwp(); 323 xm6iminor = intio_get_sysport_sramwp(); 324 snprintf(buf, bufsize, "XM6i v%d.%02d", 325 xm6imajor, xm6iminor); 326 break; 327 328 case 'g': /* XM6 TypeG */ 329 snprintf(buf, bufsize, "XM6 TypeG v%x.%02x", 330 xm6major, xm6minor); 331 break; 332 333 default: /* Other XM6 compatibles? */ 334 /* XXX what should I do? */ 335 return 0; 336 } 337 return 1; 338 } 339 340 return 0; 341 } 342 343 /* 344 * machine dependent system variables. 345 */ 346 SYSCTL_SETUP(sysctl_machdep_setup, "sysctl machdep subtree setup") 347 { 348 349 sysctl_createv(clog, 0, NULL, NULL, 350 CTLFLAG_PERMANENT, 351 CTLTYPE_NODE, "machdep", NULL, 352 NULL, 0, NULL, 0, 353 CTL_MACHDEP, CTL_EOL); 354 355 sysctl_createv(clog, 0, NULL, NULL, 356 CTLFLAG_PERMANENT, 357 CTLTYPE_STRUCT, "console_device", NULL, 358 sysctl_consdev, 0, NULL, sizeof(dev_t), 359 CTL_MACHDEP, CPU_CONSDEV, CTL_EOL); 360 } 361 362 void 363 machine_powerdown(void) 364 { 365 printf("powering off...\n"); 366 delay(1000000); 367 368 /* Turn off the alarm signal of RTC */ 369 IODEVbase->io_rtc.bank0.reset = 0x0c; 370 371 intio_set_sysport_powoff(0x00); 372 intio_set_sysport_powoff(0x0f); 373 intio_set_sysport_powoff(0x0f); 374 delay(1000000); 375 } 376 377 void 378 intrhand(int sr) 379 { 380 381 printf("intrhand: unexpected sr 0x%x\n", sr); 382 } 383 384 const uint16_t ipl2psl_table[NIPL] = { 385 [IPL_NONE] = PSL_S | PSL_IPL0, 386 [IPL_SOFTCLOCK] = PSL_S | PSL_IPL1, 387 [IPL_SOFTBIO] = PSL_S | PSL_IPL1, 388 [IPL_SOFTNET] = PSL_S | PSL_IPL1, 389 [IPL_SOFTSERIAL] = PSL_S | PSL_IPL1, 390 [IPL_VM] = PSL_S | PSL_IPL5, 391 [IPL_SCHED] = PSL_S | PSL_IPL7, 392 [IPL_HIGH] = PSL_S | PSL_IPL7, 393 }; 394 395 #if (defined(DDB) || defined(DEBUG)) && !defined(PANICBUTTON) 396 #define PANICBUTTON 397 #endif 398 399 #ifdef PANICBUTTON 400 int panicbutton = 1; /* non-zero if panic buttons are enabled */ 401 int crashandburn = 0; 402 int candbdelay = 50; /* give em half a second */ 403 void candbtimer(void *); 404 405 void 406 candbtimer(void *arg) 407 { 408 409 crashandburn = 0; 410 } 411 #endif 412 413 /* 414 * Level 7 interrupts can be caused by the NMI button. 415 */ 416 void 417 nmihand(struct frame frame) 418 { 419 420 intio_set_sysport_keyctrl(intio_get_sysport_keyctrl() | 0x04); 421 422 if (1) { 423 #ifdef PANICBUTTON 424 static int innmihand = 0; 425 426 /* 427 * Attempt to reduce the window of vulnerability for recursive 428 * NMIs (e.g. someone holding down the keyboard reset button). 429 */ 430 if (innmihand == 0) { 431 innmihand = 1; 432 printf("NMI button pressed\n"); 433 innmihand = 0; 434 } 435 #ifdef DDB 436 Debugger(); 437 #else 438 if (panicbutton) { 439 if (crashandburn) { 440 crashandburn = 0; 441 panic(panicstr ? 442 "forced crash, nosync" : "forced crash"); 443 } 444 crashandburn++; 445 callout_reset(&candbtimer_ch, candbdelay, 446 candbtimer, NULL); 447 } 448 #endif /* DDB */ 449 #endif /* PANICBUTTON */ 450 return; 451 } 452 /* panic?? */ 453 printf("unexpected level 7 interrupt ignored\n"); 454 } 455 456 #ifdef EXTENDED_MEMORY 457 458 static const struct memlist { 459 paddr_t exstart; 460 psize_t minsize; 461 psize_t maxsize; 462 } memlist[] = { 463 /* We define two extended memory regions for all possible settings. */ 464 465 /* 466 * The first region is at 0x01000000: 467 * 468 * TS-6BE16: 16MB at 0x01000000 (to 0x01FFFFFF) 469 * XM6i: 4MB - 240MB at 0x01000000 (upto 0x0FFFFFFF) 470 */ 471 { 0x01000000, 0x00400000, 0x0f000000 }, 472 473 /* 474 * The second region is at 0x10000000: 475 * 476 * 060turbo: 4MB - 128MB at 0x10000000 (upto 0x17FFFFFF) 477 * XM6i: 4MB - 768MB at 0x10000000 (upto 0x3FFFFFFF) 478 */ 479 { 0x10000000, 0x00400000, 0x30000000 }, 480 }; 481 482 /* check each 4MB region */ 483 #define EXTMEM_RANGE (4 * 1024 * 1024) 484 485 __CTASSERT(__arraycount(memlist) + 1 <= VM_PHYSSEG_MAX); 486 487 /* 488 * check memory existency 489 */ 490 static bool 491 mem_exists(paddr_t mem, paddr_t basemax) 492 { 493 /* most variables must be register! */ 494 volatile unsigned char *m, *b; 495 unsigned char save_m, save_b=0; /* XXX: shutup gcc */ 496 bool baseismem; 497 bool exists = false; 498 void *base; 499 void *begin_check, *end_check; 500 label_t faultbuf; 501 502 /* 503 * In this function we assume: 504 * - MMU is not enabled yet but PA == VA 505 * (i.e. no RELOC() macro to convert PA to VA) 506 * - bus error can be caught by setjmp() 507 * (i.e. %vbr register is initialized properly) 508 * - all memory cache is not enabled 509 */ 510 511 /* only 24bits are significant on normal X680x0 systems */ 512 base = (void *)(mem & 0x00FFFFFF); 513 514 m = (void *)mem; 515 b = (void *)base; 516 517 /* This is somewhat paranoid -- avoid overwriting myself */ 518 __asm("lea %%pc@(begin_check_mem),%0" : "=a"(begin_check)); 519 __asm("lea %%pc@(end_check_mem),%0" : "=a"(end_check)); 520 if (base >= begin_check && base < end_check) { 521 size_t off = (char *)end_check - (char *)begin_check; 522 523 m -= off; 524 b -= off; 525 } 526 527 nofault = &faultbuf; 528 if (setjmp(nofault)) { 529 nofault = NULL; 530 return false; 531 } 532 533 (void)*m; 534 /* 535 * Can't check by writing if the corresponding 536 * base address isn't memory. 537 * 538 * I hope this would be no harm.... 539 */ 540 baseismem = base < (void *)basemax; 541 542 __asm("begin_check_mem:"); 543 /* save original value (base must be saved first) */ 544 if (baseismem) 545 save_b = *b; 546 save_m = *m; 547 548 /* 549 * stack and other data segment variables are unusable 550 * til end_check_mem, because they may be clobbered. 551 */ 552 553 /* 554 * check memory by writing/reading 555 */ 556 if (baseismem) 557 *b = 0x55; 558 *m = 0xAA; 559 if ((baseismem && *b != 0x55) || *m != 0xAA) 560 goto out; 561 562 *m = 0x55; 563 if (baseismem) 564 *b = 0xAA; 565 if (*m != 0x55 || (baseismem && *b != 0xAA)) 566 goto out; 567 568 exists = true; 569 out: 570 *m = save_m; 571 if (baseismem) 572 *b = save_b; 573 574 __asm("end_check_mem:"); 575 576 nofault = NULL; 577 578 return exists; 579 } 580 #endif 581 582 void 583 setmemrange(void) 584 { 585 #ifdef EXTENDED_MEMORY 586 int i; 587 paddr_t exstart, exend; 588 psize_t size, minsize, maxsize; 589 const struct memlist *mlist = memlist; 590 paddr_t basemax = m68k_ptob(physmem); 591 #endif 592 593 /* 594 * VM system is not started yet, and even MMU is not enabled here. 595 * We assume VA == PA and don't bother to use RELOC() macro 596 * as pmap_bootstrap() does. 597 */ 598 599 /* save the original base memory range */ 600 basemem = physmem; 601 602 /* 603 * XXX 604 * Probably we should also probe the main memory region 605 * for machines which might have a wrong value in a dead SRAM. 606 */ 607 phys_seg_list[0].ps_start = lowram; 608 phys_seg_list[0].ps_end = lowram + m68k_ptob(basemem); 609 610 #ifdef EXTENDED_MEMORY 611 /* discover extended memory */ 612 for (i = 0; i < __arraycount(memlist); i++) { 613 exstart = mlist[i].exstart; 614 minsize = mlist[i].minsize; 615 maxsize = mlist[i].maxsize; 616 /* 617 * Normally, x68k hardware is NOT 32bit-clean. 618 * But some type of extended memory is in 32bit address space. 619 * Check whether. 620 */ 621 if (!mem_exists(exstart, basemax)) 622 continue; 623 exend = 0; 624 /* range check */ 625 for (size = minsize; size <= maxsize; size += EXTMEM_RANGE) { 626 if (!mem_exists(exstart + size - 4, basemax)) 627 break; 628 exend = exstart + size; 629 } 630 if (exstart < exend) { 631 phys_seg_list[i + 1].ps_start = exstart; 632 phys_seg_list[i + 1].ps_end = exend; 633 physmem += m68k_btop(exend - exstart); 634 if (maxmem < m68k_btop(exend)) 635 maxmem = m68k_btop(exend); 636 } 637 } 638 #endif 639 } 640 641 volatile unsigned int intr_depth; 642 643 bool 644 cpu_intr_p(void) 645 { 646 647 return intr_depth != 0; 648 } 649 650 int 651 mm_md_physacc(paddr_t pa, vm_prot_t prot) 652 { 653 int rv = mm_md_physacc_regular(pa, prot); 654 655 if (rv != 0) { 656 /* I/O space */ 657 if (INTIOBASE <= pa && pa < INTIOTOP) { 658 rv = kauth_authorize_machdep(kauth_cred_get(), 659 KAUTH_MACHDEP_UNMANAGEDMEM, NULL, NULL, NULL, NULL); 660 } 661 } 662 663 return rv; 664 } 665