/* $NetBSD: machdep.c,v 1.112.4.2 2023/02/12 11:47:10 martin Exp $ */ /* * Copyright (c) 1998 Darrin B. Jewell * Copyright (c) 1988 University of Utah. * Copyright (c) 1982, 1986, 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: Utah $Hdr: machdep.c 1.74 92/12/20$ * * @(#)machdep.c 8.10 (Berkeley) 4/20/94 */ #include __KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.112.4.2 2023/02/12 11:47:10 martin Exp $"); #include "opt_ddb.h" #include "opt_kgdb.h" #include "opt_modular.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for MID_* */ #include #include #include #include #include #include #ifdef KGDB #include #endif #include #include #include #ifdef DDB #include #include #include #include #endif #ifdef KGDB /* Is zs configured in? */ #include "zsc.h" #if (NZSC > 0) #include #endif #endif #include #include #include #include #include #include #include #include #include #include /* XXX should be pulled in by sys/kcore.h */ #include #include #include #include #include #include "ksyms.h" int nsym; char *ssym; extern char *esym; #define MAXMEM 64*1024 /* XXX - from cmap.h */ /* the following is used externally (sysctl_hw) */ char machine[] = MACHINE; /* from */ /* Our exported CPU info; we can have only one. */ struct cpu_info cpu_info_store; struct vm_map *phys_map = NULL; paddr_t msgbufpa; /* PA of message buffer */ int maxmem; /* max memory per process */ extern u_int lowram; extern short exframesize[]; /* prototypes for local functions */ void identifycpu(void); void initcpu(void); void dumpsys(void); int cpu_dumpsize(void); int cpu_dump(int (*)(dev_t, daddr_t, void *, size_t), daddr_t *); void cpu_init_kcore_hdr(void); /* functions called from locore.s */ void next68k_init(void); void straytrap(int, u_short); /* * Machine-independent crash dump header info. */ cpu_kcore_hdr_t cpu_kcore_hdr; /* * Memory segments initialized in locore, which are eventually loaded * as managed VM pages. */ phys_seg_list_t phys_seg_list[VM_PHYSSEG_MAX]; /* * Memory segments to dump. This is initialized from the phys_seg_list * before pages are stolen from it for VM system overhead. I.e. this * covers the entire range of physical memory. */ phys_ram_seg_t mem_clusters[VM_PHYSSEG_MAX]; int mem_cluster_cnt; /* * On the 68020/68030, the value of delay_divisor is roughly * 2048 / cpuspeed (where cpuspeed is in MHz). * * On the 68040/68060(?), the value of delay_divisor is roughly * 759 / cpuspeed (where cpuspeed is in MHz). * XXX -- is the above formula correct? */ int cpuspeed = 33; /* relative cpu speed; XXX skewed on 68040 */ int delay_divisor = 759 / 33; /* delay constant; assume fastest 33 MHz */ /****************************************************************/ /* * Early initialization, before main() is called. */ void next68k_init(void) { int i; /* * Tell the VM system about available physical memory. */ for (i = 0; i < mem_cluster_cnt; i++) { if (phys_seg_list[i].ps_start == phys_seg_list[i].ps_end) { /* * Segment has been completely gobbled up. */ continue; } /* * Note the index of the mem cluster is the free * list we want to put the memory on. */ uvm_page_physload(atop(phys_seg_list[i].ps_start), atop(phys_seg_list[i].ps_end), atop(phys_seg_list[i].ps_start), atop(phys_seg_list[i].ps_end), VM_FREELIST_DEFAULT); } { char *p = rom_boot_arg; boothowto = 0; if (*p++ == '-') { for (;*p;p++) BOOT_FLAG(*p, boothowto); } } /* * Initialize error message buffer (at end of core). */ for (i = 0; i < btoc(round_page(MSGBUFSIZE)); i++) pmap_enter(pmap_kernel(), (vaddr_t)msgbufaddr + i * PAGE_SIZE, msgbufpa + i * PAGE_SIZE, VM_PROT_READ|VM_PROT_WRITE, VM_PROT_READ|VM_PROT_WRITE|PMAP_WIRED); initmsgbuf(msgbufaddr, round_page(MSGBUFSIZE)); pmap_update(pmap_kernel()); } /* * Console initialization: called early on from main, * before vm init or startup. Do enough configuration * to choose and initialize a console. */ void consinit(void) { static int init = 0; /* * Generic console: sys/dev/cons.c * Initializes either ite or ser as console. * Can be called from locore.s and init_main.c. */ if (!init) { cninit(); #if defined(KGDB) && (NZSC > 0) zs_kgdb_init(); #endif #if NKSYMS || defined(DDB) || defined(MODULAR) /* Initialize kernel symbol table, if compiled in. */ ksyms_addsyms_elf(nsym, ssym, esym); #endif if (boothowto & RB_KDB) { #if defined(KGDB) kgdb_connect(1); #elif defined(DDB) Debugger(); #endif } init = 1; } } /* * cpu_startup: allocate memory for variable-sized tables, * initialize CPU, and do autoconfiguration. */ void cpu_startup(void) { vaddr_t minaddr, maxaddr; char pbuf[9]; #ifdef DEBUG extern int pmapdebug; int opmapdebug = pmapdebug; pmapdebug = 0; #endif if (fputype != FPU_NONE) m68k_make_fpu_idle_frame(); /* * Initialize the kernel crash dump header. */ cpu_init_kcore_hdr(); /* * Good {morning,afternoon,evening,night}. */ printf("%s%s", copyright, version); identifycpu(); format_bytes(pbuf, sizeof(pbuf), ctob(physmem)); printf("total memory = %s\n", pbuf); minaddr = 0; /* * Allocate a submap for physio */ phys_map = uvm_km_suballoc(kernel_map, &minaddr, &maxaddr, VM_PHYS_SIZE, 0, false, NULL); #ifdef DEBUG pmapdebug = opmapdebug; #endif format_bytes(pbuf, sizeof(pbuf), ptoa(uvmexp.free)); printf("avail memory = %s\n", pbuf); /* * Set up CPU-specific registers, cache, etc. */ initcpu(); } void identifycpu(void) { const char *cpu_str, *mmu_str, *fpu_str, *cache_str; extern int turbo; /* * ...and the CPU type. */ switch (cputype) { case CPU_68040: cpu_str = "MC68040"; cpuspeed = turbo ? 33 : 25; delay_divisor = 759 / cpuspeed; break; case CPU_68030: cpu_str = "MC68030"; cpuspeed = 25; delay_divisor = 2048 / cpuspeed; break; #if 0 case CPU_68020: cpu_str = "MC68020"; break; #endif default: printf("\nunknown cputype %d\n", cputype); panic("startup"); } /* * ...and the MMU type. */ switch (mmutype) { case MMU_68040: case MMU_68030: mmu_str = "+MMU"; break; #if 0 case MMU_68851: mmu_str = ", MC68851 MMU"; break; #endif default: printf("%s: unknown MMU type %d\n", cpu_str, mmutype); panic("startup"); } /* * ...and the FPU type. */ switch (fputype) { case FPU_68040: fpu_str = "+FPU"; break; case FPU_68882: fpu_str = ", MC68882 FPU"; break; case FPU_68881: fpu_str = ", MC68881 FPU"; break; default: fpu_str = ", unknown FPU"; } /* * ...and finally, the cache type. */ if (cputype == CPU_68040) cache_str = ", 4k on-chip physical I/D caches"; else cache_str = ""; cpu_setmodel("NeXT/%s CPU%s%s%s", cpu_str, mmu_str, fpu_str, cache_str); printf("%s\n", cpu_getmodel()); } /* * machine dependent system variables. */ 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); #if 0 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); #endif } /* See: sig_machdep.c */ int waittime = -1; void cpu_reboot(int howto, char *bootstr) { struct pcb *pcb = lwp_getpcb(curlwp); /* take a snap shot before clobbering any registers */ if (pcb != NULL) savectx(pcb); /* If system is cold, just halt. */ if (cold) { howto |= RB_HALT; goto haltsys; } boothowto = howto; if ((howto & RB_NOSYNC) == 0 && waittime < 0) { waittime = 0; vfs_shutdown(); /* * If we've been adjusting the clock, the todr * will be out of synch; adjust it now. */ resettodr(); } /* Disable interrupts. */ splhigh(); /* If rebooting and a dump is requested, do it. */ if (howto & RB_DUMP) dumpsys(); haltsys: /* Run any shutdown hooks. */ doshutdownhooks(); pmf_system_shutdown(boothowto); #if defined(PANICWAIT) && !defined(DDB) if ((howto & RB_HALT) == 0 && panicstr) { printf("hit any key to reboot...\n"); (void)cngetc(); printf("\n"); } #endif if ((howto & RB_POWERDOWN) == RB_POWERDOWN) { poweroff(); } /* Finally, halt/reboot the system. */ if (howto & RB_HALT) { monbootflag = 0x2d680000; /* "-h" */ } printf("rebooting...\n"); DELAY(1000000); doboot(); /*NOTREACHED*/ } /* * Initialize the kernel crash dump header. */ void cpu_init_kcore_hdr(void) { cpu_kcore_hdr_t *h = &cpu_kcore_hdr; struct m68k_kcore_hdr *m = &h->un._m68k; int i; extern char end[]; memset(&cpu_kcore_hdr, 0, sizeof(cpu_kcore_hdr)); /* * Initialize the `dispatcher' portion of the header. */ strcpy(h->name, machine); h->page_size = PAGE_SIZE; h->kernbase = KERNBASE; /* * Fill in information about our MMU configuration. */ 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; /* * Initialize pointer to kernel segment table. */ m->sysseg_pa = (u_int32_t)(pmap_kernel()->pm_stpa); /* * Initialize relocation value such that: * * pa = (va - KERNBASE) + reloc */ m->reloc = lowram; /* * Define the end of the relocatable range. */ m->relocend = (u_int32_t)end; /* * The next68k has multiple memory segments. */ for (i = 0; i < mem_cluster_cnt; i++) { m->ram_segs[i].start = mem_clusters[i].start; m->ram_segs[i].size = mem_clusters[i].size; } } /* * Compute the size of the machine-dependent crash dump header. * Returns size in disk blocks. */ #define CHDRSIZE (ALIGN(sizeof(kcore_seg_t)) + ALIGN(sizeof(cpu_kcore_hdr_t))) #define MDHDRSIZE roundup(CHDRSIZE, dbtob(1)) int cpu_dumpsize(void) { return btodb(MDHDRSIZE); } /* * Called by dumpsys() to dump the machine-dependent header. */ int cpu_dump(int (*dump)(dev_t, daddr_t, void *, size_t), daddr_t *blknop) { int buf[MDHDRSIZE / sizeof(int)]; cpu_kcore_hdr_t *chdr; kcore_seg_t *kseg; int error; kseg = (kcore_seg_t *)buf; chdr = (cpu_kcore_hdr_t *)&buf[ALIGN(sizeof(kcore_seg_t)) / sizeof(int)]; /* Create the segment header. */ CORE_SETMAGIC(*kseg, KCORE_MAGIC, MID_MACHINE, CORE_CPU); kseg->c_size = MDHDRSIZE - ALIGN(sizeof(kcore_seg_t)); memcpy(chdr, &cpu_kcore_hdr, sizeof(cpu_kcore_hdr_t)); error = (*dump)(dumpdev, *blknop, (void *)buf, sizeof(buf)); *blknop += btodb(sizeof(buf)); return (error); } /* * These variables are needed by /sbin/savecore */ u_int32_t dumpmag = 0x8fca0101; /* magic number */ int dumpsize = 0; /* pages */ long dumplo = 0; /* blocks */ /* * This is called by main to set dumplo and dumpsize. * Dumps always skip the first PAGE_SIZE of disk space * in case there might be a disk label stored there. * If there is extra space, put dump at the end to * reduce the chance that swapping trashes it. */ void cpu_dumpconf(void) { int chdrsize; /* size of dump header */ int nblks; /* size of dump area */ if (dumpdev == NODEV) return; nblks = bdev_size(dumpdev); chdrsize = cpu_dumpsize(); dumpsize = btoc(cpu_kcore_hdr.un._m68k.ram_segs[0].size); /* * Check do see if we will fit. Note we always skip the * first PAGE_SIZE in case there is a disk label there. */ if (nblks < (ctod(dumpsize) + chdrsize + ctod(1))) { dumpsize = 0; dumplo = -1; return; } /* * Put dump at the end of the partition. */ dumplo = (nblks - 1) - ctod(dumpsize) - chdrsize; } /* * Dump physical memory onto the dump device. Called by cpu_reboot(). */ void dumpsys(void) { const struct bdevsw *bdev; daddr_t blkno; /* current block to write */ /* dump routine */ int (*dump)(dev_t, daddr_t, void *, size_t); int pg; /* page being dumped */ vm_offset_t maddr; /* PA being dumped */ int error; /* error code from (*dump)() */ /* XXX initialized here because of gcc lossage */ maddr = lowram; pg = 0; /* Don't put dump messages in msgbuf. */ msgbufmapped = 0; /* Make sure dump device is valid. */ if (dumpdev == NODEV) return; bdev = bdevsw_lookup(dumpdev); if (bdev == NULL) return; if (dumpsize == 0) { cpu_dumpconf(); if (dumpsize == 0) return; } if (dumplo < 0) return; dump = bdev->d_dump; blkno = dumplo; printf("\ndumping to dev 0x%"PRIx64", offset %ld\n", dumpdev, dumplo); printf("dump "); /* Write the dump header. */ error = cpu_dump(dump, &blkno); if (error) goto bad; for (pg = 0; pg < dumpsize; pg++) { #define NPGMB (1024*1024/PAGE_SIZE) /* print out how many MBs we have dumped */ if (pg && (pg % NPGMB) == 0) printf_nolog("%d ", pg / NPGMB); #undef NPGMB pmap_enter(pmap_kernel(), (vm_offset_t)vmmap, maddr, VM_PROT_READ, VM_PROT_READ|PMAP_WIRED); pmap_update(pmap_kernel()); error = (*dump)(dumpdev, blkno, vmmap, PAGE_SIZE); bad: switch (error) { case 0: maddr += PAGE_SIZE; blkno += btodb(PAGE_SIZE); break; case ENXIO: printf("device bad\n"); return; case EFAULT: printf("device not ready\n"); return; case EINVAL: printf("area improper\n"); return; case EIO: printf("i/o error\n"); return; case EINTR: printf("aborted from console\n"); return; default: printf("error %d\n", error); return; } } printf("succeeded\n"); } void initcpu(void) { } void straytrap(int pc, u_short evec) { printf("unexpected trap (vector offset %x) from %x\n", evec & 0xFFF, pc); /* XXX kgdb/ddb entry? */ } /* XXX should change the interface, and make one badaddr() function */ int *nofault; #if 0 int badaddr(void *addr, int nbytes) { int i; label_t faultbuf; #ifdef lint i = *addr; if (i) return (0); #endif nofault = (int *) &faultbuf; if (setjmp((label_t *)nofault)) { nofault = (int *) 0; return(1); } switch (nbytes) { case 1: i = *(volatile char *)addr; break; case 2: i = *(volatile short *)addr; break; case 4: i = *(volatile int *)addr; break; default: panic("badaddr: bad request"); } nofault = NULL; return (0); } #endif /* * Level 7 interrupts can be caused by the keyboard or parity errors. */ int nmihand(void *frame) { static int innmihand; /* simple mutex */ /* Prevent unwanted recursion. */ if (innmihand) return 0; innmihand = 1; printf("Got a NMI"); if (!INTR_OCCURRED(NEXT_I_NMI)) { printf("But NMI isn't set in intrstat!\n"); } INTR_DISABLE(NEXT_I_NMI); #if defined(DDB) printf(": entering debugger\n"); Debugger(); printf("continuing after NMI\n"); #elif defined(KGDB) kgdb_connect(1); #else printf(": ignoring\n"); #endif /* DDB */ INTR_ENABLE(NEXT_I_NMI); innmihand = 0; return 0; } /* * cpu_exec_aout_makecmds(): * CPU-dependent a.out format hook for execve(). * * Determine of the given exec package refers to something which we * understand and, if so, set up the vmcmds for it. */ int cpu_exec_aout_makecmds(struct lwp *l, struct exec_package *epp) { return ENOEXEC; } int mm_md_physacc(paddr_t pa, vm_prot_t prot) { return (pa < lowram || pa >= 0xfffffffc) ? EFAULT : 0; } #ifdef MODULAR /* * Push any modules loaded by the bootloader etc. */ void module_init_md(void) { } #endif