#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.233 2026/04/28 03:29:11 thorpej Exp $");
#include "opt_ddb.h"
#include "opt_kgdb.h"
#include "opt_fpu_emulate.h"
#include "opt_m060sp.h"
#include "opt_modular.h"
#include "opt_panicbutton.h"
#include "opt_extmem.h"
#include "opt_m68k_arch.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/callout.h>
#include <sys/signalvar.h>
#include <sys/kauth.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/buf.h>
#include <sys/reboot.h>
#include <sys/conf.h>
#include <sys/file.h>
#include <sys/mbuf.h>
#include <sys/msgbuf.h>
#include <sys/ioctl.h>
#include <sys/tty.h>
#include <sys/mount.h>
#include <sys/exec.h>
#include <sys/vnode.h>
#include <sys/syscallargs.h>
#include <sys/core.h>
#include <sys/kcore.h>
#include <sys/ksyms.h>
#include <sys/module.h>
#include <sys/cpu.h>
#include <sys/sysctl.h>
#include <sys/device.h>
#include "ksyms.h"
#if NKSYMS || defined(DDB) || defined(MODULAR)
#include <sys/exec_elf.h>
#endif
#include <machine/db_machdep.h>
#include <ddb/db_sym.h>
#include <ddb/db_extern.h>
#include <m68k/cacheops.h>
#include <machine/reg.h>
#include <machine/pcb.h>
#include <machine/psl.h>
#include <machine/pte.h>
#include <machine/kcore.h>
#include <m68k/seglist.h>
#include <dev/cons.h>
#include <dev/mm.h>
#define MAXMEM 64*1024
#include <uvm/uvm.h>
#include <uvm/uvm_physseg.h>
#include <machine/bus.h>
#include <machine/autoconf.h>
#include <arch/x68k/dev/intiovar.h>
#include <arch/x68k/x68k/iodevice.h>
extern void doboot(void) __attribute__((__noreturn__));
extern paddr_t avail_start, avail_end;
extern u_int lowram;
extern int end, *esym;
int maxmem;
static int check_emulator(char *, int);
void machine_init(paddr_t);
void nmihand(struct frame);
void intrhand(int);
void setmemrange(void);
#ifdef EXTENDED_MEMORY
static bool mem_exists(paddr_t, paddr_t);
#endif
static int basemem;
int delay_divisor = delay_divisor_est(25);
static vaddr_t intiova;
#define PMBM_INTIO 0
struct pmap_bootmap machine_bootmap[] = {
{ .pmbm_vaddr_ptr = &intiova,
.pmbm_paddr = INTIOBASE,
.pmbm_size = INTIOSIZE,
.pmbm_flags = PMBM_F_CI },
{ .pmbm_vaddr = -1 },
};
static callout_t candbtimer_ch;
void
machine_init(paddr_t nextpa)
{
extern paddr_t msgbufpa;
#ifdef __HAVE_NEW_PMAP_68K
intiobase = (uint8_t *)intiova;
#endif
phys_seg_list[0].ps_start = phys_seg_list[0].ps_avail_start =
m68k_round_page(phys_seg_list[0].ps_start);
phys_seg_list[0].ps_end = phys_seg_list[0].ps_avail_end =
m68k_trunc_page(phys_seg_list[0].ps_end);
phys_seg_list[0].ps_avail_end -= m68k_round_page(MSGBUFSIZE);
msgbufpa = phys_seg_list[0].ps_avail_end;
machine_init_common(nextpa);
}
void
consinit(void)
{
config_console();
cninit();
#ifdef KGDB
zs_kgdb_init();
#endif
#if NKSYMS || defined(DDB) || defined(MODULAR)
ksyms_addsyms_elf((int)esym - (int)&end - sizeof(Elf32_Ehdr),
(void *)&end, esym);
#endif
#ifdef DDB
if (boothowto & RB_KDB)
Debugger();
#endif
}
void
cpu_startup(void)
{
cpu_startup_common();
callout_init(&candbtimer_ch, 0);
}
void
machine_set_model(void)
{
const char *mach;
char emubuf[20];
switch (intio_get_sysport_mpustat()) {
case 0xdc:
mach = "030";
break;
case 0xfe:
mach = "000XVI";
break;
case 0xff:
mach = "000/ACE/PRO/EXPERT/SUPER";
break;
default:
mach = "000?(unknown model)";
break;
}
emubuf[0] = '\0';
check_emulator(emubuf, sizeof(emubuf));
switch (cputype) {
case CPU_68060:
break;
case CPU_68040:
cpuspeed_khz = delay_divisor_est40(delay_divisor) * 1000;
break;
case CPU_68030:
cpuspeed_khz = delay_divisor_est(delay_divisor) * 1000;
break;
case CPU_68020:
cpuspeed_khz = delay_divisor_est(delay_divisor) * 1000;
break;
default:
panic("impossible cpu");
}
cpu_setmodel("X68%s %s%s",
mach, emubuf[0] ? " on " : "", emubuf);
}
static int
check_emulator(char *buf, int bufsize)
{
int xm6major;
int xm6minor;
int xm6imark;
int xm6imajor;
int xm6iminor;
intio_set_sysport_sramwp('X');
if (intio_get_sysport_sramwp() == '6') {
xm6major = intio_get_sysport_sramwp();
xm6minor = intio_get_sysport_sramwp();
xm6imark = intio_get_sysport_sramwp();
switch (xm6imark) {
case 0xff:
snprintf(buf, bufsize, "XM6 v%x.%02x",
xm6major, xm6minor);
break;
case 'i':
xm6imajor = intio_get_sysport_sramwp();
xm6iminor = intio_get_sysport_sramwp();
snprintf(buf, bufsize, "XM6i v%d.%02d",
xm6imajor, xm6iminor);
break;
case 'g':
snprintf(buf, bufsize, "XM6 TypeG v%x.%02x",
xm6major, xm6minor);
break;
default:
return 0;
}
return 1;
}
return 0;
}
SYSCTL_SETUP(sysctl_machdep_setup, "sysctl machdep subtree setup")
{
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT,
CTLTYPE_NODE, "machdep", NULL,
NULL, 0, NULL, 0,
CTL_MACHDEP, CTL_EOL);
sysctl_createv(clog, 0, NULL, NULL,
CTLFLAG_PERMANENT,
CTLTYPE_STRUCT, "console_device", NULL,
sysctl_consdev, 0, NULL, sizeof(dev_t),
CTL_MACHDEP, CPU_CONSDEV, CTL_EOL);
}
void
machine_powerdown(void)
{
printf("powering off...\n");
delay(1000000);
IODEVbase->io_rtc.bank0.reset = 0x0c;
intio_set_sysport_powoff(0x00);
intio_set_sysport_powoff(0x0f);
intio_set_sysport_powoff(0x0f);
delay(1000000);
}
void
intrhand(int sr)
{
printf("intrhand: unexpected sr 0x%x\n", sr);
}
const uint16_t ipl2psl_table[NIPL] = {
[IPL_NONE] = PSL_S | PSL_IPL0,
[IPL_SOFTCLOCK] = PSL_S | PSL_IPL1,
[IPL_SOFTBIO] = PSL_S | PSL_IPL1,
[IPL_SOFTNET] = PSL_S | PSL_IPL1,
[IPL_SOFTSERIAL] = PSL_S | PSL_IPL1,
[IPL_VM] = PSL_S | PSL_IPL5,
[IPL_SCHED] = PSL_S | PSL_IPL7,
[IPL_HIGH] = PSL_S | PSL_IPL7,
};
#if (defined(DDB) || defined(DEBUG)) && !defined(PANICBUTTON)
#define PANICBUTTON
#endif
#ifdef PANICBUTTON
int panicbutton = 1;
int crashandburn = 0;
int candbdelay = 50;
void candbtimer(void *);
void
candbtimer(void *arg)
{
crashandburn = 0;
}
#endif
void
nmihand(struct frame frame)
{
intio_set_sysport_keyctrl(intio_get_sysport_keyctrl() | 0x04);
if (1) {
#ifdef PANICBUTTON
static int innmihand = 0;
if (innmihand == 0) {
innmihand = 1;
printf("NMI button pressed\n");
innmihand = 0;
}
#ifdef DDB
Debugger();
#else
if (panicbutton) {
if (crashandburn) {
crashandburn = 0;
panic(panicstr ?
"forced crash, nosync" : "forced crash");
}
crashandburn++;
callout_reset(&candbtimer_ch, candbdelay,
candbtimer, NULL);
}
#endif
#endif
return;
}
printf("unexpected level 7 interrupt ignored\n");
}
#ifdef EXTENDED_MEMORY
static const struct memlist {
paddr_t exstart;
psize_t minsize;
psize_t maxsize;
} memlist[] = {
{ 0x01000000, 0x00400000, 0x0f000000 },
{ 0x10000000, 0x00400000, 0x30000000 },
};
#define EXTMEM_RANGE (4 * 1024 * 1024)
__CTASSERT(__arraycount(memlist) + 1 <= VM_PHYSSEG_MAX);
static bool
mem_exists(paddr_t mem, paddr_t basemax)
{
volatile unsigned char *m, *b;
unsigned char save_m, save_b=0;
bool baseismem;
bool exists = false;
void *base;
void *begin_check, *end_check;
label_t faultbuf;
base = (void *)(mem & 0x00FFFFFF);
m = (void *)mem;
b = (void *)base;
__asm("lea %%pc@(begin_check_mem),%0" : "=a"(begin_check));
__asm("lea %%pc@(end_check_mem),%0" : "=a"(end_check));
if (base >= begin_check && base < end_check) {
size_t off = (char *)end_check - (char *)begin_check;
m -= off;
b -= off;
}
nofault = &faultbuf;
if (setjmp(nofault)) {
nofault = NULL;
return false;
}
(void)*m;
baseismem = base < (void *)basemax;
__asm("begin_check_mem:");
if (baseismem)
save_b = *b;
save_m = *m;
if (baseismem)
*b = 0x55;
*m = 0xAA;
if ((baseismem && *b != 0x55) || *m != 0xAA)
goto out;
*m = 0x55;
if (baseismem)
*b = 0xAA;
if (*m != 0x55 || (baseismem && *b != 0xAA))
goto out;
exists = true;
out:
*m = save_m;
if (baseismem)
*b = save_b;
__asm("end_check_mem:");
nofault = NULL;
return exists;
}
#endif
void
setmemrange(void)
{
#ifdef EXTENDED_MEMORY
int i;
paddr_t exstart, exend;
psize_t size, minsize, maxsize;
const struct memlist *mlist = memlist;
paddr_t basemax = m68k_ptob(physmem);
#endif
basemem = physmem;
phys_seg_list[0].ps_start = lowram;
phys_seg_list[0].ps_end = lowram + m68k_ptob(basemem);
#ifdef EXTENDED_MEMORY
for (i = 0; i < __arraycount(memlist); i++) {
exstart = mlist[i].exstart;
minsize = mlist[i].minsize;
maxsize = mlist[i].maxsize;
if (!mem_exists(exstart, basemax))
continue;
exend = 0;
for (size = minsize; size <= maxsize; size += EXTMEM_RANGE) {
if (!mem_exists(exstart + size - 4, basemax))
break;
exend = exstart + size;
}
if (exstart < exend) {
phys_seg_list[i + 1].ps_start = exstart;
phys_seg_list[i + 1].ps_end = exend;
physmem += m68k_btop(exend - exstart);
if (maxmem < m68k_btop(exend))
maxmem = m68k_btop(exend);
}
}
#endif
}
volatile unsigned int intr_depth;
bool
cpu_intr_p(void)
{
return intr_depth != 0;
}
int
mm_md_physacc(paddr_t pa, vm_prot_t prot)
{
int rv = mm_md_physacc_regular(pa, prot);
if (rv != 0) {
if (INTIOBASE <= pa && pa < INTIOTOP) {
rv = kauth_authorize_machdep(kauth_cred_get(),
KAUTH_MACHDEP_UNMANAGEDMEM, NULL, NULL, NULL, NULL);
}
}
return rv;
}