#include "opt_ddb.h"
#include "opt_fpu_emulate.h"
#include "opt_lev6_defer.h"
#include "opt_m060sp.h"
#include "opt_modular.h"
#include "opt_panicbutton.h"
#include "opt_m68k_arch.h"
#include "empm.h"
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.280 2026/04/28 03:29:09 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/callout.h>
#include <sys/signalvar.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/vnode.h>
#include <sys/device.h>
#include <sys/queue.h>
#include <sys/mount.h>
#include <sys/core.h>
#include <sys/kcore.h>
#include <sys/ksyms.h>
#include <sys/module.h>
#include <sys/cpu.h>
#include <sys/exec.h>
#if defined(DDB) && defined(__ELF__)
#include <sys/exec_elf.h>
#endif
#undef PS
#define MAXMEM 64*1024
#include <uvm/uvm_extern.h>
#include <sys/sysctl.h>
#include <machine/db_machdep.h>
#include <ddb/db_sym.h>
#include <ddb/db_extern.h>
#include <machine/reg.h>
#include <machine/pcb.h>
#include <machine/psl.h>
#include <machine/pte.h>
#include <machine/kcore.h>
#include <machine/vectors.h>
#include <dev/cons.h>
#include <dev/mm.h>
#include <amiga/amiga/isr.h>
#include <amiga/amiga/custom.h>
#ifdef DRACO
#include <amiga/amiga/drcustom.h>
#include <m68k/include/asm_single.h>
#endif
#include <amiga/amiga/cia.h>
#include <amiga/amiga/cc.h>
#include <amiga/amiga/memlist.h>
#include <amiga/amiga/device.h>
#if NEMPM > 0
#include <amiga/pci/empmvar.h>
#endif
#ifdef M68060
#include <m68k/pcr.h>
#endif
#include "fd.h"
#include "ser.h"
#include "ksyms.h"
vm_offset_t reserve_dumppages(vm_offset_t);
void dumpsys(void);
void intrhand(int);
#if NSER > 0
void ser_outintr(void);
#endif
#if NFD > 0
void fdintr(int);
#endif
volatile unsigned int intr_depth = 0;
int machineid;
int maxmem;
extern int freebufspace;
extern u_int lowram;
int ser_open_speed;
#ifdef DRACO
vaddr_t DRCCADDR;
volatile u_int8_t *draco_intena, *draco_intpen, *draco_intfrc;
volatile u_int8_t *draco_misc;
volatile struct drioct *draco_ioct;
#endif
struct pmap_bootmap machine_bootmap[] = {
{ .pmbm_vaddr = -1 },
};
void
consinit(void)
{
#ifdef DRACO
if (is_draco()) {
} else
#endif
custom_chips_init();
config_console();
cninit();
#if NKSYMS || defined(DDB) || defined(MODULAR)
{
extern int end[];
extern int *esym;
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();
if (memlist->m_nseg > 0 && memlist->m_nseg < 16) {
for (u_int i = 0; i < memlist->m_nseg; i++) {
printf("memory segment %d at %08x size %08x\n", i,
memlist->m_seg[i].ms_start,
memlist->m_seg[i].ms_size);
}
}
}
void
machine_set_model(void)
{
const char *mach;
#ifdef DRACO
char machbuf[16];
if (is_draco()) {
snprintf(machbuf, sizeof(machbuf), "DraCo rev.%d", is_draco());
mach = machbuf;
} else
#endif
if (is_a4000())
mach = "Amiga 4000";
else if (is_a3000())
mach = "Amiga 3000";
else if (is_a1200())
mach = "Amiga 1200";
else if (is_a600())
mach = "Amiga 600";
else
mach = "Amiga 500/2000";
cpu_setmodel("%s", mach);
}
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)
{
#if NEMPM > 0
device_t empmdev = device_find_by_xname("empm0");
if (empmdev != NULL) {
empm_power_off(device_private(empmdev));
}
#endif
}
u_int32_t dumpmag = 0x8fca0101;
int dumpsize = 0;
long dumplo = 0;
cpu_kcore_hdr_t cpu_kcore_hdr;
#define CHDRSIZE (ALIGN(sizeof(kcore_seg_t)) + ALIGN(sizeof(cpu_kcore_hdr_t)))
#define MDHDRSIZE roundup(CHDRSIZE, dbtob(1))
void
cpu_dumpconf(void)
{
cpu_kcore_hdr_t *h = &cpu_kcore_hdr;
struct m68k_kcore_hdr *m = &h->un._m68k;
int nblks;
int i;
extern int end[];
memset(&cpu_kcore_hdr, 0, sizeof(cpu_kcore_hdr));
strcpy(h->name, machine);
h->page_size = PAGE_SIZE;
h->kernbase = KERNBASE;
m->mmutype = mmutype;
m->sg_v = SG_V;
m->sg_frame = SG_FRAME;
m->sg_ishift = SG_ISHIFT;
m->sg_pmask = SG_PMASK;
m->sg40_shift1 = SG4_SHIFT1;
m->sg40_mask2 = SG4_MASK2;
m->sg40_shift2 = SG4_SHIFT2;
m->sg40_mask3 = SG4_MASK3;
m->sg40_shift3 = SG4_SHIFT3;
m->sg40_addr1 = SG4_ADDR1;
m->sg40_addr2 = SG4_ADDR2;
m->pg_v = PG_V;
m->pg_frame = PG_FRAME;
m->sysseg_pa = (paddr_t)pmap_kernel()->pm_stpa;
m->reloc = lowram;
m->relocend = (u_int32_t)&end;
dumpsize = physmem;
m->ram_segs[0].start = lowram;
m->ram_segs[0].size = ctob(physmem);
for (i = 0; i < memlist->m_nseg; i++) {
if ((memlist->m_seg[i].ms_attrib & MEMF_CHIP) == 0)
continue;
dumpsize += btoc(memlist->m_seg[i].ms_size);
m->ram_segs[1].start = 0;
m->ram_segs[1].size = memlist->m_seg[i].ms_size;
break;
}
if (bdevsw_lookup(dumpdev) == NULL) {
dumpdev = NODEV;
return;
}
nblks = bdev_size(dumpdev);
if (nblks > 0) {
if (dumpsize > btoc(dbtob(nblks - dumplo)))
dumpsize = btoc(dbtob(nblks - dumplo));
else if (dumplo == 0)
dumplo = nblks - btodb(ctob(dumpsize));
}
dumplo -= ctod(btoc(MDHDRSIZE));
if (dumplo < btodb(PAGE_SIZE))
dumplo = btodb(PAGE_SIZE);
}
static vm_offset_t dumpspace;
vm_offset_t
reserve_dumppages(vm_offset_t p)
{
dumpspace = p;
return (p + PAGE_SIZE);
}
void
dumpsys(void)
{
unsigned bytes, i, n, seg;
int maddr, psize;
daddr_t blkno;
int (*dump)(dev_t, daddr_t, void *, size_t);
int error = 0;
kcore_seg_t *kseg_p;
cpu_kcore_hdr_t *chdr_p;
char dump_hdr[MDHDRSIZE];
const struct bdevsw *bdev;
if (dumpdev == NODEV)
return;
bdev = bdevsw_lookup(dumpdev);
if (bdev == NULL || bdev->d_psize == NULL)
return;
if (dumpsize == 0)
cpu_dumpconf();
if (dumplo <= 0) {
printf("\ndump to dev %u,%u not possible\n", major(dumpdev),
minor(dumpdev));
return;
}
printf("\ndumping to dev %u,%u offset %ld\n", major(dumpdev),
minor(dumpdev), dumplo);
psize = bdev_size(dumpdev);
printf("dump ");
if (psize == -1) {
printf("area unavailable.\n");
return;
}
kseg_p = (kcore_seg_t *)dump_hdr;
chdr_p = (cpu_kcore_hdr_t *)&dump_hdr[ALIGN(sizeof(*kseg_p))];
memset(dump_hdr, 0, sizeof(dump_hdr));
CORE_SETMAGIC(*kseg_p, KCORE_MAGIC, MID_MACHINE, CORE_CPU);
kseg_p->c_size = MDHDRSIZE - ALIGN(sizeof(*kseg_p));
*chdr_p = cpu_kcore_hdr;
bytes = ctob(dumpsize);
maddr = cpu_kcore_hdr.un._m68k.ram_segs[0].start;
seg = 0;
blkno = dumplo;
dump = bdev->d_dump;
error = (*dump) (dumpdev, blkno, (void *)dump_hdr, sizeof(dump_hdr));
blkno += btodb(sizeof(dump_hdr));
for (i = 0; i < bytes && error == 0; i += n) {
n = bytes - i;
if (n && (n % (1024 * 1024)) == 0)
printf_nolog("%d ", n / (1024 * 1024));
if (n > PAGE_SIZE)
n = PAGE_SIZE;
if (maddr == 0) {
maddr += PAGE_SIZE;
n -= PAGE_SIZE;
i += PAGE_SIZE;
++blkno;
}
pmap_enter(pmap_kernel(), dumpspace, maddr,
VM_PROT_READ, VM_PROT_READ|PMAP_WIRED);
pmap_update(pmap_kernel());
error = (*dump) (dumpdev, blkno, (void *) dumpspace, n);
if (error)
break;
maddr += n;
blkno += btodb(n);
if (maddr >= (cpu_kcore_hdr.un._m68k.ram_segs[seg].start +
cpu_kcore_hdr.un._m68k.ram_segs[seg].size)) {
++seg;
maddr = cpu_kcore_hdr.un._m68k.ram_segs[seg].start;
if (cpu_kcore_hdr.un._m68k.ram_segs[seg].size == 0)
break;
}
}
switch (error) {
case ENXIO:
printf("device bad\n");
break;
case EFAULT:
printf("device not ready\n");
break;
case EINVAL:
printf("area improper\n");
break;
case EIO:
printf("i/o error\n");
break;
default:
printf("succeeded\n");
break;
}
printf("\n\n");
delay(5000000);
}
struct isr *isr_ports;
#ifdef DRACO
struct isr *isr_slot3;
struct isr *isr_supio;
#endif
struct isr *isr_exter;
void
add_isr(struct isr *isr)
{
struct isr **p, *q;
#ifdef DRACO
switch (isr->isr_ipl) {
case 2:
p = &isr_ports;
break;
case 3:
p = &isr_slot3;
break;
case 5:
p = &isr_supio;
break;
default:
p = &isr_exter;
break;
}
#else
p = isr->isr_ipl == 2 ? &isr_ports : &isr_exter;
#endif
while ((q = *p) != NULL)
p = &q->isr_forw;
isr->isr_forw = NULL;
*p = isr;
#ifdef DRACO
if (is_draco())
switch(isr->isr_ipl) {
case 6:
single_inst_bset_b(*draco_intena, DRIRQ_INT6);
break;
case 2:
single_inst_bset_b(*draco_intena, DRIRQ_INT2);
break;
default:
break;
}
else
#endif
custom.intena = isr->isr_ipl == 2 ?
INTF_SETCLR | INTF_PORTS :
INTF_SETCLR | INTF_EXTER;
}
void
remove_isr(struct isr *isr)
{
struct isr **p, *q;
#ifdef DRACO
switch (isr->isr_ipl) {
case 2:
p = &isr_ports;
break;
case 3:
p = &isr_slot3;
break;
case 5:
p = &isr_supio;
break;
default:
p = &isr_exter;
break;
}
#else
p = isr->isr_ipl == 6 ? &isr_exter : &isr_ports;
#endif
while ((q = *p) != NULL && q != isr)
p = &q->isr_forw;
if (q)
*p = q->isr_forw;
else
panic("remove_isr: handler not registered");
#ifdef DRACO
switch (isr->isr_ipl) {
case 2:
p = &isr_ports;
break;
case 3:
p = &isr_slot3;
break;
case 5:
p = &isr_supio;
break;
case 6:
p = &isr_exter;
break;
}
#else
p = isr->isr_ipl == 6 ? &isr_exter : &isr_ports;
#endif
if (*p == NULL) {
#ifdef DRACO
if (is_draco()) {
switch(isr->isr_ipl) {
case 2:
single_inst_bclr_b(*draco_intena,
DRIRQ_INT2);
break;
case 6:
single_inst_bclr_b(*draco_intena,
DRIRQ_INT6);
break;
default:
break;
}
} else
#endif
custom.intena = isr->isr_ipl == 6 ?
INTF_EXTER : INTF_PORTS;
}
}
void
intrhand(int sr)
{
register unsigned int ipl;
register unsigned short ireq;
register struct isr **p, *q;
ipl = (sr >> 8) & 7;
#ifdef REALLYDEBUG
printf("intrhand: got int. %d\n", ipl);
#endif
#ifdef DRACO
if (is_draco())
ireq = ((ipl == 1) && (*draco_intfrc & DRIRQ_SOFT) ?
INTF_SOFTINT : 0);
else
#endif
ireq = custom.intreqr;
switch (ipl) {
case 1:
#ifdef DRACO
if (is_draco() && (draco_ioct->io_status & DRSTAT_KBDRECV))
drkbdintr();
#endif
if (ireq & INTF_TBE) {
#if NSER > 0
ser_outintr();
#else
custom.intreq = INTF_TBE;
#endif
}
if (ireq & INTF_DSKBLK) {
#if NFD > 0
fdintr(0);
#endif
custom.intreq = INTF_DSKBLK;
}
if (ireq & INTF_SOFTINT) {
#ifdef DEBUG
printf("intrhand: SOFTINT ignored\n");
#endif
custom.intreq = INTF_SOFTINT;
}
break;
case 2:
p = &isr_ports;
while ((q = *p) != NULL) {
if ((q->isr_intr)(q->isr_arg))
break;
p = &q->isr_forw;
}
if (q == NULL)
ciaa_intr ();
#ifdef DRACO
if (is_draco())
single_inst_bclr_b(*draco_intpen, DRIRQ_INT2);
else
#endif
custom.intreq = INTF_PORTS;
break;
#ifdef DRACO
case 6:
p = &isr_exter;
while ((q = *p) != NULL) {
if ((q->isr_intr)(q->isr_arg))
break;
p = &q->isr_forw;
}
single_inst_bclr_b(*draco_intpen, DRIRQ_INT6);
break;
#endif
case 3:
if (ireq & INTF_BLIT)
blitter_handler();
if (ireq & INTF_COPER)
copper_handler();
if (ireq & INTF_VERTB)
vbl_handler();
break;
#ifdef DRACO
case 5:
p = &isr_supio;
while ((q = *p) != NULL) {
if ((q->isr_intr)(q->isr_arg))
break;
p = &q->isr_forw;
}
break;
#endif
#if 0
case 5:
serintr (0);
custom.intreq = INTF_DSKSYNC;
break;
#endif
case 4:
#ifdef DRACO
#include "drsc.h"
if (is_draco())
#if NDRSC > 0
drsc_handler();
#else
single_inst_bclr_b(*draco_intpen, DRIRQ_SCSI);
#endif
else
#endif
audio_handler();
break;
default:
printf("intrhand: unexpected sr 0x%x, intreq = 0x%x\n",
sr, ireq);
break;
}
#ifdef REALLYDEBUG
printf("intrhand: leaving.\n");
#endif
}
bool
cpu_intr_p(void)
{
return intr_depth != 0;
}
#if defined(DEBUG) && !defined(PANICBUTTON)
#define PANICBUTTON
#endif
#ifdef PANICBUTTON
int panicbutton = 1;
int crashandburn = 0;
int candbdelay = 50;
void candbtimer(void);
callout_t candbtimer_ch;
void
candbtimer(void)
{
crashandburn = 0;
}
#endif
#if 0
nmihand(struct frame frame)
{
if (kbdnmi()) {
#ifdef PANICBUTTON
static int innmihand = 0;
if (innmihand == 0) {
innmihand = 1;
printf("Got a keyboard NMI\n");
innmihand = 0;
}
if (panicbutton) {
if (crashandburn) {
crashandburn = 0;
panic(panicstr ?
"forced crash, nosync" : "forced crash");
}
crashandburn++;
callout_reset(&candbtimer_ch, candbdelay,
candbtimer, NULL);
}
#endif
return;
}
if (parityerror(&frame))
return;
printf("unexpected level 7 interrupt ignored\n");
}
#endif
#ifdef MODULAR
int _spllkm6(void);
int _spllkm7(void);
#ifdef LEV6_DEFER
int _spllkm6(void) {
return spl4();
};
int _spllkm7(void) {
return spl4();
};
#else
int _spllkm6(void) {
return spl6();
};
int _spllkm7(void) {
return spl7();
};
#endif
#endif
int ipl2spl_table[_NIPL] = {
[IPL_NONE] = PSL_IPL0|PSL_S,
[IPL_SOFTCLOCK] = PSL_IPL1|PSL_S,
[IPL_VM] = PSL_IPL4|PSL_S,
#if defined(LEV6_DEFER)
[IPL_SCHED] = PSL_IPL4|PSL_S,
[IPL_HIGH] = PSL_IPL4|PSL_S,
#else
[IPL_SCHED] = PSL_IPL6|PSL_S,
[IPL_HIGH] = PSL_IPL7|PSL_S,
#endif
};
int
mm_md_physacc(paddr_t pa, vm_prot_t prot)
{
return (pa >= 0xfffffffc || pa < lowram) ? EFAULT : 0;
}