#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: m68k_machdep.c,v 1.24 2026/05/03 19:10:41 thorpej Exp $");
#include "opt_compat_netbsd.h"
#include "opt_compat_sunos.h"
#include "opt_execfmt.h"
#include "opt_m68k_arch.h"
#include <sys/param.h>
#include <sys/cpu.h>
#include <sys/exec.h>
#include <sys/lwp.h>
#include <sys/proc.h>
#include <sys/msgbuf.h>
#include <sys/reboot.h>
#include <sys/device.h>
#include <sys/vnode.h>
#include <sys/kernel.h>
#include <sys/kcore.h>
#include <dev/cons.h>
#ifdef EXEC_AOUT
#include <sys/exec_aout.h>
#endif
#include <uvm/uvm_extern.h>
#include <m68k/m68k.h>
#include <m68k/kcore.h>
#include <m68k/frame.h>
#include <m68k/pcb.h>
#include <m68k/reg.h>
#ifdef M68060
#include <m68k/pcr.h>
#endif
#include <m68k/seglist.h>
char machine[] = MACHINE;
char machine_arch[] = MACHINE_ARCH;
struct cpu_info cpu_info_store;
int cpuspeed_khz;
#if defined(M68020) || defined(M68030)
int fpuspeed_khz;
#endif
#ifdef M68K_EC
int ecsize;
#endif
phys_seg_list_t phys_seg_list[VM_PHYSSEG_MAX];
#ifndef __HAVE_M68K_PRIVATE_MSGSBUF
paddr_t msgbufpa = (paddr_t)-1;
#endif
void *msgbufaddr;
vaddr_t m68k_usrstack;
void
machine_init_common(paddr_t nextpa)
{
extern paddr_t avail_start, avail_end;
int i;
#ifndef __HAVE_M68K_PRIVATE_MSGSBUF
int end_seg = 0;
#endif
avail_start = UINT_MAX;
avail_end = 0;
for (i = 0; i < VM_PHYSSEG_MAX; i++) {
phys_seg_list[i].ps_start =
m68k_round_page(phys_seg_list[i].ps_start);
phys_seg_list[i].ps_end =
m68k_trunc_page(phys_seg_list[i].ps_end);
if (phys_seg_list[i].ps_avail_start == 0 &&
phys_seg_list[i].ps_avail_end == 0) {
phys_seg_list[i].ps_avail_start =
phys_seg_list[i].ps_start;
phys_seg_list[i].ps_avail_end =
phys_seg_list[i].ps_end;
}
if (phys_seg_list[i].ps_start == phys_seg_list[i].ps_end) {
continue;
}
if (nextpa >= phys_seg_list[i].ps_start &&
nextpa <= phys_seg_list[i].ps_end &&
nextpa > phys_seg_list[i].ps_avail_start) {
phys_seg_list[i].ps_avail_start = nextpa;
}
if (phys_seg_list[i].ps_avail_start ==
phys_seg_list[i].ps_avail_end) {
continue;
}
if (phys_seg_list[i].ps_avail_start < avail_start) {
avail_start = phys_seg_list[i].ps_avail_start;
}
if (phys_seg_list[i].ps_avail_end > avail_end) {
avail_end = phys_seg_list[i].ps_avail_end;
#ifndef __HAVE_M68K_PRIVATE_MSGSBUF
end_seg = i;
#endif
}
}
#ifndef __HAVE_M68K_PRIVATE_MSGSBUF
if (msgbufpa == (paddr_t)-1) {
KASSERT((phys_seg_list[end_seg].ps_avail_end
- phys_seg_list[end_seg].ps_avail_start)
>= round_page(MSGBUFSIZE));
phys_seg_list[end_seg].ps_avail_end -= round_page(MSGBUFSIZE);
msgbufpa = phys_seg_list[end_seg].ps_avail_end;
}
#endif
#ifndef VM_PHYS_SEG_TO_FREELIST
#define VM_PHYS_SEG_TO_FREELIST(s) VM_FREELIST_DEFAULT
#endif
for (i = 0; i < VM_PHYSSEG_MAX; i++) {
if (phys_seg_list[i].ps_avail_start ==
phys_seg_list[i].ps_avail_end) {
continue;
}
uvm_page_physload(atop(phys_seg_list[i].ps_avail_start),
atop(phys_seg_list[i].ps_avail_end),
atop(phys_seg_list[i].ps_avail_start),
atop(phys_seg_list[i].ps_avail_end),
VM_PHYS_SEG_TO_FREELIST(i));
}
#ifndef __HAVE_M68K_PRIVATE_MSGSBUF
for (i = 0; i < btoc(round_page(MSGBUFSIZE)); i++) {
pmap_kenter_pa((vaddr_t)msgbufaddr + i * PAGE_SIZE,
msgbufpa + i * PAGE_SIZE,
VM_PROT_READ|VM_PROT_WRITE, 0);
}
pmap_update(pmap_kernel());
#endif
initmsgbuf(msgbufaddr, round_page(MSGBUFSIZE));
}
void
cpu_startup_common(void)
{
vaddr_t minaddr, maxaddr;
char pbuf[9];
m68k_usrstack = USRSTACK;
fpu_init();
machine_set_model();
printf("%s%s", copyright, version);
#ifdef __HAVE_CPU_STARTUP_PRINT_MACHINE_MODEL
cpu_startup_print_machine_model(printf);
#endif
#ifdef __HAVE_CPU_STARTUP_PRINT_TOTAL_MEMORY
cpu_startup_print_total_memory(printf);
#else
format_bytes(pbuf, sizeof(pbuf), ctob(physmem));
printf("total memory = %s\n", pbuf);
#endif
format_bytes(pbuf, sizeof(pbuf), ptoa(uvm_availmem(false)));
printf("avail memory = %s\n", pbuf);
minaddr = 0;
phys_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr,
VM_PHYS_SIZE, 0, false, NULL);
}
__weak_alias(cpu_startup, cpu_startup_common);
static const char *
mhz_string_from_khz(int speed_khz, char *buf, size_t bufsize)
{
int whole_mhz = speed_khz / 1000;
int frac_mhz = (speed_khz % 1000) / 10;
if ((frac_mhz % 10) == 0) {
frac_mhz /= 10;
}
if (frac_mhz == 0) {
snprintf(buf, bufsize, " @ %dMHz", whole_mhz);
} else {
snprintf(buf, bufsize, " @ %d.%dMHz", whole_mhz, frac_mhz);
}
return buf;
}
void
cpu_startup_print_machine_model(void (*pr)(const char *, ...)
__printflike(1, 2))
{
char speed_str[sizeof("@ xxx.xxxMHz")] = { 0 };
#ifdef __HAVE_M68K_MACHINE_PRINT_MODEL
machine_print_model(pr);
#else
(*pr)("%s\n", cpu_getmodel());
#endif
switch (cputype) {
#ifdef M68010
case CPU_68010:
(*pr)("MC68010 CPU");
break;
#endif
#ifdef M68020
case CPU_68020:
(*pr)("MC68020 CPU");
break;
#endif
#ifdef M68030
case CPU_68030:
(*pr)("MC68030 CPU+MMU");
break;
#endif
#ifdef M68040
case CPU_68040:
(*pr)("MC68040 CPU+MMU");
break;
#endif
#ifdef M68060
case CPU_68060: {
u_int pcr = get_pcr();
(*pr)("MC68%s060 rev.%d CPU+MMU",
(PCR_ID(pcr) & 1) ? "LC" : "",
(int)PCR_REVISION(pcr));
if (pcr & PCR_DFP) {
(*pr)("+FPU(disabled)");
}
break;
}
#endif
default:
(*pr)("unknown CPU type\n");
panic("startup");
}
#if defined(M68040) || defined(M68060)
switch (fputype) {
case FPU_68040:
case FPU_68060:
(*pr)("+FPU");
break;
default:
break;
}
#endif
if (cpuspeed_khz) {
(*pr)("%s",
mhz_string_from_khz(cpuspeed_khz,
speed_str, sizeof(speed_str)));
}
switch (mmutype) {
#ifdef M68K_MMU_68851
case MMU_68851:
(*pr)(", MC68851 MMU");
break;
#endif
#ifdef M68K_MMU_HP
case MMU_HP:
(*pr)(", HP MMU");
break;
#endif
#ifdef M68K_MMU_SUN
case MMU_SUN:
(*pr)(", Sun MMU");
break;
#endif
#ifdef M68K_MMU_CUSTOM
case MMU_CUSTOM:
(*pr)(", custom MMU");
break;
#endif
default:
break;
}
switch (fputype) {
#ifdef FPU_EMULATE
case FPU_NONE:
(*pr)(", emulated FPU");
break;
#endif
#if defined(M68020) || defined(M68030)
case FPU_68881:
(*pr)(", MC68881 FPU");
break;
case FPU_68882:
(*pr)(", MC68882 FPU");
break;
#endif
default:
break;
}
#if defined(M68020) || defined(M68030)
if (fpuspeed_khz != 0 && fpuspeed_khz != cpuspeed_khz) {
(*pr)("%s",
mhz_string_from_khz(fpuspeed_khz,
speed_str, sizeof(speed_str)));
}
#endif
switch (cputype) {
#if defined(M68040) || defined(M68060)
case CPU_68040:
case CPU_68060:
(*pr)(", %dK+%dK on-chip I/D caches",
cputype == CPU_68040 ? 4 : 8,
cputype == CPU_68040 ? 4 : 8);
break;
#endif
default:
#ifdef M68K_EC
if (ectype != EC_NONE) {
(*pr)("\nExternal ");
if (ecsize >= 1024) {
(*pr)("%dK ", ecsize / 1024);
}
(*pr)("%s-address cache",
ectype == EC_PHYS ? "physical" : "virtual");
}
#endif
break;
}
(*pr)("\n");
}
int
mm_md_physacc_regular(paddr_t pa, vm_prot_t prot)
{
int i;
for (i = 0; i < VM_PHYSSEG_MAX; i++) {
if (phys_seg_list[i].ps_start == phys_seg_list[i].ps_end) {
continue;
}
if (pa < phys_seg_list[i].ps_start) {
continue;
}
if (pa >= phys_seg_list[i].ps_end) {
continue;
}
return 0;
}
return EFAULT;
}
int
cpu_reboot_poll_console(bool wait)
{
int rv;
cnpollc(true);
while ((rv = cngetc()) == 0 && wait) {
delay(100000);
}
cnpollc(false);
return rv;
}
void
machine_powerdown_default(void)
{
}
__weak_alias(machine_powerdown,machine_powerdown_default);
void
machine_halt_default(void)
{
printf("Please press any key to reboot.\n");
cpu_reboot_poll_console(true);
}
__weak_alias(machine_halt,machine_halt_default);
int waittime = -1;
void
bootsync(void)
{
if (waittime < 0) {
waittime = 0;
vfs_shutdown();
}
}
void
cpu_reboot_common(int howto, char *bootstr)
{
struct pcb *pcb = lwp_getpcb(curlwp);
if (pcb != NULL) {
savectx(pcb);
}
if (cold) {
howto |= RB_HALT;
goto haltsys;
}
cnpollc(true);
boothowto = howto;
if ((howto & RB_NOSYNC) == 0) {
bootsync();
}
splhigh();
if (howto & RB_DUMP) {
dumpsys();
}
haltsys:
doshutdownhooks();
pmf_system_shutdown(boothowto);
#if defined(PANICWAIT) && !defined(DDB)
if ((howto & RB_HALT) == 0 && panicstr) {
printf("hit any key to reboot...\n");
cpu_reboot_poll_console(false);
printf("\n");
}
#endif
if ((howto & RB_POWERDOWN) == RB_POWERDOWN) {
machine_powerdown();
}
if (howto & RB_HALT) {
printf("The operating system has halted.\n");
machine_halt();
}
printf("rebooting...\n");
delay(1000000);
machine_reboot(howto, bootstr);
printf("WARNING: system reboot failed, holding here.\n\n");
for (;;) {
}
}
__weak_alias(cpu_reboot,cpu_reboot_common);
__weak_alias(mm_md_physacc, mm_md_physacc_regular);
void
setregs(struct lwp *l, struct exec_package *pack, vaddr_t stack)
{
struct trapframe *tf = (struct trapframe *)l->l_md.md_regs;
struct pcb *pcb = lwp_getpcb(l);
memset(tf, 0, sizeof(*tf));
tf->tf_sr = PSL_USERSET;
tf->tf_pc = pack->ep_entry & ~1;
tf->tf_regs[D0] = 0;
tf->tf_regs[D1] = 0;
tf->tf_regs[D2] = 0;
tf->tf_regs[D3] = 0;
tf->tf_regs[D4] = 0;
tf->tf_regs[D5] = 0;
tf->tf_regs[D6] = 0;
tf->tf_regs[D7] = 0;
tf->tf_regs[A0] = 0;
tf->tf_regs[A1] = 0;
tf->tf_regs[A2] = l->l_proc->p_psstrp;
tf->tf_regs[A3] = 0;
tf->tf_regs[A4] = 0;
tf->tf_regs[A5] = 0;
tf->tf_regs[A6] = 0;
tf->tf_regs[SP] = stack;
pcb->pcb_fpregs.fpf_null = 0;
#if !defined(__mc68010__)
if (fputype)
m68881_restore(&pcb->pcb_fpregs);
#endif
#ifdef COMPAT_SUNOS
l->l_md.md_flags = 0;
#endif
}
#ifdef EXEC_AOUT
int
cpu_exec_aout_makecmds(struct lwp *l, struct exec_package *epp)
{
#ifdef __mc68010__
return ENOEXEC;
#elif defined(COMPAT_NOMID) || defined(COMPAT_44)
struct exec *execp = epp->ep_hdr;
u_long midmag, magic;
u_short mid;
int error;
midmag = ntohl(execp->a_midmag);
mid = (midmag >> 16) & 0xffff;
magic = midmag & 0xffff;
midmag = mid << 16 | magic;
switch (midmag) {
#ifdef COMPAT_NOMID
case (MID_ZERO << 16) | ZMAGIC:
error = exec_aout_prep_oldzmagic(l, epp);
break;
#endif
#ifdef COMPAT_44
case (MID_HP300 << 16) | ZMAGIC:
error = exec_aout_prep_oldzmagic(l, epp);
break;
#endif
default:
error = ENOEXEC;
}
return error;
#else
return ENOEXEC;
#endif
}
#endif