Home | History | Annotate | Line # | Download | only in x68k
      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