7488 lines
188 KiB
C
7488 lines
188 KiB
C
#ident "@(#)machdep.c 1.1 94/10/31 SMI"
|
|
|
|
/*
|
|
* Copyright (c) 1989-1991 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/user.h>
|
|
#include <sys/map.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/vm.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/reboot.h>
|
|
#include <sys/vnode.h>
|
|
#include <sys/file.h>
|
|
#include <sys/clist.h>
|
|
#include <sys/callout.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/msgbuf.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/dnlc.h>
|
|
#include <sys/debug.h>
|
|
#include <debug/debug.h>
|
|
#include <sys/dumphdr.h>
|
|
#include <sys/bootconf.h>
|
|
#include <machine/async.h>
|
|
#include <mon/sunromvec.h>
|
|
|
|
/*Placeholder when UFS isn't included, like for DL kernels.*/
|
|
int ufs_lock_released;
|
|
|
|
#ifdef IOMMU
|
|
struct map *sunpcmap;
|
|
#endif
|
|
|
|
/*
|
|
* KMAPBYTES: Maximal size of Kernel SegKMap
|
|
* KMAPBASE - KMAPLIMIT: SegKMap
|
|
* XXX - move to param.h
|
|
*/
|
|
#define KMAPBYTES (16*1024*1024)
|
|
#define KMAPPAGES (KMAPBYTES / MMU_PAGESIZE)
|
|
#define KMAPLIMIT HEAPBASE
|
|
#define KMAPBASE (KMAPLIMIT - KMAPBYTES)
|
|
|
|
/*
|
|
* We may, from time to time, reorganize the layout of
|
|
* the stuff that we organize at the top of memory.
|
|
* This macro "hides" which of the things happens to be
|
|
* at the bottom of this area, so we know (for instance)
|
|
* how many page tables to allocate and so on.
|
|
*/
|
|
#define TOPSEGBASE KMAPBASE
|
|
#define TOPSEGLIMIT BUFLIMIT
|
|
|
|
/*
|
|
* Temporary PTEs are below "topseg", which might
|
|
* not start with the Heap.
|
|
* We appologize for the confusion.
|
|
* XXX - fix this in param.h
|
|
*/
|
|
#undef VA_TMPPTES
|
|
#define VA_TMPPTES (TOPSEGBASE - TMPPTES)
|
|
|
|
#ifndef NOPROM
|
|
union ptpe *tmpptes = 0;
|
|
u_int PA_TMPPTES = 0; /* Use as location for mapping */
|
|
#endif NOPROM
|
|
|
|
#include <os/dlyprf.h>
|
|
typedef void (*voidfunc_t)();
|
|
|
|
/*
|
|
* no telling what kind of pointers we are going
|
|
* to want to convert, so route the translation
|
|
* through this macro. It used to cast to unsigned
|
|
* and mask off some top bits, but now it uses
|
|
* a real routine over in vm_hat that does the
|
|
* mmu probing and figures out what is going on.
|
|
*/
|
|
#define VA2PA(va) va2pa((addr_t)(va))
|
|
extern unsigned va2pa();
|
|
|
|
/*
|
|
* If we know that we are translating
|
|
* a virtual address that is in the contiguous
|
|
* kernel mapping area, we can use CA2PA. This
|
|
* has the benefit that we can use it when the
|
|
* address is not yet mapped in, and econtig
|
|
* has not been extended to include it.
|
|
*/
|
|
#define CA2PA(va) (((u_int)(va)) - KERNELBASE)
|
|
|
|
#include "percpu.h"
|
|
|
|
#ifndef MULTIPROCESSOR
|
|
extern struct user p0uarea;
|
|
#else MULTIPROCESSOR
|
|
extern struct PerCPU cpu0[]; /* locore.s */
|
|
#endif MULTIPROCESSOR
|
|
|
|
#if defined(IPCSEMAPHORE) || defined(IPCMESSAGE) || defined(IPCSHMEM)
|
|
#include <sys/ipc.h>
|
|
#endif IPCSEMAPHORE || IPCMESSAGE || IPCSHMEM
|
|
|
|
#ifdef IPCSEMAPHORE
|
|
#include <sys/sem.h>
|
|
#endif IPCSEMAPHORE
|
|
|
|
#ifdef IPCMESSAGE
|
|
#include <sys/msg.h>
|
|
#endif IPCMESSAGE
|
|
|
|
#ifdef IPCSHMEM
|
|
#include <sys/shm.h>
|
|
#endif IPCSHMEM
|
|
|
|
#include <net/if.h>
|
|
#ifdef UFS
|
|
#include <ufs/inode.h>
|
|
#ifdef QUOTA
|
|
#include <ufs/quota.h>
|
|
#endif QUOTA
|
|
#endif UFS
|
|
#include <sun/consdev.h>
|
|
#include <sun/fault.h>
|
|
#include <machine/frame.h>
|
|
#include <sundev/mbvar.h>
|
|
|
|
#include <machine/psl.h>
|
|
#include <machine/reg.h>
|
|
#include <machine/clock.h>
|
|
#include <machine/pte.h>
|
|
#include <machine/scb.h>
|
|
#include <machine/mmu.h>
|
|
#include <machine/cpu.h>
|
|
#include <machine/asm_linkage.h>
|
|
#include <machine/eeprom.h>
|
|
#include <machine/intreg.h>
|
|
#include <machine/seg_kmem.h>
|
|
#include <machine/buserr.h>
|
|
#include <machine/auxio.h>
|
|
#include <machine/trap.h>
|
|
#include <machine/module.h>
|
|
|
|
#include <mon/openprom.h>
|
|
|
|
#include <vm/hat.h>
|
|
#include <vm/anon.h>
|
|
#include <vm/as.h>
|
|
#include <vm/page.h>
|
|
#include <vm/seg.h>
|
|
#include <vm/seg_map.h>
|
|
#include <vm/seg_vn.h>
|
|
#include <vm/seg_u.h>
|
|
#include <vm/swap.h>
|
|
|
|
#ifdef IOMMU
|
|
#include <machine/iommu.h>
|
|
|
|
/*
|
|
* NOTE: mce 2/14/94 - SunPC merge.
|
|
* SunPC needs a larger DVMA address space than
|
|
* is currently allocated. The best approach would be to switch
|
|
* to using a larger mapping range thus making bigsbusmap much
|
|
* bigger. This runs the risk of breaking some bundled/unbundled
|
|
* device drivers (eg: ledma/le assume they are allocated DVMA
|
|
* in upper 16MB range). So before cleaning up sunpc code check
|
|
* for compatibility issues (esp with SunConnect).
|
|
* This SunPC workaround creates a new DVMA map for exclusive
|
|
* use by SunPC of size 48MB. All other DVMA addresse values
|
|
* remain constant thus avoiding any risk of breaking existing devices.
|
|
* No doubt other devices could benefit from larger DVMA address space.
|
|
*/
|
|
|
|
#undef IOMMU_CTL_RANGE
|
|
#undef IOMMU_DVMA_RANGE
|
|
#undef BIGSBUSMAP_BASE
|
|
#undef BIGSBUSDVMA_BASE
|
|
|
|
/* constants for DVMA */
|
|
#define IOMMU_CTL_RANGE (2) /* 64MB for now */
|
|
#define IOMMU_DVMA_RANGE ((0x1000000) << IOMMU_CTL_RANGE)
|
|
#define IOMMU_DVMA_BASE (0 - IOMMU_DVMA_RANGE)
|
|
#define IOMMU_DVMA_RANGE_OLD (0x1000000)
|
|
#define IOMMU_DVMA_BASE_OLD (0 - IOMMU_DVMA_RANGE_OLD)
|
|
|
|
/* mapping SunPC */
|
|
#define IOMMU_SUNPCMEM_BASE (iommu_btop(IOMMU_DVMA_BASE)) /* in pages */
|
|
#define IOMMU_SUNPCMEM_RANGE (IOMMU_DVMA_BASE_OLD - IOMMU_DVMA_BASE) /* in bytes, 48MB */
|
|
#define IOMMU_SUNPCMEM_SIZE (iommu_btop(IOMMU_SUNPCMEM_RANGE)) /* in pages */
|
|
|
|
#define BIGSBUSMAP_BASE (iommu_btop(IOMMU_DVMA_BASE_OLD))
|
|
#define BIGSBUSDVMA_BASE (IOMMU_DVMA_BASE_OLD)
|
|
|
|
#endif
|
|
|
|
#ifdef IOC
|
|
#include <machine/iocache.h>
|
|
#endif
|
|
|
|
#define MINBUF 8 /* min # of buffers (8) */
|
|
#define BUFPERCENT (1000 / 15) /* max pmem for buffers(1.5%) */
|
|
#define MAXBUF (BUFBYTES/MAXBSIZE - 1) /* max # of buffers (255) */
|
|
|
|
#define MINPTSA (((mmu_btop(0x2000000) + NL3PTEPERPT - 1) * 16) / NL3PTEPERPT)
|
|
#define MINPTS(a) (a > MINPTSA? a : MINPTSA)
|
|
|
|
/*
|
|
* Declare these as initialized data so we can patch them.
|
|
*/
|
|
int nbuf = 0;
|
|
extern struct memlist *physmemory; /* set up in fillsysinfo */
|
|
extern struct memlist *availmemory; /* set up in fillsysinfo */
|
|
extern struct memlist *virtmemory; /* set up in fillsysinfo */
|
|
int physmem = 0; /* memory size in pages, patch if you want less */
|
|
int kernprot = 1; /* write protect kernel text */
|
|
int msgbufinit = 1; /* message buffer has been initialized, ok to printf */
|
|
int nrnode = 0; /* maximum number of NFS rnodes */
|
|
int nopanicdebug = 0; /* 0 = call debugger (if any) on panic, 1 = reboot */
|
|
#if defined(ROOT_ON_TAPE)
|
|
int boothowto = RB_SINGLE | RB_WRITABLE; /* MUNIX gets "-sw" */
|
|
#else
|
|
int boothowto = 0; /* reboot flags (see sys/reboot.h) */
|
|
#endif
|
|
|
|
extern int initing;
|
|
extern int cpu;
|
|
|
|
extern char end[], edata[];
|
|
|
|
#if defined(SUN4M_35)
|
|
int small_4m = 0; /* small_4m based system */
|
|
int no_vme = 0; /* no vme bus present on this system */
|
|
int no_mix = 0; /* no superscalar processor */
|
|
int hat_cachesync_bug = 0;
|
|
#endif
|
|
|
|
#if defined(VAC)
|
|
|
|
int use_cache = 1;
|
|
|
|
int use_page_coloring = 1;
|
|
int use_mix = 1;
|
|
int use_vik_prefetch = 0;
|
|
int use_mxcc_prefetch = 1;
|
|
int use_table_walk = 1;
|
|
int use_store_buffer = 1;
|
|
int use_ic = 1;
|
|
int use_dc = 1;
|
|
int use_ec = 1;
|
|
#if defined(SUN4M_35)
|
|
int use_pe = 1;
|
|
#endif
|
|
int use_multiple_cmd = 1;
|
|
int use_rdref_only = 0;
|
|
int vac_copyback = 1; /* default to CopyBack */
|
|
|
|
int do_pg_coloring = 0; /* page coloring for viking/-Ecache case */
|
|
|
|
char *vac_mode;
|
|
addr_t kern_nc_top_va = end;
|
|
u_int kern_nc_top_pp = 0;
|
|
addr_t kern_nc_end_va = end;
|
|
u_int kern_nc_end_pp = 0;
|
|
#else
|
|
int use_cache = 0;
|
|
#endif VAC && !MINIROOT
|
|
|
|
#ifdef IOC
|
|
int use_ioc = 1; /* variable to patch to have the kernel use i/o cache */
|
|
int iocenable = 0; /* tells all that the IOC is turned on. */
|
|
#else !IOC
|
|
#define use_ioc 0
|
|
#endif !IOC
|
|
|
|
#ifndef MULTIPROCESSOR
|
|
int bcopy_res = -1; /* block copy buffer reserved (in use) */
|
|
#else
|
|
extern int bcopy_res;
|
|
#endif !MULTIPROCESSOR
|
|
int use_bcopy = 1; /* variable to patch to have kernel use bcopy buffer */
|
|
int bcopy_cnt = 0; /* number of bcopys that used the buffer */
|
|
int bzero_cnt = 0; /* number of bzeros that used the buffer */
|
|
|
|
extern int uniprocessor; /* default to MultiProcessor */
|
|
#ifdef NOPROM
|
|
/*
|
|
* Configuration parameters set at boot time.
|
|
*/
|
|
struct cpuinfo {
|
|
u_int cpui_mmutype : 1; /* mmu type, 0=2 level, 1=3 level */
|
|
u_int cpui_vac : 1; /* has virtually addressed cache */
|
|
u_int cpui_ioctype : 2; /* I/O cache type, 0 = none */
|
|
u_int cpui_iom : 1; /* has I/O MMU */
|
|
u_int cpui_clocktype : 2; /* clock type */
|
|
u_int cpui_bcopy_buf : 1; /* has bcopy buffer */
|
|
u_int cpui_sscrub : 1; /* requires software ECC scrub */
|
|
u_int cpui_linesize : 8; /* cache line size (if any) */
|
|
u_short cpui_nlines; /* number of cache lines */
|
|
u_short cpui_dvmasize; /* size of DMA area */
|
|
u_short cpui_nctx; /* number of contexts */
|
|
u_int cpui_nsme; /* number of segment map entries */
|
|
u_int cpui_npme; /* number of page map entries */
|
|
} cpuinfo[] = {
|
|
/* mmu vac ioc iom clk bcp scr lsz nln dsz nctx nsme npme */
|
|
/* 4C_60 */
|
|
{ 0, 1, 0, 0, 0, 0, 0, 16, 4096, 192, 8, 32768, 8192 },
|
|
};
|
|
struct cpuinfo *cpuinfop; /* misc machine info */
|
|
/*
|
|
* ===========================================
|
|
* FIXME: will be gone when true cpuinfo is done.
|
|
* ===========================================
|
|
*/
|
|
u_int module_type = -1; /* some impossible value */
|
|
|
|
#else NOPROM
|
|
extern struct machinfo mach_info;
|
|
#ifdef VME
|
|
extern struct vmeinfo vme_info;
|
|
#endif VME
|
|
extern struct modinfo mod_info[];
|
|
#endif
|
|
#define MAX_NCTXS (1<<12) /* the most we will ever want */
|
|
u_int nctxs = 0; /* for srmmu table size and alignment */
|
|
u_int nswctxs = 0; /* how many we really use */
|
|
addr_t econtig = end; /* End of first block of contiguous kernel */
|
|
addr_t eecontig = end; /* end of segu, which is after econtig */
|
|
addr_t vstart; /* Start of valloc'ed region */
|
|
addr_t vvm; /* Start of valloc'ed region we can mmap */
|
|
int vac_linesize; /* size of a cache line */
|
|
int vac_nlines; /* number of lines in the cache */
|
|
int vac_pglines; /* number of cache lines in a page */
|
|
int vac_size = 0; /* cache size in bytes */
|
|
#ifndef MULTIPROCESSOR
|
|
int Cpudelay = 0; /* delay loop count/usec */
|
|
#endif MULTIPROCESSOR
|
|
int cpudelay = 0; /* for compatibility with old macros */
|
|
#ifdef NOPROM
|
|
int dvmasize; /* usable dvma space */
|
|
#else /* XXX - how should this be setup */
|
|
int dvmasize = 0xff; /* usable dvma space, in pages (1M) */
|
|
#endif NOPROM
|
|
u_int shm_alignment = 0; /* VAC address consistency modulus */
|
|
u_int ctxtab_align; /* minimum alignment for context tables */
|
|
|
|
u_int nl1pts = 0; /* how many l1pts we have */
|
|
extern struct l1pt *l1pts, *el1pts;
|
|
extern struct l1pt *hat_l1alloc();
|
|
|
|
int nmod = 1; /* number of modules */
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
|
|
int ncpu = 1; /* active cpu count */
|
|
int procset = 1; /* bit map of active CPUs */
|
|
|
|
/*
|
|
* points to the level-2 page table structure (shared by all CPUs)
|
|
* used to map in the area where all the CPU's per-CPU areas
|
|
* are mapped: VA_PERCPU.
|
|
*/
|
|
struct ptbl *percpu_ptbl;
|
|
/*
|
|
* the level-1 page table entry for each CPU's per-CPU area
|
|
* which is at virtual address VA_PERCPUME.
|
|
* used by hat_map_percpu to map in the per-CPU area for a
|
|
* user process.
|
|
*/
|
|
union ptpe percpu_ptpe[NCPU]; /* for hat_map_percpu */
|
|
#endif MULTIPROCESSOR
|
|
|
|
#ifdef NOPROM
|
|
extern int availmem; /* physical memory available in SAS */
|
|
#endif NOPROM
|
|
|
|
extern u_int ldphys();
|
|
|
|
/*
|
|
* Most of these vairables are set to indicate whether or not a particular
|
|
* architectural feature is implemented. If the kernel is not configured
|
|
* for these features they are #defined as zero. This allows the unused
|
|
* code to be eliminated by dead code detection without messy ifdefs all
|
|
* over the place.
|
|
*/
|
|
|
|
#ifdef VAC
|
|
int cache = 0; /* address cache type. (none = 0) */
|
|
int vac = 0; /* virtual address cache type (none == 0) */
|
|
#endif VAC
|
|
|
|
#ifdef IOMMU
|
|
int iom = 0;
|
|
union iommu_pte *ioptes, *eioptes; /* virtual addr of ioptes */
|
|
union iommu_pte *phys_iopte; /* phys addr of ioptes */
|
|
|
|
#endif IOMMU
|
|
|
|
#ifdef IOC
|
|
int ioc = 1; /* I/O cache type (none == 0) */
|
|
#endif IOC
|
|
|
|
int bcopy_buf = 0; /* block copy buffer present */
|
|
|
|
/*
|
|
* Dynamically allocated MMU structures
|
|
*/
|
|
extern struct ctx *ctxs, *ectxs; /* declared in vm_hat.c */
|
|
/* Pointer to (root) context Table */
|
|
/* extern */ struct ctx_table *ctxtabptr; /* ctx_table is not declared in
|
|
* vm_hat.c !! */
|
|
/* pointer to level 1 page table for system */
|
|
struct l1pt *kl1pt = (struct l1pt *)NULL;
|
|
/* pointer to array of level 2 page tables */
|
|
struct l2pt *kl2pts = (struct l2pt *)NULL;
|
|
|
|
/*
|
|
* VM data structures
|
|
*/
|
|
int page_hashsz; /* Size of page hash table (power of two) */
|
|
struct page **page_hash; /* Page hash table */
|
|
struct seg *segkmap; /* Kernel generic mapping segment */
|
|
struct map *kernelmap; /* for Sysbase -> Syslimit */
|
|
struct map *bufmap; /* for Disk Buffer Cache */
|
|
struct map *heapmap; /* for Kernel Heap */
|
|
struct seg ktextseg; /* Segment used for kernel executable image */
|
|
struct seg kvalloc; /* Segment used for "valloc" mapping */
|
|
#ifdef MULTIPROCESSOR
|
|
struct seg kpercpume;
|
|
struct seg kpercpu;
|
|
#endif MULTIPROCESSOR
|
|
|
|
int npts = 0; /* number of page tables */
|
|
|
|
#define TESTVAL 0xA55A /* memory test value */
|
|
|
|
/*
|
|
* FORTH monitor gives us romp as a variable.
|
|
*/
|
|
#ifdef notdef
|
|
struct sunromvec *romp = (struct sunromvec *)0xffe00010;
|
|
struct debugvec *dvec = (struct debugvec *)0;
|
|
#else
|
|
extern struct sunromvec *romp;
|
|
extern struct debugvec *dvec;
|
|
#endif
|
|
#ifdef OPENPROMS
|
|
extern dnode_t prom_nextnode();
|
|
extern char *prom_bootargs();
|
|
#ifndef MULTIPROCESSOR
|
|
extern void prom_exit_to_mon();
|
|
extern void prom_enter_mon();
|
|
extern void prom_printf();
|
|
#define prom_eprintf prom_printf
|
|
extern void prom_writestr();
|
|
extern void prom_putchar();
|
|
extern void prom_mayput();
|
|
#else MULTIPROCESSOR
|
|
#define prom_exit_to_mon mpprom_exit
|
|
#define prom_enter_mon mpprom_enter
|
|
#define prom_printf mpprom_printf
|
|
#define prom_eprintf mpprom_eprintf
|
|
#define prom_writestr mpprom_writestr
|
|
#define prom_putchar mpprom_putchar
|
|
#define prom_mayput mpprom_mayput
|
|
#endif MULTIPROCESSOR
|
|
#endif OPENPROMS
|
|
|
|
/*
|
|
* Globals for asynchronous fault handling
|
|
*/
|
|
atom_t module_error;
|
|
/*
|
|
* When the first system-fatal asynchronous fault is detected,
|
|
* this variable is atomically set.
|
|
*/
|
|
atom_t system_fatal;
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
|
|
atom_t mm_flag;
|
|
|
|
#else MULTIPROCESSOR
|
|
/*
|
|
* ... and info specifying the fault is stored here:
|
|
*/
|
|
struct async_flt sys_fatal_flt;
|
|
/*
|
|
* The following data is stored in the PERCPU area for MP systems,
|
|
* but for single-processor systems it is stored here:
|
|
*/
|
|
/*
|
|
* Used to store a queue of non-fatal asynchronous faults to be
|
|
* processed.
|
|
*/
|
|
struct async_flt a_flts[NCPU][MAX_AFLTS];
|
|
/*
|
|
* Incremented by 1 (modulo MAX_AFLTS) by the level-15 asynchronous
|
|
* interrupt thread before info describing the next non-fatal fault
|
|
* in a_flts[a_head].
|
|
*/
|
|
u_int a_head[NCPU];
|
|
/*
|
|
* Incremented by 1 (modulo MAX_AFLTS) by the level-12 asynchronous
|
|
* fault handler thread before processing the next non-fatal fault
|
|
* described by info in a_flts[a_tail].
|
|
*/
|
|
u_int a_tail[NCPU];
|
|
#endif MULTIPROCESSOR
|
|
/*
|
|
* When nofault is set, if an asynchronous fault occurrs, this
|
|
* flag will get set, so that the probe routine knows something
|
|
* went wrong. It is up to the probe routine to reset this
|
|
* once it's done messing around.
|
|
*
|
|
* values:
|
|
* 0 no fault expected
|
|
* -1 fault may happen soon
|
|
* 1 fault happened.
|
|
*/
|
|
int pokefault = 0;
|
|
|
|
#ifndef MULTIPROCESSOR
|
|
/*
|
|
* for single processor which doesn't have a PERCPU area
|
|
* which contains cpuid, define cpuid here to be always set
|
|
* to 0.
|
|
*/
|
|
u_int cpuid = 0;
|
|
#endif MULTIPROCESSOR
|
|
|
|
/*
|
|
* Monitor pages may not be where this sez they are.
|
|
* and the debugger may not be there either.
|
|
*
|
|
* The point is, is the IOPBMEM will set itself up
|
|
* as the last couple of pages of physical memory
|
|
* that we see.
|
|
*
|
|
* Also, note that 'pages' here are *physical* pages,
|
|
* which are 4k on sun4c. Thus, for us the first 4 pages
|
|
* of physical memory are equivalent of the first two
|
|
* 8k pages on the sun4.
|
|
*
|
|
* Physical memory layout
|
|
* (not necessarily contiguous)
|
|
*
|
|
* |-----------------------|
|
|
* | monitor pages |
|
|
* availmem -|-----------------------|
|
|
* | IOPBMEM |
|
|
* |-----------------------|
|
|
* | |
|
|
* | page pool |
|
|
* | |
|
|
* |-----------------------|
|
|
* | configured tables |
|
|
* | buffers |
|
|
* firstaddr -|-----------------------|
|
|
* | hat data structures |
|
|
* |-----------------------|
|
|
* | kernel data, bss |
|
|
* |-----------------------|
|
|
* | interrupt stack |
|
|
* |-----------------------|
|
|
* | kernel text (RO) |
|
|
* |-----------------------|
|
|
* | trap table (4k) |
|
|
* |-----------------------|
|
|
* page 2-3| msgbuf |
|
|
* |-----------------------|
|
|
* page 0-1| unused- reclaimed |
|
|
* |-----------------------|
|
|
*
|
|
*
|
|
*
|
|
*
|
|
* Virtual memory layout.
|
|
*
|
|
* NOTE: specific virtual addresses of things below may change from
|
|
* build to build. If you want the actual address, extract it from an
|
|
* existing kernel using either the debugger (if the kernel keeps the
|
|
* address around) or insert a "printf" of the constant named on the
|
|
* right.
|
|
*
|
|
* Use one of these constants in anything not compiled for each
|
|
* release of the kernel, go to jail.
|
|
*
|
|
* Certain hex addresses have been provided. This is not a guarantee
|
|
* that they will never change, just that there are a number of places
|
|
* where the kernel has to agree with stuff supported by other folks,
|
|
* so changing these numbers is a difficult task indeed.
|
|
*
|
|
* Beware: libkvm and debuggers might make assumptions about various
|
|
* things not changing, like KERNELBASE or VA_PERCPU.
|
|
*
|
|
* 0xFFFFFFFF -|-----------------------|
|
|
* | DVMA |
|
|
* | IOPB memory |
|
|
* 0xFFF00000 -|-----------------------|- DVMA
|
|
* | monitor (2M) |
|
|
* 0xFFD00000 -|-----------------------|- MONSTART
|
|
* | debugger (1M) |
|
|
* 0xFFC00000 -|-----------------------|- DEBUGSTART
|
|
* | unused (old segkmap) |
|
|
* |-----------------------|
|
|
* | NCARGS (1MB) |
|
|
* |-----------------------|- Syslimit
|
|
* | mmap (1 page) |
|
|
* |-----------------------|- vmmap
|
|
* | CMAP2 (1 page) |
|
|
* |-----------------------|- CADDR2
|
|
* | CMAP1 (1 page) |
|
|
* |-----------------------|- CADDR1
|
|
* | Mbmap |
|
|
* | MBPOOLMMUPAGES (2MB) |
|
|
* |-----------------------|- mbutl
|
|
* | kernelmap |
|
|
* | SYSPTSIZE (6.25MB) |
|
|
* 0xFF000000 -|-----------------------|- Sysbase
|
|
* | system dev mappings |
|
|
* | see devaddr.h |
|
|
* |-----------------------| V_WKBASE_ADDR
|
|
*
|
|
* This "BOOTPROM" segment can't be moved! The boot prom may want
|
|
* to use it for virtual address mappings. Of course, we really
|
|
* ought to make our virtual layout dynamic and use the
|
|
* information that the boot prom tells us about what virtual
|
|
* addresses are available, but that's not doable yet.
|
|
*
|
|
* 0xFEF00000 -|-----------------------| given to boot prom
|
|
* | BOOTPROM | 11+meg free here
|
|
* 0xFE400000 -|-----------------------| given to boot prom
|
|
*
|
|
* |-----------------------|
|
|
* | Percpu |
|
|
* | Data Areas |
|
|
* |-----------------------|- VA_PERCPU (at region boundrey)
|
|
*
|
|
* |-----------------------|
|
|
* | mappings unstable |
|
|
* | thru end of region |
|
|
* |-----------------------|
|
|
* | PercpuMe |
|
|
* | Data Area |
|
|
* |-----------------------|- VA_PERCPUME (at region boundrey)
|
|
*
|
|
* |-----------------------|- TOPSEGLIMIT
|
|
* ||---------------------||- BUFLIMIT
|
|
* || Buffer Cache || BUFBYTES
|
|
* ||---------------------||- BUFBASE
|
|
* ||---------------------||- HEAPLIMIT
|
|
* || Kernel Heap || HEAPBYTES
|
|
* ||---------------------||- HEAPBASE
|
|
* ||---------------------||- HEAPLIMIT
|
|
* || segkmap || KMAPBYTES
|
|
* ||---------------------||- KMAPBASE
|
|
* |-----------------------|- TOPSEGBASE
|
|
* | TMPPTES | (goes away during startup)
|
|
* |-----------------------|- VA_TMPPTES
|
|
*
|
|
* This space is the hole between the stuff that grows down from
|
|
* the top of memory (above this comment) and the stuff that
|
|
* grows up from KERNELBASE (the stuff below this comment).
|
|
*
|
|
* |-----------------------|- eecontig
|
|
* | segu |
|
|
* |-----------------------|- econtig
|
|
* | configured tables |
|
|
* | buffers |
|
|
* | hat data structures |
|
|
* |-----------------------|- end
|
|
* | kernel bss segment |
|
|
* |-----------------------|- edata
|
|
* | kernel data segment |
|
|
* |-----------------------|- etext
|
|
* | kernel text segment |
|
|
* ||---------------------||
|
|
* || trap table (4k) ||
|
|
* ||---------------------||- scb
|
|
* |-----------------------|- start
|
|
*
|
|
* |-----------------------|
|
|
* | msgbuf |
|
|
* |-----------------------|- msgbuf
|
|
* user copy red zone
|
|
* (invalid)
|
|
* |-----------------------|- KERNELBASE
|
|
*
|
|
* |-----------------------|
|
|
* | user stack |
|
|
* : :
|
|
* : :
|
|
* | user data |
|
|
* |-----------------------|
|
|
* | user text |
|
|
* 0x00002000 -|-----------------------|
|
|
* | invalid |
|
|
* 0x00000000 -|-----------------------|
|
|
*/
|
|
|
|
/*
|
|
* Define for start of dvma segment that monitor maps in.
|
|
*/
|
|
#define MON_DVMA_ADDR (0 - DVMA_MAP_SIZE)
|
|
|
|
#ifndef NOPROM
|
|
/*
|
|
* return the level-2 ptp or pte for the specified address in the
|
|
* current translation map. if the translation uses a level-1 pte,
|
|
* fake up a l2 pte.
|
|
* If no translation exists return 'dflt'.
|
|
*/
|
|
|
|
u_int
|
|
get_rom_l2_ptpe(vaddr, rv)
|
|
u_int vaddr;
|
|
u_int rv;
|
|
{
|
|
union ptpe ct, rp, l1, l2;
|
|
unsigned pa, po;
|
|
|
|
ct.ptpe_int = mmu_getctp();
|
|
pa = ct.ptp.PageTablePointer << MMU_STD_PTPSHIFT;
|
|
rp.ptpe_int = ldphys(pa);
|
|
switch (rp.ptp.EntryType) {
|
|
case MMU_ET_PTE: /* pte in context table: fake a level-2 pte. */
|
|
po = ((vaddr >> MMU_STD_PAGESHIFT) &
|
|
(MMU_STD_ROOTMASK &~ MMU_STD_SECONDMASK));
|
|
rp.pte.PhysicalPageNumber += po;
|
|
rv = rp.ptpe_int;
|
|
break;
|
|
|
|
case MMU_ET_PTP:
|
|
pa = (rp.ptp.PageTablePointer << MMU_STD_PTPSHIFT) |
|
|
MMU_L1_INDEX(vaddr) << 2;
|
|
l1.ptpe_int = ldphys(pa);
|
|
switch (l1.ptp.EntryType) {
|
|
case MMU_ET_PTE: /* pte in ctx table: fake a level-2 pte. */
|
|
po = ((vaddr >> MMU_STD_PAGESHIFT) &
|
|
(MMU_STD_FIRSTMASK &~ MMU_STD_SECONDMASK));
|
|
l1.pte.PhysicalPageNumber += po;
|
|
rv = l1.ptpe_int;
|
|
break;
|
|
|
|
case MMU_ET_PTP:
|
|
pa = ((l1.ptp.PageTablePointer << MMU_STD_PTPSHIFT) |
|
|
MMU_L2_INDEX(vaddr) << 2);
|
|
l2.ptpe_int = ldphys(pa);
|
|
switch (l2.ptp.EntryType) {
|
|
case MMU_ET_PTE:
|
|
case MMU_ET_PTP:
|
|
rv = l2.ptpe_int;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return (rv);
|
|
}
|
|
#endif NOPROM
|
|
|
|
/*
|
|
* This routine sets up a temporary mapping for the kernel so it can
|
|
* run at the right addresses quickly. It uses the built-in level 1
|
|
* and level 2 page table structs, and borrows the end of physical
|
|
* memory for the level 3 tables. When it is done, the first
|
|
* MAINMEM_MAP_SIZE of memory will be mapped straight through with a
|
|
* virtual offset of KERNELBASE. Also, the monitor and debugger ptes
|
|
* are preserved as is, and the dvma segment that the monitor set up
|
|
* is also preserved. The routine returns the value to be loaded into
|
|
* the context table pointer.
|
|
*
|
|
* NOTE: this routine now runs at the proper virtual address, since
|
|
* we can kludge up a mapping that works.
|
|
*/
|
|
|
|
#define ALIGN(v, m) ((((int)v) + m - 1) &~ (m - 1))
|
|
|
|
u_int
|
|
load_tmpptes()
|
|
{
|
|
register struct l1pt *kl1ptr;
|
|
register union ptpe *kl2ptps;
|
|
register union ptpe *kpteptr;
|
|
struct l3pt *kl3ptr;
|
|
struct l2pt *kl2ptr;
|
|
register u_int i, j;
|
|
int npages;
|
|
struct memlist *pmemptr;
|
|
#ifndef NOPROM
|
|
struct memlist *first_membank;
|
|
extern union ptpe *tmpptes;
|
|
extern void build_memlists();
|
|
extern caddr_t prom_map();
|
|
#endif
|
|
|
|
#ifndef NOPROM
|
|
u_int get_rom_l2_ptpe();
|
|
#endif
|
|
extern int kptstart[];
|
|
|
|
#ifndef NOPROM
|
|
build_memlists(); /* build {phys,avail,virt}memory lists */
|
|
|
|
/*
|
|
* Start with the memory size the monitor claims.
|
|
* "availmemory" is set up in fillsysinfo
|
|
*/
|
|
npages = 0;
|
|
for (pmemptr = availmemory; pmemptr; pmemptr = pmemptr->next) {
|
|
npages += mmu_btop(pmemptr->size);
|
|
if (!(pmemptr->address))
|
|
first_membank = pmemptr;
|
|
}
|
|
physmem = npages;
|
|
if ((first_membank->size) > TMPPTES) {
|
|
PA_TMPPTES = ((first_membank->size) - TMPPTES);
|
|
} else {
|
|
panic("no physical space for tmpptes");
|
|
}
|
|
#endif NOPROM
|
|
/* locate start of reserved area [virtual address] */
|
|
i = (int)kptstart;
|
|
|
|
/*
|
|
* Calculate actual location for kernel level 1 & 2 page tables.
|
|
* aligned to multiple of size of l1 page table.
|
|
*/
|
|
i = ALIGN(i, sizeof (struct l1pt));
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
/* further, make sure it is page aligned. */
|
|
i = ALIGN(i, PAGESIZE);
|
|
#endif MULTIPROCESSOR
|
|
kl1pt = kl1ptr = (struct l1pt *)i;
|
|
#ifndef MULTIPROCESSOR
|
|
i += sizeof (struct l1pt);
|
|
#else MULTIPROCESSOR
|
|
/* one PAGE per cpu for level-1 tables ??? */
|
|
i += PAGESIZE * NCPU;
|
|
#endif MULTIPROCESSOR
|
|
|
|
/*
|
|
* Calculate the page number to use for the temp ptes and the
|
|
* physical address of the ptes.
|
|
*/
|
|
/* ### If running with sas, be sure to use at least 2 megabytes of memory */
|
|
/*
|
|
* We grab the memory just under the top of the memory (+ slop) to put
|
|
* the temporary 3rd level page tables. We will copy these out during
|
|
* startup to the real tables
|
|
*/
|
|
#ifndef NOPROM
|
|
/*
|
|
* Call to prom to get space for tmpptes
|
|
*/
|
|
if (!(tmpptes = (union ptpe *)prom_map((caddr_t)VA_TMPPTES, OBMEM,
|
|
PA_TMPPTES, TMPPTES))) {
|
|
panic("can't map space for tmpptes");
|
|
}
|
|
kpteptr = tmpptes; /* THIS IS A VA ABOVE KERNELBASE */
|
|
|
|
#else NOPROM
|
|
kpteptr = TMPPTES; /* THIS IS A VA ABOVE KERNELBASE */
|
|
#endif NOPROM
|
|
kl3ptr = (struct l3pt *) kpteptr;
|
|
|
|
kl2pts = (struct l2pt *)&kpteptr[NL3PTEPERPT*NKL3PTS];
|
|
kl2ptps = (union ptpe *)(kl3ptr + NKL3PTS);
|
|
kl2ptr = (struct l2pt *)kl2ptps;
|
|
|
|
/*
|
|
* Have the first level 0 entry map the bottom part of memory so
|
|
* that we can continue to run down there until we relocate ourselves
|
|
* to high memory. This will map the low 16 Meg of memory to itself.
|
|
*/
|
|
|
|
kl1ptr->ptpe[0].ptpe_int = PTEOF(0, 0, MMU_STD_SRWX, 0);
|
|
|
|
/*
|
|
* Invalidate all the level 1 entries.
|
|
*/
|
|
for (i = 1; i < NL1PTEPERPT; i++)
|
|
kl1ptr->ptpe[i].ptpe_int = MMU_STD_INVALIDPTP;
|
|
|
|
/*
|
|
* Validate the last level 1 entries to point to the level 2 tables.
|
|
*/
|
|
for (j = 0, i = NL1PTEPERPT - NKL2PTS; i < NL1PTEPERPT; j++, i++)
|
|
kl1ptr->ptpe[i].ptpe_int = PTPOF(VA2PA(kl2ptr+j));
|
|
/*
|
|
* Validate the level 2 entries to point to the level 3 tables.
|
|
* For the level 3 tables mapping the debugger and monitor, we
|
|
* make the level 2 entries point to the tables already set up
|
|
* by the monitor. This is necessary so the debugger & monitor
|
|
* can keep track of where their ptes are.
|
|
*/
|
|
for (i = 0, j = 0; i < NKL2PTS * NL2PTEPERPT; i++, j += NL3PTEPERPT) {
|
|
#ifdef NOPROM
|
|
kl2ptps[i].ptpe_int = PTPOF(VA2PA(kpteptr+j));
|
|
#else NOPROM
|
|
/*
|
|
* If the rom provides a level-2 page table, use it.
|
|
* this also picks up root, level-1 and level-2 PTEs,
|
|
* merging part of the VA into the root and level-1
|
|
* PTEs so the same mapping occurs.
|
|
*/
|
|
kl2ptps[i].ptpe_int =
|
|
get_rom_l2_ptpe(i * L3PTSIZE + KERNELBASE,
|
|
PTPOF(VA2PA(kpteptr+j)));
|
|
#endif NOPROM
|
|
}
|
|
/*
|
|
* Invalidate the level 3 entries.
|
|
*/
|
|
for (i = 0; i < NL3PTEPERPT * NKL3PTS; i++)
|
|
kpteptr[i].ptpe_int = MMU_STD_INVALIDPTP;
|
|
/*
|
|
* Validate the first MAINMEM_MAP_SIZE to map through.
|
|
* (see sunromvec.h for details).
|
|
* The default for kernel text is to be cacheable.
|
|
* Any exceptions should be taken care of here.
|
|
* Maybe someday we can use short termination to
|
|
* reduce how hard we hit the TLB.
|
|
*/
|
|
i = mmu_btop(MAINMEM_MAP_SIZE);
|
|
|
|
while (i-->0)
|
|
kpteptr[i].ptpe_int = PTEOF(0, i, MMU_STD_SRWX, 1);
|
|
/*
|
|
* convert the level1 page table ptr into something
|
|
* that can be used as a context entry value,
|
|
* and return it for further use.
|
|
*/
|
|
|
|
return (PTPOF(VA2PA(kl1ptr)));
|
|
}
|
|
|
|
int vscb_cacheable = 1;
|
|
#ifdef MULTIPROCESSOR
|
|
extern struct proc idleproc;
|
|
|
|
int percpu_cacheable = 1; /* XXX-for debug */
|
|
#endif MULTIPROCESSOR
|
|
|
|
/*
|
|
* Early startup code that no longer needs to be assembler. Note that the
|
|
* ptes being acted on here are the temporary ones. All changes made here
|
|
* will be propagated to the real ptes when they are initialized.
|
|
*/
|
|
|
|
void
|
|
early_startup()
|
|
{
|
|
register int index, i, j;
|
|
extern void mmu_flushall();
|
|
extern union ptpe *tmpptes;
|
|
extern void system_setup();
|
|
extern void prom_init();
|
|
|
|
prom_init("Kernel");
|
|
map_wellknown_devices();
|
|
fill_machinfo();
|
|
setcputype();
|
|
|
|
/*
|
|
* setup system implementation dependent routines
|
|
*/
|
|
system_setup((int) mach_info.sys_type);
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
|
|
/*
|
|
* we used to reserve NCPU * space for multiprocessor mode,
|
|
* it waists a lot of space if we only have one or two
|
|
* modules on the system. temporarily, we assign only one
|
|
* CPU (ctx table, PERCPU area, ptes) space if there is only
|
|
* one cpu on the system. for system that have more than 1
|
|
* and less than 4 cpus, we have to find a way to save some
|
|
* space later. Note that a system with two viking modules
|
|
* plugged in, the cpuid will be 8 and a which means cpuid
|
|
* 0 and 2 (not 0 and 1).
|
|
*/
|
|
if (mach_info.nmods != 1) {
|
|
nmod = NCPU;
|
|
} else {
|
|
uniprocessor = 1;
|
|
}
|
|
#endif MULTIPROCESSOR
|
|
|
|
/*
|
|
* Establish per-cpu mappings
|
|
*/
|
|
j = nmod;
|
|
|
|
while (j-- > 0) {
|
|
#ifdef MULTIPROCESSOR
|
|
/*
|
|
* Per-CPU data area only applies to
|
|
* the multiprocessor case.
|
|
*/
|
|
index = mmu_btop(VA_PERCPU + j * PERCPUSIZE - KERNELBASE);
|
|
for (i = 0; i < PERCPU_PAGES; i++)
|
|
tmpptes[index++].ptpe_int =
|
|
PTEOF(0, va2pp((addr_t)cpu0) +
|
|
(j * PERCPU_PAGES) + i,
|
|
MMU_STD_SRWX, percpu_cacheable);
|
|
/* XXX- for debug make PERCPU area cacheability tunable */
|
|
#endif MULTIPROCESSOR
|
|
/*
|
|
* Counter/Timer registers for each possible MID
|
|
* Make 'em readable by the user for fast profiling
|
|
* we want permissions to be kernel rw, user ro;
|
|
* it should be safe to let the user monkey with
|
|
* the data fields. kernel MUST be able to write.
|
|
*/
|
|
index = mmu_btop(VA_PERCPU + PC_utimers +
|
|
(j << PERCPU_SHIFT) - KERNELBASE);
|
|
tmpptes[index] =
|
|
tmpptes[mmu_btop(V_COUNTER_ADDR - KERNELBASE) + j];
|
|
|
|
/* %%% fun hack %%% make the user timers readable by the user! */
|
|
tmpptes[index].pte.AccessPermissions = MMU_STD_SRWUR;
|
|
|
|
/*
|
|
* Interrupt registers for each possible MID
|
|
*/
|
|
index = mmu_btop(VA_PERCPU + PC_intregs +
|
|
(j << PERCPU_SHIFT) - KERNELBASE);
|
|
tmpptes[index] =
|
|
tmpptes[mmu_btop(V_INTERRUPT_ADDR - KERNELBASE) + j];
|
|
|
|
#ifdef PC_vscb
|
|
index = mmu_btop(VA_PERCPU + PC_vscb +
|
|
(j << PERCPU_SHIFT) - KERNELBASE);
|
|
tmpptes[index].ptpe_int =
|
|
PTEOF(0, va2pp((addr_t)&scb), MMU_STD_SRWX,
|
|
vscb_cacheable);
|
|
#endif PC_vscb
|
|
}
|
|
|
|
/*
|
|
* Establish my-cpu mappings
|
|
* by duplicating the right per-cpu area.
|
|
*/
|
|
i = mmu_btop(VA_PERCPU - KERNELBASE);
|
|
index = mmu_btop(VA_PERCPUME - KERNELBASE);
|
|
j = PERCPU_MAPS; /* number of mappings to copy */
|
|
while (j-- > 0)
|
|
tmpptes[index++] = tmpptes[i++];
|
|
/*
|
|
* Conservatively wipe out the ATC in case the above mappings were
|
|
* somehow in there.
|
|
*/
|
|
mmu_flushall();
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
/*
|
|
* initialize U-area pointer. MULTIPROCESSOR kernels put
|
|
* this variable in the percpu area, so we can't staticly
|
|
* initialize it.
|
|
*/
|
|
uunix = &idleuarea;
|
|
/*
|
|
* Initialize bcopy HW lock to "locked".
|
|
* Needed to minimize checks done in bcopy routine
|
|
* and to only try to use when we should.
|
|
* Can't initialize PERCPU variable to non-zero
|
|
* value, and need to lock until determine we have HW.
|
|
*/
|
|
bcopy_res = -1;
|
|
#endif MULTIPROCESSOR
|
|
/*
|
|
* Zero out the u page, and the bss. NOTE - the scb part
|
|
* makes assumptions about its layout with the msgbuf.
|
|
*/
|
|
bzero((caddr_t)uunix, sizeof (*uunix));
|
|
bzero(edata, (u_int)(end - edata));
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
idleuarea.u_procp = &idleproc;
|
|
#endif MULTIPROCESSOR
|
|
|
|
}
|
|
|
|
/* Flag for testing directed interrupts */
|
|
int test_dirint = 0;
|
|
|
|
extern struct dev_reg obpctx; /* obp's context table info, reg struc */
|
|
|
|
int debug_msg = 0; /* variable to patch to get debugging messages */
|
|
|
|
/*
|
|
* Print out ENABLED and DISABLED debugging messages
|
|
*/
|
|
void
|
|
print_debug_msg()
|
|
{
|
|
#ifdef IOC
|
|
if (ioc) {
|
|
if (use_ioc)
|
|
printf("IOC ENABLED\n");
|
|
else
|
|
printf("IOC DISABLED\n");
|
|
}
|
|
#endif IOC
|
|
|
|
#ifndef MULTIPROCESSOR
|
|
if (bcopy_buf) {
|
|
if (use_bcopy)
|
|
printf("BCOPY BUFFER ENABLED!\n");
|
|
else
|
|
printf("BCOPY BUFFER DISABLED!\n");
|
|
}
|
|
#else
|
|
if (bcopy_buf) {
|
|
if (use_bcopy)
|
|
printf("BCOPY BUFFERS ENABLED!\n");
|
|
else
|
|
printf("BCOPY BUFFERS DISABLED!\n");
|
|
}
|
|
#endif !MULTIPROCESSOR
|
|
|
|
#ifdef VAC
|
|
if (!vac) {
|
|
if (use_cache) {
|
|
if (use_ic)
|
|
printf(" Instruction Cache ENABLED\n");
|
|
else
|
|
printf(" Instruction Cache DISABLED\n");
|
|
if (use_dc)
|
|
printf(" Data Cache ENABLED\n");
|
|
else
|
|
printf(" Data Cache DISABLED\n");
|
|
#if defined(SUN4M_35)
|
|
if (!no_mix) {
|
|
if (use_mix) /* superscalar lives in breakpoint reg */
|
|
printf(" Superscalar ENABLED\n");
|
|
else
|
|
printf(" Superscalar DISABLED\n");
|
|
}
|
|
#else
|
|
if (use_mix) /* superscalar lives in breakpoint reg */
|
|
printf(" Superscalar ENABLED\n");
|
|
else
|
|
printf(" Superscalar DISABLED\n");
|
|
#endif
|
|
}
|
|
if (use_vik_prefetch)
|
|
printf(" Viking Data Prefetcher ENABLED\n");
|
|
else
|
|
printf(" Viking Data Prefetcher DISABLED\n");
|
|
if (use_store_buffer)
|
|
printf(" Store Buffer ENABLED\n");
|
|
else
|
|
printf(" Store Buffer DISABLED\n");
|
|
if (cache == CACHE_PAC_E) {
|
|
if (use_ec) {
|
|
if (use_table_walk) {
|
|
printf(" Table Walk ");
|
|
printf("Cacheable ENABLED\n");
|
|
}
|
|
else {
|
|
printf(" Table Walk ");
|
|
printf("Cacheable DISABLED\n");
|
|
}
|
|
|
|
printf(" External Cache ENABLED\n");
|
|
}
|
|
else
|
|
printf(" External Cache DISABLED\n");
|
|
if (use_mxcc_prefetch)
|
|
printf(" MXCC Prefetch ENABLED\n");
|
|
else
|
|
printf(" MXCC Prefetch DISABLED\n");
|
|
if (use_multiple_cmd)
|
|
printf(" Multiple Command ENABLED\n");
|
|
else
|
|
printf(" Multiple Command DISABLED\n");
|
|
}
|
|
}
|
|
#endif VAC
|
|
}
|
|
|
|
struct memlist obp_context_table_memory;
|
|
|
|
/*
|
|
* Machine-dependent startup code
|
|
*/
|
|
startup()
|
|
{
|
|
register int unixsize;
|
|
#if !defined(SAS) && defined(VMEFIXED)
|
|
register int dvmapage;
|
|
#endif
|
|
register unsigned i, j;
|
|
register caddr_t v;
|
|
u_int firstpage; /* first free physical page number */
|
|
u_int mapaddr, npages, page_base;
|
|
struct page *pp;
|
|
struct segmap_crargs a;
|
|
struct memseg *memseg_base;
|
|
extern void v_handler();
|
|
extern void hat_setup_kas();
|
|
extern char etext[], end[], Syslimit[];
|
|
extern trapvec kclock14_vec;
|
|
extern struct scb scb;
|
|
#ifdef NOPROM
|
|
extern char *cpu_str;
|
|
extern char *mod_str;
|
|
#endif
|
|
#ifdef VAC
|
|
extern void cache_on();
|
|
#endif
|
|
extern void mmu_flushall();
|
|
|
|
int dbug_mem, memblocks;
|
|
struct memlist *pmem, *lastpmem;
|
|
struct memseg *memsegp;
|
|
u_int first_page, num_pages, index;
|
|
u_int bytes_used, bytes_avail;
|
|
struct ptbl *ptbl;
|
|
struct pte *pte;
|
|
union ptpe *kptp, *kptp_min, *kptp_max;
|
|
union ptpe *kptp1;
|
|
struct modinfo *modinfop;
|
|
int minimum_npts;
|
|
#ifdef NOPROM
|
|
union ptpe *tmpptes;
|
|
|
|
tmpptes = TMPPTES;
|
|
#else NOPROM
|
|
extern union ptpe *tmpptes;
|
|
#endif NOPROM
|
|
noproc = 1;
|
|
|
|
if (test_dirint)
|
|
send_dirint(0, 6);
|
|
/*
|
|
* The following statements setup various flags to indicate the presence
|
|
* of a particular hardware feature on the machine which we are running on.
|
|
* Traditionally this information has been hardcoded based on the cpu types
|
|
* for which the kernel was configured. Now much of the configuration
|
|
* information is supplied by the PROM.
|
|
*/
|
|
|
|
/* Old method */
|
|
#ifdef NOPROM
|
|
cpuinfop = &cpuinfo[(cpu & CPU_MACH) - 1];
|
|
setcpudelay();
|
|
dvmasize = cpuinfop->cpui_dvmasize;
|
|
|
|
#ifdef VAC
|
|
vac = cpuinfop->cpui_vac;
|
|
#endif
|
|
|
|
#ifdef IOMMU
|
|
iom = cpuinfop->cpui_iom;
|
|
#endif
|
|
|
|
#ifdef IOC
|
|
ioc = cpuinfop->cpui_ioctype;
|
|
#endif IOC
|
|
|
|
bcopy_buf = cpuinfop->cpui_bcopy_buf;
|
|
|
|
#ifdef VAC
|
|
if (vac) {
|
|
vac_linesize = cpuinfop->cpui_linesize;
|
|
vac_nlines = cpuinfop->cpui_nlines;
|
|
vac_pglines = PAGESIZE / vac_linesize;
|
|
vac_size = vac_nlines * vac_linesize;
|
|
}
|
|
#endif /* VAC */
|
|
|
|
/* New method */
|
|
#else /* NOPROM */
|
|
cpu = mach_info.sys_type;
|
|
modinfop = &mod_info[0];
|
|
setcpudelay();
|
|
#ifdef VAC
|
|
if (modinfop->ncaches) {
|
|
if (modinfop->phys_cache)
|
|
if (modinfop->eclinesize)
|
|
cache = CACHE_PAC_E;
|
|
else
|
|
cache = CACHE_PAC;
|
|
else {
|
|
cache = CACHE_VAC;
|
|
vac = 1;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef IOMMU
|
|
iom = mach_info.iommu;
|
|
#endif
|
|
|
|
#ifdef IOC
|
|
ioc = vme_info.iocache;
|
|
#endif
|
|
|
|
/*
|
|
* Dont need this to be percpu for MP since all the
|
|
* modules should be the same. So if one is capable
|
|
* of HW bcopy, all are.
|
|
*/
|
|
bcopy_buf = modinfop->bcopy;
|
|
|
|
if (nctxs == 0) {
|
|
nctxs = modinfop->mmu_nctx;
|
|
|
|
if (nctxs > MAX_NCTXS)
|
|
nctxs = MAX_NCTXS;
|
|
}
|
|
|
|
nl1pts = nproc + 10;
|
|
|
|
if (nswctxs == 0)
|
|
nswctxs = nproc + 10;
|
|
|
|
if (nswctxs > nctxs)
|
|
nswctxs = nctxs;
|
|
|
|
#if defined(SUN4M_35)
|
|
if (nctxs < 1024)
|
|
nctxs = MAX_NCTXS;
|
|
#endif
|
|
|
|
#ifdef VAC
|
|
if (vac) {
|
|
vac_linesize = modinfop->clinesize;
|
|
vac_nlines = modinfop->cnlines;
|
|
vac_pglines = PAGESIZE / vac_linesize;
|
|
vac_size = vac_nlines * vac_linesize;
|
|
}
|
|
#endif /* VAC */
|
|
|
|
#endif /* NOPROM */
|
|
|
|
#ifdef IOC
|
|
if (ioc) {
|
|
if (use_ioc) {
|
|
ioc_init();
|
|
iocache_on();
|
|
iocenable = 1;
|
|
} else {
|
|
iocenable = 0;
|
|
ioc = 0;
|
|
}
|
|
}
|
|
#endif IOC
|
|
|
|
#ifndef MULTIPROCESSOR
|
|
if (bcopy_buf) {
|
|
if (use_bcopy) {
|
|
bcopy_res = 0; /* allow use of hardware */
|
|
} else {
|
|
bcopy_res = -1; /* reserve now, hold forever */
|
|
}
|
|
}
|
|
#else
|
|
if (bcopy_buf) {
|
|
if (use_bcopy) {
|
|
for (i = 0; i < nmod; ++i)
|
|
PerCPU[i].bcopy_res = 0;
|
|
} else {
|
|
for (i = 0; i < nmod; ++i)
|
|
PerCPU[i].bcopy_res = -1;
|
|
}
|
|
} else {
|
|
for (i = 0; i < nmod; ++i)
|
|
PerCPU[i].bcopy_res = -1;
|
|
}
|
|
#endif !MULTIPROCESSOR
|
|
|
|
/*
|
|
* Save the kernel's level 14 interrupt vector code and install
|
|
* the monitor's. This lets the monitor run the console until we
|
|
* take it over.
|
|
*/
|
|
|
|
init_mon_clock();
|
|
|
|
(void) splzs(); /* allow hi clock ints but not zs */
|
|
|
|
bootflags(); /* get the boot options */
|
|
|
|
/*
|
|
* If the boot flags say that the debugger is there,
|
|
* test and see if it really is by peeking at DVEC.
|
|
* If is isn't, we turn off the RB_DEBUG flag else
|
|
* we call the debugger scbsync() routine.
|
|
*/
|
|
#ifndef NOPROM
|
|
if ((boothowto & RB_DEBUG) != 0) {
|
|
if (dvec == NULL || peek((short *)dvec) == -1)
|
|
boothowto &= ~RB_DEBUG;
|
|
else {
|
|
extern trapvec kadb_tcode, trap_ff_tcode,
|
|
trap_fe_tcode;
|
|
|
|
(*dvec->dv_scbsync)();
|
|
/*
|
|
* Now steal back the traps.
|
|
*/
|
|
kadb_tcode = scb.user_trap[TRAPBRKNO];
|
|
scb.user_trap[TRAPBRKNO-1] = trap_fe_tcode;
|
|
scb.user_trap[TRAPBRKNO] = trap_fe_tcode;
|
|
}
|
|
}
|
|
#endif NOPROM
|
|
|
|
#ifdef NOPROM
|
|
/* SAS has contigouous memory */
|
|
npages = physmem = btop(availmem);
|
|
physmax = physmem - 1;
|
|
memblocks = 0; /* set this to zero so the valloc below ends up w/ 1 */
|
|
#else
|
|
/*
|
|
* v_vector_cmd is the handler for the monitor vector command .
|
|
* We install v_handler() there for Unix.
|
|
*/
|
|
if (prom_sethandler((voidfunc_t) 0, v_handler) != 0)
|
|
panic("No handler for PROM?");
|
|
|
|
if (obpctx.reg_size > PAGESIZE) {
|
|
/*
|
|
* Dont need OBP context table anymore.
|
|
* Keep the first page of context table for OBP to start other CPUs.
|
|
* Put them back to the availmemory pool
|
|
* The availmemory pool was preassigned to a 4K page size
|
|
* We only search for the case that ctx table space merged with
|
|
* the higher address. It doesn't care about if the space
|
|
* can be merged into the end of a contiguous space because
|
|
* it will never happen (we reserved the first 4K of the space for
|
|
* OBP)
|
|
*/
|
|
|
|
u_int va = (u_int) obpctx.reg_addr + PAGESIZE;
|
|
u_int vs = (u_int) obpctx.reg_size - PAGESIZE;
|
|
|
|
for (pmem = availmemory; pmem && obpctx.reg_size; pmem = pmem->next) {
|
|
if (pmem->address == (va + vs)) {
|
|
/*
|
|
* extend this block down to cover
|
|
* the memory.
|
|
*/
|
|
physmem -= mmu_btop(pmem->size);
|
|
pmem->address = va;
|
|
pmem->size += vs;
|
|
physmem += mmu_btop(pmem->size);
|
|
obpctx.reg_size = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (obpctx.reg_size) {
|
|
struct memlist **ref;
|
|
struct memlist *new;
|
|
/*
|
|
* standard ordered singly-linked list insertion.
|
|
* we only do this once, so we just use a node that
|
|
* we staticly allocated in our BSS.
|
|
*/
|
|
for (ref = &availmemory; pmem = *ref; ref = &pmem->next)
|
|
if (pmem->address > va)
|
|
break;
|
|
new = &obp_context_table_memory;
|
|
new->next = pmem;
|
|
new->address = va;
|
|
new->size = vs;
|
|
*ref = new;
|
|
physmem += mmu_btop(vs);
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Add up how much physical memory the prom has passed us.
|
|
* Remember what the physically available highest page is
|
|
* so that dumpsys works properly.
|
|
*/
|
|
memblocks = 0;
|
|
for (pmem = availmemory; pmem; pmem = pmem->next) {
|
|
|
|
memblocks++;
|
|
/*
|
|
* Remember the last pmem segment so that we can
|
|
* shove our IOPBMAP just below the debugger.
|
|
* N.B.: This assumes that the debugger (and maybe the
|
|
* monitor) will leave enough room in this physical memory
|
|
* segment for the pages that IOPBMAP requires.
|
|
*
|
|
*/
|
|
if (pmem->next == (struct memlist *) NULL) {
|
|
lastpmem = pmem;
|
|
physmax = mmu_btop(pmem->size + pmem->address);
|
|
}
|
|
}
|
|
/*
|
|
* Now add up how much physical memory is really present (so
|
|
* dumpsys will work).
|
|
*/
|
|
npages = 0;
|
|
for (pmem = availmemory; pmem; pmem = pmem->next) {
|
|
npages += mmu_btop(pmem->size);
|
|
if (pmem->next == NULL)
|
|
physmax = mmu_btop(pmem->size + pmem->address) - 1;
|
|
}
|
|
|
|
/*
|
|
* If debugger is in memory, note the pages it stole from physmem.
|
|
*/
|
|
dbug_mem = (boothowto & RB_DEBUG) ? *dvec->dv_pages : 0;
|
|
|
|
#endif NOPROM
|
|
/*
|
|
* maxmem is the amount of physical memory we're playing with,
|
|
* less the amount we'll give to the IOPBMAP
|
|
*/
|
|
maxmem = physmem - IOPBMEM;
|
|
|
|
/*
|
|
* Why is this here?
|
|
*/
|
|
if (nrnode == 0)
|
|
nrnode = ncsize << 1;
|
|
|
|
/*
|
|
* Allocate space for system data structures.
|
|
* The first available kernel virtual address is in "v".
|
|
* As pages of kernel virtual memory are allocated, "v" is incremented.
|
|
* It is used when remapping the tables later.
|
|
*/
|
|
/*
|
|
* First the machine dependent, Hardware Address Translation (hat)
|
|
* data structures are allocated. We depend on the fact that the
|
|
* Monitor maps in more memory than required. We do this first so
|
|
* that the remaining data structures can be allocated from the
|
|
* page pool in the usual way.
|
|
*/
|
|
|
|
/* the 2 in statement below is due to 4k page size */
|
|
firstpage = mmu_btopr((int)end - KERNELBASE) + (UPAGES - 2);
|
|
|
|
vvm = vstart = v = (caddr_t)(mmu_ptob(firstpage) + KERNELBASE);
|
|
mapaddr = btoc((int)vvm - KERNELBASE);
|
|
|
|
#define valloc(name, type, num) \
|
|
(name) = (type *)(v); \
|
|
econtig = (v) = (caddr_t)((name)+(num));
|
|
|
|
#define valloclim(name, type, num, lim) \
|
|
(name) = (type *)(v); \
|
|
econtig = (v) = (caddr_t)((lim) = ((name)+(num)));
|
|
|
|
#ifdef DLYPRF
|
|
valloc(dpring, struct dpr, dpsize);
|
|
#endif
|
|
|
|
v = (addr_t)roundup((u_int)v, PAGESIZE);
|
|
kern_nc_top_va = v;
|
|
kern_nc_top_pp = va2pp(v);
|
|
|
|
#ifdef IOMMU
|
|
|
|
if (iom) {
|
|
/* definition: align iopte tbl to 'iopte table size' boundary */
|
|
v = (addr_t)roundup((u_int)v, IOMMU_PTE_TBL_SIZE);
|
|
valloclim(ioptes, union iommu_pte, IOMMU_N_PTES, eioptes);
|
|
phys_iopte = (union iommu_pte *) CA2PA(ioptes);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* NOTE: context table has to be aligned at
|
|
* its size boundary which differs for different modules.
|
|
*
|
|
* XXX-context table size should be (nctxs * sizeof(union ptpe))
|
|
* since roundup operates on u_ints.
|
|
*/
|
|
v = (addr_t)roundup((u_int)v, (nctxs * sizeof (union ptpe)));
|
|
if ((cache == CACHE_PAC_E) && (use_cache)) {
|
|
kern_nc_end_va = v;
|
|
kern_nc_end_pp = va2pp(v);
|
|
}
|
|
|
|
valloclim(contexts, union ptpe, nmod * nctxs, econtexts);
|
|
ctxtabptr = (struct ctx_table *) CA2PA(contexts);
|
|
|
|
/*
|
|
* Calculate how many page tables we will have.
|
|
* The algorithm is to have enough ptes to map all
|
|
* of memory 16 times over (due to fragmentation). We
|
|
* add in the size of the I/O Mapper, since it is always there.
|
|
* We also add in enough tables to map the kernel text/data once.
|
|
* There is an upper limit, however, since we use indices
|
|
* in page table lists.
|
|
*/
|
|
|
|
/* XXX - patch as required */
|
|
if (npts == 0) {
|
|
npts = 64;
|
|
npts += (firstpage + NL3PTEPERPT - 1) / NL3PTEPERPT;
|
|
npts += (maxmem - firstpage + NL3PTEPERPT - 1) * 16 /
|
|
NL3PTEPERPT;
|
|
|
|
minimum_npts = MINPTS(minpts);
|
|
if (npts < minimum_npts)
|
|
npts = minimum_npts;
|
|
|
|
if (npts > MAX_PTBLS)
|
|
npts = MAX_PTBLS;
|
|
}
|
|
|
|
/*
|
|
* Allocate the ptbl structs, and the page tables.
|
|
* Note that page tables have a 256 byte alignment
|
|
* requirement.
|
|
*/
|
|
|
|
/* level 2 and 3 page tables need to be on 256 bytes boundary */
|
|
v = (caddr_t) roundup((u_int)v, sizeof (struct l3pt));
|
|
valloclim(ptes, struct pte, npts * NL3PTEPERPT, eptes);
|
|
pts_addr = CA2PA(ptes);
|
|
|
|
v = (addr_t)roundup((u_int)v, PAGESIZE);
|
|
|
|
valloclim(l1pts, struct l1pt, nl1pts, el1pts);
|
|
|
|
v = (addr_t)roundup((u_int)v, PAGESIZE);
|
|
if ((cache != CACHE_PAC_E) || (!use_cache)) {
|
|
kern_nc_end_va = v;
|
|
kern_nc_end_pp = va2pp(v);
|
|
}
|
|
|
|
/* allocate software ctx, ptbl, pte after real h/w ones */
|
|
valloclim(ctxs, struct ctx, nctxs, ectxs);
|
|
valloclim(ptbls, struct ptbl, npts, eptbls);
|
|
valloclim(sptes, struct spte, npts * NL3PTEPERPT, esptes);
|
|
|
|
v = (caddr_t)roundup((u_int)v, PAGESIZE);
|
|
|
|
/*
|
|
* Vallocate the remaining kernel data structures.
|
|
* "mapaddr" is the real memory address where the tables start.
|
|
* It is used when remapping the tables later.
|
|
*/
|
|
|
|
#ifdef UFS
|
|
#ifdef QUOTA
|
|
valloclim(dquot, struct dquot, ndquot, dquotNDQUOT);
|
|
#endif QUOTA
|
|
#endif UFS
|
|
|
|
valloclim(file, struct file, nfile, fileNFILE);
|
|
valloclim(proc, struct proc, nproc, procNPROC);
|
|
valloc(cfree, struct cblock, nclist);
|
|
valloc(callout, struct callout, ncallout);
|
|
valloc(kernelmap, struct map, MIN(4 * nproc, SYSPTSIZE/2));
|
|
valloc(bufmap, struct map, BUFPAGES/2);
|
|
valloc(heapmap, struct map, HEAPPAGES/2);
|
|
valloc(iopbmap, struct map, IOPBMAPSIZE);
|
|
|
|
#ifdef IOMMU
|
|
if (iom) {
|
|
if (cpu == CPU_SUN4M_690) {
|
|
valloc(vme24map, struct map, NDVMAMAPS(VME24MAP_SIZE));
|
|
valloc(vme32map, struct map, NDVMAMAPS(VME32MAP_SIZE));
|
|
} else {
|
|
valloc(altsbusmap, struct map,
|
|
NDVMAMAPS(ALTSBUSMAP_SIZE));
|
|
}
|
|
valloc(sbusmap, struct map, NDVMAMAPS(SBUSMAP_SIZE));
|
|
valloc(bigsbusmap, struct map, NDVMAMAPS(BIGSBUSMAP_SIZE));
|
|
valloc(mbutlmap, struct map, NDVMAMAPS(MBUTLMAP_SIZE));
|
|
valloc(sunpcmap, struct map, NDVMAMAPS(IOMMU_SUNPCMEM_SIZE));
|
|
} else {
|
|
valloc(dvmamap, struct map, NDVMAMAPS(dvmasize));
|
|
}
|
|
|
|
#else IOMMU
|
|
valloc(dvmamap, struct map, NDVMAMAPS(dvmasize));
|
|
#endif IOMMU
|
|
|
|
valloc(ncache, struct ncache, ncsize);
|
|
|
|
/* define macro to round up to integer size */
|
|
#define INTSZ(X) howmany((X), sizeof (int))
|
|
/* define macro to round up to nearest int boundary */
|
|
#define INTRND(X) roundup((X), sizeof (int))
|
|
|
|
#ifdef IPCSEMAPHORE
|
|
valloc(sem, struct sem, seminfo.semmns);
|
|
valloc(sema, struct semid_ds, seminfo.semmni);
|
|
valloc(semmap, struct map, seminfo.semmap);
|
|
valloc(sem_undo, struct sem_undo *, nproc);
|
|
valloc(semu, int, INTSZ(seminfo.semusz * seminfo.semmnu));
|
|
#endif IPCSEMAPHORE
|
|
|
|
#ifdef IPCMESSAGE
|
|
valloc(msg, char, INTRND(msginfo.msgseg * msginfo.msgssz));
|
|
valloc(msgmap, struct map, msginfo.msgmap);
|
|
valloc(msgh, struct msg, msginfo.msgtql);
|
|
valloc(msgque, struct msqid_ds, msginfo.msgmni);
|
|
#endif IPCMESSAGE
|
|
|
|
#ifdef IPCSHMEM
|
|
valloc(shmem, struct shmid_ds, shminfo.shmmni);
|
|
#endif IPCSHMEM
|
|
|
|
/*
|
|
* Allocate memory segment structures to describe physical memory.
|
|
* Allocate one more than the number of actual memory blocks we've
|
|
* been told about by the monitor, because we want to reclaim
|
|
* physical pages 0 and 1.
|
|
*
|
|
*/
|
|
valloc(memseg_base, struct memseg, memblocks + 1);
|
|
|
|
/*
|
|
* Allocate space for page structures and hash table.
|
|
* Get enough for all of physical memory minus amount
|
|
* dedicated to the system minus amount used for IOPBMAP.
|
|
*
|
|
* Leaving off the pages for the IOPBMAP is necessary so
|
|
* those pages can't ever be made be cacheable.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* use maxmem here to *not* back IOPBMEM with page structures
|
|
*/
|
|
|
|
bytes_used = (u_int)v - KERNELBASE;
|
|
bytes_avail = mmu_ptob(maxmem) - bytes_used;
|
|
npages = bytes_avail / (PAGESIZE + sizeof (struct page));
|
|
page_base = maxmem - npages;
|
|
|
|
valloc(pp, struct page, npages);
|
|
|
|
/*
|
|
* Compute hash size and round up to the nearest power of two.
|
|
*/
|
|
page_hashsz = npages / PAGE_HASHAVELEN;
|
|
for (i = (0x80 << ((sizeof (int) - 1) * NBBY)); i != 0; i >>= 1) {
|
|
if ((page_hashsz & i) != 0) {
|
|
page_hashsz = i << 1;
|
|
break;
|
|
}
|
|
}
|
|
valloc(page_hash, struct page *, page_hashsz);
|
|
|
|
econtig = (addr_t)roundup((u_int)v, PAGESIZE);
|
|
/* panic if valloc'd over tmpptes */
|
|
|
|
if ((unsigned)(econtig - KERNELBASE) > (unsigned)PA_TMPPTES) {
|
|
panic("valloc'd past tmpptes");
|
|
}
|
|
|
|
#if 0
|
|
/* bugid #1151376 */
|
|
if ((unsigned)econtig > (unsigned)tmpptes) {
|
|
panic("tmpptes too low");
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* For the purpose of setting up the kernel's page tables, which
|
|
* is done below, after hat_init, we have to add in the segu segment,
|
|
* even though is hasn't been created yet. Note that this assumes
|
|
* that seg_alloc will place the segu segment starting at econtig.
|
|
* This happens because there is no cache alignment that has to
|
|
* be dealt with.
|
|
*/
|
|
eecontig = (addr_t)((u_int)econtig + nproc * ptob (SEGU_PAGES));
|
|
|
|
/*
|
|
* Non-recoverable conditions. Need to check for
|
|
* this situation before checking eecontig below.
|
|
* Else may suspect the wrong problem.
|
|
*/
|
|
|
|
if ((u_int)v > TOPSEGBASE)
|
|
halt("out of virtual memory");
|
|
unixsize = mmu_btopr(v - KERNELBASE);
|
|
if (unixsize >= physmem - 8 * UPAGES)
|
|
halt("out of physical memory");
|
|
|
|
/*
|
|
* Need to check for the problem of nproc being to large here
|
|
* because we could step on tmpptes and die ungracefully later
|
|
* during hat_ptecopy().
|
|
* This check obsoletes the check later in startup just before
|
|
* the segu segment is actually created.
|
|
* XXX--Can get more space for segu by relocating VA_TMPPTES to
|
|
* just below TOPSEGBASE (8meg of kvm can be recovered).
|
|
* Too late for integration of that fix.
|
|
* XXX--Also should recalculate nproc if eecontig too large.
|
|
* Then print message so user knows about the adjustment.
|
|
*/
|
|
if (eecontig >= (addr_t)(roundup((u_int)tmpptes,
|
|
(u_int)L3PTSIZE)-L3PTSIZE))
|
|
panic("insufficient kvm for segu: nproc(MAXUSERS) too big?");
|
|
|
|
/*
|
|
* Set up the kernel text mappings, again.
|
|
* %%% maybe we can actually use a L1 PTE
|
|
* do map in kernel text, data, bss?
|
|
*/
|
|
for (i = 0; i < firstpage; i++)
|
|
tmpptes[i].ptpe_int = PTEOF(0, i, MMU_STD_SRWX, 1);
|
|
/*
|
|
* Map in the valloc'd structures.
|
|
* Note that the vac has not been turned on; we are just
|
|
* setting the cachaeble bit right now, so that when the
|
|
* vac is later turned on, these data structures will be
|
|
* cacheable.
|
|
*/
|
|
j = mmu_btop(CA2PA(econtig-1));
|
|
for (i = firstpage; i <= j; i++) {
|
|
#ifdef VAC
|
|
if (cache && use_cache &&
|
|
((i < kern_nc_top_pp) || (i >= kern_nc_end_pp)))
|
|
tmpptes[i].ptpe_int = PTEOF(0, i, MMU_STD_SRWX, 1);
|
|
else
|
|
#endif VAC
|
|
tmpptes[i].ptpe_int = PTEOF(0, i, MMU_STD_SRWX, 0);
|
|
}
|
|
/*
|
|
* unmap the rest of the pages in the L3 past eecontig
|
|
* Else they remain mapped in forever.
|
|
*/
|
|
i = mmu_btop(CA2PA(eecontig-1)) + 1;
|
|
j = mmu_btop(CA2PA((addr_t)((u_int)eecontig | (L3PTSIZE - 1))));
|
|
for (; i <= j ; i ++)
|
|
tmpptes[i].ptpe_int = 0;
|
|
|
|
mmu_flushall();
|
|
|
|
#ifndef SAS_FAST
|
|
bzero((caddr_t)vvm, (u_int)(econtig - vvm));
|
|
#else SAS_FAST
|
|
/* Hardware simulator has already cleared memory */
|
|
bzero((caddr_t)vvm, 20);
|
|
#endif SAS_FAST
|
|
/*
|
|
* Initialize VM system, and map kernel address space.
|
|
*/
|
|
hat_init();
|
|
/* Fun begins, just as in pegasus */
|
|
for (i = 0 - L3PTSIZE,
|
|
kptp = &kl2pts[NKL2PTS - 1].ptpe[NL2PTEPERPT - 1];
|
|
/* Remember, we want to go below the lowest address */
|
|
i >= TOPSEGBASE;
|
|
i -= L3PTSIZE, kptp--)
|
|
{
|
|
#ifndef NOPROM
|
|
if (avail_at_vaddr(i) < L3PTSIZE)
|
|
continue;
|
|
#endif
|
|
ptbl = hat_ptblreserve((addr_t)i, 3);
|
|
pte = ptbltopte(ptbl);
|
|
|
|
hat_ptecopy(&(tmpptes[mmu_btop(i - KERNELBASE)].pte), pte);
|
|
kptp->ptp.PageTablePointer = ptetota(pte);
|
|
kptp->ptp.EntryType = MMU_ET_PTP;
|
|
mmu_flushall();
|
|
}
|
|
kptp_max = kptp;
|
|
for (i = KERNELBASE, kptp = &kl2pts[0].ptpe[0];
|
|
i < (u_int)eecontig;
|
|
i += L3PTSIZE, kptp++)
|
|
{
|
|
ptbl = hat_ptblreserve((addr_t)i, 3);
|
|
pte = ptbltopte(ptbl);
|
|
hat_ptecopy(&(tmpptes[mmu_btop(i - KERNELBASE)].pte), pte);
|
|
kptp->ptp.PageTablePointer = ptetota(pte);
|
|
kptp->ptp.EntryType = MMU_ET_PTP;
|
|
mmu_flushall();
|
|
}
|
|
for (j = 0; j < nmod; j++) {
|
|
i = VA_PERCPU + (j * PERCPUSIZE);
|
|
kptp = &kl2pts[(i - KERNELBASE) >> 24].ptpe[(j * PERCPUSIZE)
|
|
>> 18];
|
|
ptbl = hat_ptblreserve((addr_t)i, 3);
|
|
pte = ptbltopte(ptbl);
|
|
hat_ptecopy(&(tmpptes[mmu_btop(i - KERNELBASE)].pte), pte);
|
|
kptp->ptp.PageTablePointer = ptetota(pte);
|
|
kptp->ptp.EntryType = MMU_ET_PTP;
|
|
|
|
mmu_flushall();
|
|
}
|
|
/*
|
|
* Map our PerCPU area down to PerCPU_me
|
|
*/
|
|
kptp = kl2pts[(VA_PERCPUME - KERNELBASE) >> 24].ptpe;
|
|
kptp1 = kl2pts[(VA_PERCPU - KERNELBASE) >> 24].ptpe;
|
|
kptp->ptp = kptp1->ptp;
|
|
|
|
mmu_flushall();
|
|
|
|
kptp_min = kptp;
|
|
/*
|
|
* Next we need to copy the level 2 page tables into our array.
|
|
* We need to make sure that all the entries we didn't fill in
|
|
* above get marked invalid. Also, we may leave out
|
|
* some of the tables, since there is a hole in the address space.
|
|
*/
|
|
|
|
for (kptp = kptp_min; kptp <= kptp_max; kptp++) {
|
|
if (kptp->ptp.EntryType != MMU_ET_PTP)
|
|
kptp->ptp.EntryType = MMU_ET_INVALID;
|
|
|
|
mmu_flushall();
|
|
}
|
|
/*
|
|
* Walk through the level 2 tables, except the last one,
|
|
* copying ones with real pages.
|
|
*/
|
|
|
|
for (i = 0; i < NKL2PTS; i++) {
|
|
kptp = &kl1pt->ptpe[NL1PTEPERPT - NKL2PTS + i];
|
|
bytes_used = KERNELBASE + i * L2PTSIZE;
|
|
/*
|
|
* There is a hole between eecontig and V_WKBASE_ADDR,
|
|
* which is the last piece of the kernel address.
|
|
* We invalidate the hole, but map in the rest.
|
|
*/
|
|
if (bytes_used >= (u_int)eecontig &&
|
|
bytes_used < (V_WKBASE_ADDR & ~(L2PTSIZE-1)) &&
|
|
bytes_used != ((u_int)tmpptes & ~(L2PTSIZE-1)) &&
|
|
bytes_used != VA_PERCPUME &&
|
|
bytes_used != VA_PERCPU &&
|
|
!(bytes_used >= (TOPSEGBASE & ~(L2PTSIZE-1)) &&
|
|
bytes_used < roundup(TOPSEGLIMIT, L2PTSIZE))) {
|
|
kptp->ptp.EntryType = MMU_ET_INVALID;
|
|
continue;
|
|
}
|
|
ptbl = hat_ptblreserve((addr_t)bytes_used, 2);
|
|
pte = ptbltopte(ptbl);
|
|
hat_ptecopy((struct pte *)&kl2pts[i].ptpe[0], pte);
|
|
kptp->ptp.PageTablePointer = ptetota(pte);
|
|
kptp->ptp.EntryType = MMU_ET_PTP;
|
|
#ifdef MULTIPROCESSOR
|
|
/*
|
|
* keep track of the level-2 page table for the
|
|
* common per-CPU area, so that vm_hat.c can correctly
|
|
* initialize the percpu-area for all the CPUs.
|
|
*/
|
|
if (bytes_used == VA_PERCPU)
|
|
percpu_ptbl = ptbl;
|
|
|
|
/*
|
|
* keep track of the page table pointer to CPU 0's
|
|
* level-2 page table for its per-CPU area, so that
|
|
* hat_map_percpu can map in the appropriate CPU's
|
|
* per-CPU area when a process migrates to a different
|
|
* CPU. presumably, we are bringing up CPU 0 here!
|
|
* this same record keeping is also done in vm_hat.c
|
|
* (hat_percpu_setup) for the other CPUs.
|
|
*/
|
|
if (bytes_used == VA_PERCPUME)
|
|
percpu_ptpe[0].ptp = kptp->ptp;
|
|
#endif MULTIPROCESSOR
|
|
|
|
mmu_flushall();
|
|
}
|
|
|
|
/* we just copied level2 entries for 256K worht of space for
|
|
* eecontig. Which means we copied extra stuff that points to
|
|
* l3 maps in PA_TMPPTES. This is bad. unmap it right now
|
|
* Make sure all maps point to within our ptes-eptes range
|
|
* or they are unmapped at a level within the kernels l1 and
|
|
* pte-eptes range.
|
|
* Else bad programs can jump here and cause translation errors
|
|
* and panic. We need to avoid panics.
|
|
*/
|
|
|
|
unmap_to_end_rgn((((u_int)eecontig + L3PTSIZE) & ~(L3PTSIZE-1)));
|
|
|
|
mmu_flushall();
|
|
|
|
/*
|
|
* Finish setting up the kernel address space,
|
|
* and activate it.
|
|
*/
|
|
hat_setup_kas();
|
|
/*
|
|
* Dont need tmpptes anymore, so lets free the pages.
|
|
* Prom will think it has a hole since this wont cause
|
|
* it to free the pages it used for pte's for our mapping
|
|
* but we wont see this as a hole.
|
|
* %%% Need a promlib call from unmapping.
|
|
*/
|
|
(void) OBP_V2_UNMAP((caddr_t)tmpptes, TMPPTES);
|
|
|
|
/*
|
|
* We have copied the L1 for the tmppte space from our old mapping.
|
|
* After that we unmapped the tmpptes mapping by doing OBP_V2_UNMAP.
|
|
* At this time we have L1-L2 mapping pointing to some physical pages
|
|
* at PA_TMPPTES that belongs to us in our freelist. Therefore depending
|
|
* on the use of these pages (PA_TMPPTES to PA_TMPPTES+TMPPTES), the
|
|
* mappings may be transient.
|
|
|
|
*/
|
|
|
|
i = ((u_int)tmpptes & ~(L2PTSIZE-1))/L2PTSIZE;
|
|
|
|
kl1pt->ptpe[i].ptpe_int = 0;
|
|
|
|
/*
|
|
* Initialize VM system, and map kernel address space.
|
|
*/
|
|
kvm_init();
|
|
/* officially start the monitor rom clock */
|
|
start_mon_clock();
|
|
|
|
#if !defined(SAS) && defined(VMEFIXED)
|
|
/* FIXME: these might be wrong, we'll know how VME works later */
|
|
disable_dvma();
|
|
for (dvmapage = 0; dvmapage < btoc(DVMASIZE); dvmapage++) {
|
|
segkmem_mapin(&kseg, CADDR1, MMU_PAGESIZE,
|
|
PROT_READ | PROT_WRITE,
|
|
(u_int)(dvmapage + btop(VME24D16_BASE)), 0);
|
|
if (poke((short *)CADDR1, TESTVAL) == 0)
|
|
break;
|
|
segkmem_mapin(&kseg, CADDR1, MMU_PAGESIZE,
|
|
PROT_READ | PROT_WRITE,
|
|
(u_int)(dvmapage + btop(VME24D32_BASE)), 0);
|
|
if (poke((short *)CADDR1, TESTVAL) == 0)
|
|
break;
|
|
segkmem_mapin(&kseg, CADDR1, MMU_PAGESIZE,
|
|
PROT_READ | PROT_WRITE,
|
|
(u_int)(dvmapage + btop(VME32D32_BASE)), 0);
|
|
if (poke((short *)CADDR1, TESTVAL) == 0)
|
|
break;
|
|
}
|
|
|
|
if (dvmapage < btoc(DVMASIZE)) {
|
|
printf("CAN'T HAVE PERIPHERALS IN RANGE 0 - %dKB\n",
|
|
DVMASIZE / 1024);
|
|
halt("dvma collision");
|
|
}
|
|
enable_dvma();
|
|
#endif
|
|
|
|
#ifdef TLBLOCK_ATOMS
|
|
atom_tlblock();
|
|
#endif TLBLOCK_ATOMS
|
|
|
|
#ifdef VAC
|
|
if (vac) {
|
|
if (use_cache) {
|
|
if (boothowto & RB_WRITETHRU)
|
|
vac_copyback = 0;
|
|
else if (boothowto & RB_COPYBACK)
|
|
vac_copyback = 1;
|
|
else if (boothowto & RB_ASKMORE) {
|
|
char cmi[64];
|
|
printf("Cache mode (WriteThru, CopyBack)? ");
|
|
gets(cmi);
|
|
if ((cmi[0] == 'w') || (cmi[0] == 'W'))
|
|
vac_copyback = 0;
|
|
if ((cmi[0] == 'c') || (cmi[0] == 'C'))
|
|
vac_copyback = 1;
|
|
}
|
|
vac_mode = (char *)0;
|
|
vac_init();
|
|
cache_on();
|
|
setcpudelay();
|
|
shm_alignment = vac_size;
|
|
if (vac_mode && vac_mode[0])
|
|
printf("VAC ENABLED in %s mode\n", vac_mode);
|
|
else
|
|
printf("VAC ENABLED\n");
|
|
} else {
|
|
cache = 0; /* indicate cache is off */
|
|
vac = 0; /* indicate cache is off */
|
|
vac_mode = (char *)0;
|
|
vac_init(); /* presumed to leave cacheoff*/
|
|
setcpudelay(); /* overkill? */
|
|
shm_alignment = PAGESIZE;
|
|
printf("VAC DISABLED\n");
|
|
}
|
|
} else {
|
|
if (use_cache) {
|
|
/*
|
|
* FIXME: this is only for bringup debugging purpose. They can be removed once
|
|
* we finish up the debugging since PROM will do the init and cache-on.
|
|
*/
|
|
extern void bpt_reg();
|
|
|
|
vac_init(); /*
|
|
* this routine will also turn on cahces
|
|
* if necessary
|
|
*/
|
|
setcpudelay();
|
|
if (vac_mode && vac_mode[0])
|
|
printf("%s: PAC ENABLED\n", vac_mode);
|
|
else
|
|
printf("PAC ENABLED\n");
|
|
#if defined(SUN4M_35)
|
|
if (!no_mix) {
|
|
if (use_mix) {
|
|
/* superscalar lives in breakpoint reg */
|
|
bpt_reg(MBAR_MIX, 0);
|
|
} else { /* don't want superscalar, shut it off */
|
|
bpt_reg(0, MBAR_MIX);
|
|
}
|
|
}
|
|
#else
|
|
if (use_mix) {
|
|
/* superscalar lives in breakpoint reg */
|
|
bpt_reg(MBAR_MIX, 0);
|
|
} else { /* don't want superscalar, shut it off */
|
|
bpt_reg(0, MBAR_MIX);
|
|
}
|
|
#endif
|
|
} else {
|
|
vac = 0; /* indicate cache is off */
|
|
cache = 0; /* indicate cache is off */
|
|
if (vac_mode && vac_mode[0])
|
|
printf("%s: PAC DISABLED\n", vac_mode);
|
|
else
|
|
printf("PAC DISABLED\n");
|
|
}
|
|
shm_alignment = PAGESIZE;
|
|
}
|
|
#endif VAC
|
|
|
|
if (debug_msg) /* Print out debugging messages. */
|
|
print_debug_msg();
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
for (i = 0; i < nmod; ++i)
|
|
PerCPU[i].cpuid = i;
|
|
#endif MULTIPROCESSOR
|
|
/*
|
|
* miscellaneous asynchronous fault initialization.
|
|
*/
|
|
for (i = 0; i < nmod; ++i) {
|
|
#ifdef MULTIPROCESSOR
|
|
atom_init(PerCPU[i].aflt_sync);
|
|
PerCPU[i].a_head = MAX_AFLTS - 1;
|
|
PerCPU[i].a_tail = MAX_AFLTS - 1;
|
|
}
|
|
atom_init(mm_flag);
|
|
#else MULTIPROCESSOR
|
|
a_head[i] = MAX_AFLTS - 1;
|
|
a_tail[i] = MAX_AFLTS - 1;
|
|
}
|
|
#endif MULTIPROCESSOR
|
|
atom_init(system_fatal);
|
|
atom_init(module_error);
|
|
|
|
/*
|
|
* Good {morning, afternoon, evening, night}.
|
|
* When printing memory, show the total as physmem less
|
|
* that stolen by a debugger.
|
|
*/
|
|
printf(version);
|
|
#ifdef NOPROM
|
|
if (cpu_str != (char *)0) printf("cpu = %s\n", cpu_str);
|
|
if (mod_str != (char *)0) printf("mod = %s\n", mod_str);
|
|
#else
|
|
printf("cpu = %s\n", mach_info.sys_name);
|
|
for (i = 0, modinfop = &mod_info[0]; i < mach_info.nmods;
|
|
++i, ++modinfop)
|
|
if (modinfop->mod_name)
|
|
printf("mod%d = %s (mid = %d)\n", i,
|
|
modinfop->mod_name,
|
|
modinfop->mid);
|
|
#endif
|
|
|
|
#ifdef NOPROM
|
|
printf("mem = %dK (0x%x)\n", availmem>>10, availmem);
|
|
#else
|
|
printf("mem = %dK (0x%x)\n", calc_memsize()>>10, calc_memsize());
|
|
#endif NOPROM
|
|
|
|
if (Syslimit + NCARGS + MINMAPSIZE > (addr_t)DEBUGSTART)
|
|
halt("system map tables too large");
|
|
|
|
/*
|
|
* Now we are using mapping for rdwr type routines and buffers
|
|
* are needed only for file system control information
|
|
* (i.e. cylinder groups, inodes, etc).
|
|
*
|
|
* nbuf is the maximum number of MAXBSIZE buffers that
|
|
* can be in the cache if it (dynamically) grows to that size.
|
|
* It should never be less than 2 since deadlock may result.
|
|
* We check for <2 since some people automatically patch
|
|
* their kernels to have nbuf=1 before dynamic control buffer
|
|
* allocation was implemented.
|
|
*
|
|
* Currently, we calculate nbuf as a percentage of maxmem.
|
|
* with MINBUF and MAXBUF as lower and upper bounds.
|
|
* Note this sets the cache size according to the system's ability
|
|
* to provide a cache -- not the systems need for a cache --
|
|
* which will depend on I/O load.
|
|
*/
|
|
|
|
if (nbuf < 2){
|
|
nbuf = MAX(((maxmem * NBPG) / BUFPERCENT / MAXBSIZE), MINBUF);
|
|
}
|
|
nbuf = MIN(nbuf, MAXBUF);
|
|
|
|
#ifndef NOPROM
|
|
/*
|
|
* Initialize more of the page structures. We walk through the
|
|
* list of physical memory, initializing all the pages that are
|
|
* backed by page structs.
|
|
*
|
|
* npages was set above.
|
|
*/
|
|
|
|
memsegp = memseg_base;
|
|
index = 0;
|
|
for (pmem = availmemory; pmem != 0; pmem = pmem->next) {
|
|
first_page = mmu_btop(pmem->address);
|
|
if (page_base > first_page)
|
|
first_page = page_base;
|
|
num_pages = mmu_btop(pmem->address + pmem->size) - first_page;
|
|
if (num_pages > npages - index)
|
|
num_pages = npages - index;
|
|
page_init(&pp[index], num_pages, first_page, memsegp++);
|
|
index += num_pages;
|
|
if (index >= npages)
|
|
break;
|
|
}
|
|
#else NOPROM
|
|
/* for SAS, memory is contiguous */
|
|
page_init(&pp[0], npages, page_base, memseg_base);
|
|
#endif NOPROM
|
|
|
|
#ifdef VAC
|
|
/*
|
|
* If we are running on a machine with a virtual address cache, we call
|
|
* mapin again for the valloc'ed memory now that we have the page
|
|
* structures set up so the hat routines leave the translations cached.
|
|
*/
|
|
if (vac)
|
|
segkmem_mapin(&kvalloc, (addr_t)vvm, (u_int)(econtig - vvm),
|
|
PROT_READ | PROT_WRITE, mapaddr, 0);
|
|
#endif VAC
|
|
|
|
/*
|
|
* Initialize callouts.
|
|
*/
|
|
callfree = callout;
|
|
for (i = 1; i < ncallout; i++)
|
|
callout[i-1].c_next = &callout[i];
|
|
#ifdef NOPROM
|
|
first_page = memsegs->pages_base;
|
|
if (first_page < mapaddr + btoc(econtig - vvm))
|
|
first_page = mapaddr + btoc(econtig - vvm);
|
|
memialloc(first_page, memsegs->pages_end);
|
|
#else NOPROM
|
|
/*
|
|
* Initialize memory free list. We free all the pages that are in
|
|
* our memseg list and aren't already taken by the valloc's.
|
|
*/
|
|
for (memsegp = memsegs; memsegp; memsegp = memsegp->next) {
|
|
first_page = memsegp->pages_base;
|
|
if (first_page < mapaddr + btoc(econtig - vvm))
|
|
first_page = mapaddr + btoc(econtig - vvm);
|
|
memialloc(first_page, memsegp->pages_end);
|
|
}
|
|
#endif NOPROM
|
|
|
|
printf("avail mem = %d\n", ctob(freemem));
|
|
|
|
/*
|
|
* Initialize kernel memory allocator.
|
|
*/
|
|
kmem_init();
|
|
|
|
/*
|
|
* Initialize resource maps.
|
|
*/
|
|
rminit(kernelmap, (long)(SYSPTSIZE - 1), (u_long)1,
|
|
"kernel map", MIN(4 * nproc, SYSPTSIZE/2));
|
|
rminit(bufmap, (long)(BUFPAGES - 1), (u_long)1,
|
|
"buffer map", BUFPAGES/2);
|
|
rminit(heapmap, (long)(HEAPPAGES - 1), (u_long)1,
|
|
"heap map", HEAPPAGES/2);
|
|
rminit(iopbmap, (long)ctob(IOPBMEM), (u_long)DVMA,
|
|
"IOPB space", IOPBMAPSIZE);
|
|
#ifdef IOMMU
|
|
if (iom) {
|
|
if (cpu == CPU_SUN4M_690) {
|
|
rminit(vme24map, (long) VME24MAP_SIZE,
|
|
(u_long) VME24MAP_BASE, "vme24 map space",
|
|
(int) NDVMAMAPS(VME24MAP_SIZE));
|
|
|
|
rminit(vme32map, (long) VME32MAP_SIZE,
|
|
(u_long) VME32MAP_BASE, "vme32 map space",
|
|
(int) NDVMAMAPS(VME32MAP_SIZE));
|
|
} else {
|
|
rminit(altsbusmap, (long) ALTSBUSMAP_SIZE,
|
|
(u_long) ALTSBUSMAP_BASE, "alt.sbus map space",
|
|
(int) NDVMAMAPS(ALTSBUSMAP_SIZE));
|
|
}
|
|
|
|
rminit(sbusmap, (long) SBUSMAP_SIZE, (u_long) SBUSMAP_BASE,
|
|
"sbus map space", (int) NDVMAMAPS(SBUSMAP_SIZE));
|
|
|
|
rminit(bigsbusmap, (long) BIGSBUSMAP_SIZE,
|
|
(u_long) BIGSBUSMAP_BASE, "bigsbus map space",
|
|
(int) NDVMAMAPS(BIGSBUSMAP_SIZE));
|
|
|
|
rminit(sunpcmap, (long) IOMMU_SUNPCMEM_SIZE,
|
|
(u_long) IOMMU_SUNPCMEM_BASE, "SunPC map space",
|
|
(int) NDVMAMAPS(IOMMU_SUNPCMEM_SIZE));
|
|
|
|
rminit(mbutlmap, (long) MBUTLMAP_SIZE, (u_long) MBUTLMAP_BASE,
|
|
"mbutl map space", (int) NDVMAMAPS(MBUTLMAP_SIZE));
|
|
|
|
/*
|
|
* make sure dvmamap and sbus are the same one
|
|
* NOTE: this assumes that dvmamap is used by SBUS
|
|
* drivers, and mb_hd.mh_map is used by
|
|
* VME driver.
|
|
*/
|
|
|
|
dvmamap = sbusmap;
|
|
|
|
if (cpu == CPU_SUN4M_690) {
|
|
/* defaults vme I/O to vme24map */
|
|
mb_hd.mh_map = vme24map;
|
|
} else {
|
|
mb_hd.mh_map = 0;
|
|
}
|
|
} else {
|
|
rminit(dvmamap, (long)(btoc(DVMASIZE) - IOPBMEM),
|
|
(u_long)IOPBMEM, "DVMA map space", NDVMAMAPS(dvmasize));
|
|
}
|
|
#else
|
|
rminit(dvmamap, (long)(dvmasize - IOPBMEM), (u_long)IOPBMEM,
|
|
"DVMA map space", NDVMAMAPS(dvmasize));
|
|
#endif
|
|
|
|
/*
|
|
* Allocate IOPB memory space just below the end of
|
|
* memory and map it to the first pages of DVMA space.
|
|
* The memory from maxmem to physmem has no page structs
|
|
* allocated to it. This is necessary so those pages cannot
|
|
* be cacheable. This is a cornerstone of our software cache
|
|
* consistency algorithm.
|
|
* NOTE: this assumes that the memory for IOPBMEM is physically
|
|
* contiguous.
|
|
*/
|
|
#ifndef NOPROM
|
|
/*
|
|
* Debuggers and monitor have already stolen their memory
|
|
* from the physmem structure.
|
|
*/
|
|
first_page = mmu_btop(lastpmem->address+lastpmem->size) - IOPBMEM;
|
|
if (first_page < mmu_btop(lastpmem->address)) {
|
|
if (dbug_mem) {
|
|
printf("Debugger Size: %d Too Big!\n",
|
|
mmu_ptob(dbug_mem));
|
|
panic("too many debugger pages\n");
|
|
/*NOTREACHED*/
|
|
} else {
|
|
printf("No room for IOPBMAP: last mem %d bytes\n",
|
|
mmu_ptob(lastpmem->size));
|
|
panic("no room for IOPBMAP");
|
|
/*NOTREACHED*/
|
|
}
|
|
}
|
|
segkmem_mapin(&kdvmaseg, DVMA, mmu_ptob(IOPBMEM),
|
|
PROT_READ | PROT_WRITE, first_page, 0);
|
|
#else NOPROM
|
|
segkmem_mapin(&kdvmaseg, DVMA, mmu_ptob(IOPBMEM),
|
|
PROT_READ | PROT_WRITE, maxmem, 0);
|
|
#endif NOPROM
|
|
|
|
#ifdef IOMMU
|
|
/* load tranlations for IOPBMEM in ioptes */
|
|
if (iom) {
|
|
/* init/turn on iommu for kernel */
|
|
iom_init();
|
|
for (i = 0; i < iommu_btop(mmu_ptob(IOPBMEM)); i++) {
|
|
#ifndef NOPROM
|
|
if (cpu == CPU_SUN4M_690) {
|
|
/* map iopbmem in vme24 range */
|
|
iom_dvma_pteload((int) (first_page + i),
|
|
IOMMU_IOPBMEM_BASE + iommu_ptob(i),
|
|
IOM_WRITE);
|
|
}
|
|
|
|
/* also map iopbmem in sbus range */
|
|
iom_dvma_pteload((int) (first_page + i),
|
|
IOMMU_SBUS_IOPBMEM_BASE + iommu_ptob(i),
|
|
IOM_WRITE);
|
|
#ifdef IOC
|
|
/*
|
|
* need to setup 'w' bit in IOC even though it's
|
|
* notused
|
|
*/
|
|
|
|
if (cpu == CPU_SUN4M_690) {
|
|
ioc_setup((int) iommu_ptob(i),
|
|
IOC_LINE_NOIOC | IOC_LINE_WRITE);
|
|
}
|
|
#endif IOC
|
|
|
|
#else
|
|
if (cpu == CPU_SUN4M_690) {
|
|
/* map iopbmem in vme24 range */
|
|
iom_dvma_pteload(maxmem + i,
|
|
IOMMU_IOPBMEM_BASE + iommu_ptob(i),
|
|
IOM_WRITE);
|
|
}
|
|
|
|
/* also map iopbmem in sbus range */
|
|
iom_dvma_pteload(maxmem + i,
|
|
IOMMU_SBUS_IOPBMEM_BASE + iommu_ptob(i),
|
|
IOM_WRITE);
|
|
|
|
/*
|
|
* need to setup 'w' bit in IOC even though it's
|
|
* notused
|
|
*/
|
|
|
|
if (cpu == CPU_SUN4M_690) {
|
|
ioc_setup(iommu_ptob(i),
|
|
IOC_LINE_NOIOC | IOC_LINE_WRITE);
|
|
}
|
|
|
|
#endif NOPROM
|
|
} /* end of for */
|
|
}
|
|
#endif IOMMU
|
|
|
|
/*
|
|
* Configure the system.
|
|
*/
|
|
startup_cpus();
|
|
configure(); /* set up devices */
|
|
memerr_init(); /* Make sure detect/correct is enabled */
|
|
maxmem = freemem;
|
|
|
|
/*
|
|
* Initialize the u-area segment type. We position it
|
|
* after the configured tables and buffers (whose end
|
|
* is given by econtig) and before TOPSEGBASE.
|
|
*/
|
|
v = econtig;
|
|
i = nproc * ptob(SEGU_PAGES);
|
|
if (v + i > (caddr_t) TOPSEGBASE)
|
|
panic("insufficient virtual space for segu: nproc too big?");
|
|
segu = seg_alloc(&kas, v, i);
|
|
if (segu == NULL)
|
|
panic("cannot allocate segu\n");
|
|
if (segu_create(segu, (caddr_t)NULL) != 0)
|
|
panic("segu_create segu");
|
|
|
|
/*
|
|
* Now create generic mapping segment. This mapping
|
|
* is at KMAPBASE thru KMAPLIMIT (KMAPBYTES long).
|
|
* If the total virtual address is greater than the
|
|
* amount of free memory that is available, then we trim
|
|
* back the segment size to that amount.
|
|
*/
|
|
v = (caddr_t)KMAPBASE;
|
|
i = (unsigned)KMAPBYTES;
|
|
if (i > mmu_ptob(freemem))
|
|
i = mmu_ptob(freemem);
|
|
segkmap = seg_alloc(&kas, v, i);
|
|
if (segkmap == NULL)
|
|
panic("cannot allocate segkmap");
|
|
a.prot = PROT_READ | PROT_WRITE;
|
|
if (segmap_create(segkmap, (caddr_t)&a) != 0)
|
|
panic("segmap_create segkmap");
|
|
|
|
ledping((int *)0); /* start LED light show */
|
|
|
|
(void) spl0(); /* allow interrupts */
|
|
}
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
#define MID2CPU(mid) ((mid)^8)
|
|
int okprocset = 0xF;
|
|
int uniprocessor = 0; /* default to MultiProcessor */
|
|
unsigned snid[NCPU]; /* saved node id's for each CPU */
|
|
struct dev_reg ctxpr[NCPU];
|
|
#endif MULTIPROCESSOR
|
|
|
|
startup_cpus()
|
|
{
|
|
#ifdef MULTIPROCESSOR
|
|
extern unsigned cpuX_startup[];
|
|
unsigned nodeid;
|
|
unsigned mid;
|
|
unsigned who;
|
|
unsigned ctxpa[NCPU];
|
|
unsigned multi;
|
|
int delays;
|
|
extern addr_t map_regs();
|
|
extern dnode_t prom_childnode();
|
|
|
|
/*
|
|
* Kick in other CPUs
|
|
*
|
|
* While the general mechanism here is pretty good, the specifics
|
|
* of how we figure which context table to use -- ugh, should allocate
|
|
* and construct them here, not earlier -- and physical addresses
|
|
* and such, are all very ugly and Sun-4M specific. Porting folks
|
|
* please take note ...
|
|
*
|
|
* It would be even nicer if we could make this look MORE like
|
|
* the cpus are just "normal devices" instead of doing all
|
|
* this horrible special-casing.
|
|
*
|
|
* okprocset sanitization:
|
|
* force boot processor on,
|
|
* only allow bits zero thru NCPU-1 to be on.
|
|
*/
|
|
okprocset &= (1<<nmod) - 1; /* limit indicies to sane values */
|
|
|
|
multi = 0;
|
|
/*
|
|
* Locate all supported processors.
|
|
*/
|
|
nodeid = prom_nextnode(0); /* root node */
|
|
for (nodeid = (unsigned) prom_childnode((dnode_t) nodeid);
|
|
(nodeid != 0) && (nodeid != -1);
|
|
nodeid = (unsigned) prom_nextnode((dnode_t) nodeid)) {
|
|
if ((prom_getproplen(nodeid, "mid") == sizeof mid) &&
|
|
(prom_getprop(nodeid, "mid", (caddr_t) &mid) != -1) &&
|
|
(okprocset & (1 << (who = MID2CPU(mid))))) {
|
|
#ifdef PC_prom_mailbox
|
|
struct dev_reg reg;
|
|
if ((prom_getproplen(nodeid, "mailbox") == sizeof reg) &&
|
|
(prom_getprop(nodeid, "mailbox", (caddr_t) ®) != -1))
|
|
PerCPU[who].prom_mailbox =
|
|
(u_char *) map_regs(reg.reg_addr, reg.reg_size, reg.reg_bustype);
|
|
#endif PC_prom_mailbox
|
|
ctxpa[who] = CA2PA(&contexts[who*nctxs].ptpe_int);
|
|
snid[who] = nodeid;
|
|
printf("cpu%d at Mbus 0x%x 0x%x\n",
|
|
who, mid, ctxpa[who]);
|
|
|
|
if (who != cpuid)
|
|
multi |= 1<<who;
|
|
}
|
|
}
|
|
/*
|
|
* If we have the potential of becoming a multiprocessor,
|
|
* examine the boot flags and maybe override the uniprocessor
|
|
* flag; note that the "-a" flag makes us ask whether the user
|
|
* wants to be uniprocessor or multiprocessor, and if the kernel
|
|
* had its uniprocessor flag patched on, we override that here.
|
|
*/
|
|
if (multi) {
|
|
if (boothowto & RB_MULTIPROCESSOR)
|
|
uniprocessor = 0;
|
|
else if (boothowto & RB_UNIPROCESSOR)
|
|
uniprocessor = 1;
|
|
else if (boothowto & RB_ASKMORE) {
|
|
char pmi[64];
|
|
printf("processor mode (Uniprocessor, Multiprocessor)? ");
|
|
gets(pmi);
|
|
if ((pmi[0] == 'u') || (pmi[0] == 'U'))
|
|
uniprocessor = 1;
|
|
if ((pmi[0] == 'm') || (pmi[0] == 'M'))
|
|
uniprocessor = 0;
|
|
}
|
|
}
|
|
/*
|
|
* If we are supposed to be uniprocessor -- possibly modified
|
|
* by boot flags or asking -- forget about all non-boot processors.
|
|
*/
|
|
if (uniprocessor)
|
|
multi = 0; /* do not multiprocess. */
|
|
/*
|
|
* If we still know of other processors, become a multiprocessor.
|
|
*/
|
|
if (multi) {
|
|
/*
|
|
* set up all PerCPU areas.
|
|
*/
|
|
for (who=0; who<NCPU; ++who)
|
|
if (multi & (1<<who)) {
|
|
struct user *up;
|
|
struct proc *pp;
|
|
|
|
up = &PerCPU[who].idleuarea;
|
|
pp = &PerCPU[who].idleproc;
|
|
*up = idleuarea;
|
|
*pp = idleproc;
|
|
PerCPU[who].cpuid = who;
|
|
pp->p_stat = SRUN;
|
|
pp->p_cpuid = who;
|
|
pp->p_pam = 1<<who;
|
|
PerCPU[who].uunix = &idleuarea;
|
|
PerCPU[who].masterprocp = &idleproc;
|
|
}
|
|
|
|
/*
|
|
* Initialize XC and MP services
|
|
*/
|
|
xc_init();
|
|
mp_init();
|
|
|
|
#ifdef VAC
|
|
/*
|
|
* Flush data from cache to memory so the other CPUs
|
|
* can see what we just wrote before they have their
|
|
* VACs turned on.
|
|
*/
|
|
if (vac)
|
|
vac_flushall();
|
|
#endif VAC
|
|
/*
|
|
* Call the other processors out of their holding patterns.
|
|
*/
|
|
for (who=0; who<NCPU; ++who)
|
|
if (multi & (1<<who)) {
|
|
ctxpr[who].reg_bustype = 0;
|
|
ctxpr[who].reg_addr = (addr_t)ctxpa[who];
|
|
ctxpr[who].reg_size = 0;
|
|
PerCPU[who].cpu_exists = 0;
|
|
delays = 0;
|
|
(void)prom_startcpu(snid[who], &ctxpr[who], 0, (addr_t)cpuX_startup);
|
|
klock_exit();
|
|
while (PerCPU[who].cpu_exists != 1) {
|
|
DELAY(0x10000);
|
|
delays++;
|
|
if (delays > 20)
|
|
break;
|
|
}
|
|
klock_enter();
|
|
if ((delays > 20) && (debug_msg))
|
|
printf("cpu %d won't startup\n", who);
|
|
}
|
|
printf("entering multiprocessor mode\n");
|
|
uniprocessor = 0;
|
|
} else {
|
|
printf("entering uniprocessor mode\n");
|
|
uniprocessor = 1;
|
|
}
|
|
|
|
#endif MULTIPROCESSOR
|
|
}
|
|
|
|
struct bootf {
|
|
char let;
|
|
short bit;
|
|
} bootf[] = {
|
|
'a', RB_ASKNAME,
|
|
'b', RB_NOBOOTRC,
|
|
'c', RB_COPYBACK,
|
|
'd', RB_DEBUG,
|
|
'h', RB_HALT,
|
|
'i', RB_INITNAME,
|
|
'm', RB_MULTIPROCESSOR,
|
|
'q', RB_ASKMORE,
|
|
's', RB_SINGLE,
|
|
't', RB_WRITETHRU,
|
|
'u', RB_UNIPROCESSOR,
|
|
'w', RB_WRITABLE,
|
|
0, 0,
|
|
};
|
|
|
|
char *initpath[] = {
|
|
"/sbin/",
|
|
"/single/",
|
|
"/etc/",
|
|
"/bin/",
|
|
"/usr/etc/",
|
|
"/usr/bin/",
|
|
0
|
|
};
|
|
char *initname = "init";
|
|
|
|
/*
|
|
* Parse the boot line to determine boot flags.
|
|
*/
|
|
bootflags()
|
|
{
|
|
register char *cp;
|
|
register int i;
|
|
|
|
cp = prom_bootargs();
|
|
if (cp) {
|
|
while (*cp && (*cp != '-'))
|
|
++cp;
|
|
if (*cp++ == '-')
|
|
do {
|
|
for (i = 0; bootf[i].let; i++) {
|
|
if (*cp == bootf[i].let) {
|
|
boothowto |= bootf[i].bit;
|
|
break;
|
|
}
|
|
}
|
|
cp++;
|
|
} while (bootf[i].let && *cp && (*cp != ' '));
|
|
|
|
if (boothowto & RB_INITNAME) {
|
|
while (*cp && (*cp == ' '))
|
|
++cp;
|
|
initname = cp;
|
|
}
|
|
}
|
|
if (boothowto & RB_HALT) {
|
|
printf("halted by -h flag\n");
|
|
prom_enter_mon();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Start the initial user process.
|
|
* The program [initname] is invoked with one argument
|
|
* containing the boot flags.
|
|
*/
|
|
icode(rp)
|
|
struct regs *rp;
|
|
{
|
|
struct execa {
|
|
char *fname;
|
|
char **argp;
|
|
char **envp;
|
|
} *ap;
|
|
char *ucp, **uap, *arg0, *arg1;
|
|
char *str;
|
|
char **path;
|
|
static char pathbuf[128];
|
|
int i;
|
|
extern char *strcpy();
|
|
struct proc *p = u.u_procp;
|
|
int errors = 0;
|
|
|
|
/*
|
|
* Wait for pageout to wake us up. This is to insure that pageout
|
|
* is created before init starts running. I use &proc[1] as a convient
|
|
* address because nobody else uses it so there won't be a conflict.
|
|
*/
|
|
(void) sleep((caddr_t)&proc[1], PZERO);
|
|
|
|
u.u_error = 0; /* paranoid */
|
|
u.u_ar0 = (int *)rp;
|
|
|
|
/*
|
|
* Allocate user address space.
|
|
*/
|
|
p->p_as = as_alloc();
|
|
|
|
/*
|
|
* Make a (1 page) user stack.
|
|
*/
|
|
if (u.u_error = as_map(p->p_as, (addr_t)(USRSTACK - PAGESIZE),
|
|
PAGESIZE, segvn_create, zfod_argsp)) {
|
|
printf("Can't invoke %s, error %d\n", initname, u.u_error);
|
|
panic("icode");
|
|
}
|
|
|
|
for (path = initpath; *path; path++) {
|
|
u.u_error = 0; /* paranoid */
|
|
/*
|
|
* Move out the boot flag argument.
|
|
*/
|
|
ucp = (char *)USRSTACK;
|
|
(void) subyte(--ucp, 0); /* trailing zero */
|
|
for (i = 0; bootf[i].let; i++) {
|
|
if (boothowto & bootf[i].bit)
|
|
(void) subyte(--ucp, bootf[i].let);
|
|
}
|
|
(void) subyte(--ucp, '-'); /* leading hyphen */
|
|
arg1 = ucp;
|
|
|
|
/*
|
|
* Build a pathname.
|
|
*/
|
|
if (*initname != '/') {
|
|
(void) strcpy(pathbuf, *path);
|
|
for (str = pathbuf; *str; str++)
|
|
;
|
|
(void) strcpy(str, initname);
|
|
} else
|
|
(void) strcpy(pathbuf, initname);
|
|
|
|
/*
|
|
* Move out the file name (also arg 0).
|
|
*/
|
|
i = 0;
|
|
while (pathbuf[i]) {
|
|
i += 1; /* size the name */
|
|
}
|
|
while (i >= 0) {
|
|
(void) subyte(--ucp, pathbuf[i]);
|
|
i -= 1;
|
|
}
|
|
arg0 = ucp;
|
|
|
|
/*
|
|
* Move out the arg pointers.
|
|
*/
|
|
uap = (char **)((int)ucp & ~(NBPW-1));
|
|
(void) suword((caddr_t)--uap, 0); /* terminator */
|
|
(void) suword((caddr_t)--uap, (int)arg1);
|
|
(void) suword((caddr_t)--uap, (int)arg0);
|
|
|
|
/*
|
|
* Point at the arguments.
|
|
*/
|
|
u.u_ap = u.u_arg;
|
|
ap = (struct execa *)u.u_ap;
|
|
ap->fname = arg0;
|
|
ap->argp = uap;
|
|
ap->envp = 0;
|
|
|
|
/*
|
|
* Now let exec do the hard work.
|
|
*/
|
|
execve();
|
|
if (!u.u_error)
|
|
break;
|
|
else {
|
|
errors++;
|
|
printf("Can't invoke %s, error %d\n", pathbuf,
|
|
u.u_error);
|
|
}
|
|
|
|
/*
|
|
* The user passed in the full path name for init so don't
|
|
* bother trying all of the paths.
|
|
*/
|
|
if (*initname == '/')
|
|
break;
|
|
}
|
|
if (u.u_error)
|
|
panic("icode");
|
|
if (errors)
|
|
printf("init is %s\n", pathbuf);
|
|
}
|
|
|
|
#ifdef PGINPROF
|
|
/*
|
|
* Return the difference (in microseconds)
|
|
* between the current time and a previous
|
|
* time as represented by the arguments.
|
|
*/
|
|
vmtime(otime, olbolt)
|
|
register int otime, olbolt;
|
|
{
|
|
|
|
return (((time-otime)*HZ + lbolt-olbolt)*(1000000/HZ));
|
|
}
|
|
#endif PGINPROF
|
|
|
|
/*
|
|
* Clear registers on exec
|
|
*/
|
|
setregs(entry)
|
|
register u_long entry;
|
|
{
|
|
register struct regs *rp;
|
|
register struct pcb *pcb = &u.u_pcb;
|
|
|
|
/*
|
|
* Initialize user registers.
|
|
*/
|
|
rp = (struct regs *)u.u_ar0;
|
|
rp->r_g1 = rp->r_g2 = rp->r_g3 = rp->r_g4 = rp->r_g5 =
|
|
rp->r_g6 = rp->r_g7 = rp->r_o0 = rp->r_o1 = rp->r_o2 =
|
|
rp->r_o3 = rp->r_o4 = rp->r_o5 = rp->r_o7 = 0;
|
|
|
|
rp->r_psr = PSL_USER;
|
|
rp->r_pc = entry;
|
|
rp->r_npc = entry + 4;
|
|
u.u_eosys = JUSTRETURN;
|
|
|
|
/*
|
|
* Throw out old user windows, init window buf.
|
|
*/
|
|
trash_user_windows();
|
|
|
|
/*
|
|
* If the process's parent used the fpu, the child
|
|
* inherited a copy of the fpu context, but if we're
|
|
* going to exec, we should toss this context in
|
|
* order to avoid the grok of, say, init (for some
|
|
* reason) getting a floating point context and
|
|
* forcing the overhead of same on all its children.
|
|
* If the child then wants to use the fpu, then let it
|
|
* pay for it (by taking a FPU trap, which will cause
|
|
* a context to be allocated).
|
|
*/
|
|
|
|
if (pcb->pcb_fpctxp) {
|
|
kmem_free((caddr_t) pcb->pcb_fpctxp, sizeof (struct fpu));
|
|
pcb->pcb_fpctxp = (struct fpu *) 0;
|
|
/*
|
|
* This flag is mostly historical
|
|
*/
|
|
pcb->pcb_fpflags = FP_UNINITIALIZED;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* Send an interrupt to process.
|
|
*
|
|
* When using new signals user code must do a
|
|
* sys #139 to return from the signal, which
|
|
* calls sigcleanup below, which resets the
|
|
* signal mask and the notion of onsigstack,
|
|
* and returns from the signal handler.
|
|
*/
|
|
sendsig(p, sig, mask)
|
|
void (*p)();
|
|
int sig, mask;
|
|
{
|
|
register int *regs;
|
|
struct sigframe {
|
|
struct rwindow rwin; /* save area for new sp */
|
|
struct sigargs { /* arguments for handler */
|
|
int sig;
|
|
int code;
|
|
struct sigcontext *scp;
|
|
char *addr;
|
|
} args;
|
|
struct sigcontext sc; /* sigcontext for signal */
|
|
};
|
|
register struct sigframe *fp;
|
|
int oonstack;
|
|
register int i;
|
|
register struct pcb *pcb = &u.u_pcb;
|
|
|
|
/*
|
|
* Make sure the current last user window has been flushed to
|
|
* the stack save area before we change the sp.
|
|
*/
|
|
flush_user_windows_to_stack();
|
|
regs = u.u_ar0;
|
|
oonstack = u.u_onstack;
|
|
if (!u.u_onstack && (u.u_sigonstack & sigmask(sig))) {
|
|
fp = (struct sigframe *)
|
|
((int)u.u_sigsp - SA(sizeof (struct sigframe)));
|
|
u.u_onstack = 1;
|
|
} else {
|
|
fp = (struct sigframe *)
|
|
((int)regs[SP] - SA(sizeof (struct sigframe)));
|
|
}
|
|
/*
|
|
* Allocate and validate space for the signal handler
|
|
* context. on_fault will catch any faults.
|
|
*/
|
|
if (((int)fp & (STACK_ALIGN-1)) != 0 ||
|
|
(caddr_t)fp >= (caddr_t)KERNELBASE || on_fault()) {
|
|
/*
|
|
* Process has trashed its stack; give it an illegal
|
|
* instruction to halt it in its tracks.
|
|
*/
|
|
printf("sendsig: bad signal stack pid=%d, sig=%d\n",
|
|
u.u_procp->p_pid, sig);
|
|
printf("sigsp = 0x%x, action = 0x%x, upc = 0x%x\n",
|
|
fp, p, regs[PC]);
|
|
u.u_signal[SIGILL] = SIG_DFL;
|
|
sig = sigmask(SIGILL);
|
|
u.u_procp->p_sigignore &= ~sig;
|
|
u.u_procp->p_sigcatch &= ~sig;
|
|
u.u_procp->p_sigmask &= ~sig;
|
|
psignal(u.u_procp, SIGILL);
|
|
return;
|
|
}
|
|
/*
|
|
* Put signal and return info on signal stack.
|
|
*/
|
|
fp->sc.sc_onstack = oonstack;
|
|
fp->sc.sc_mask = mask;
|
|
fp->sc.sc_sp = regs[SP];
|
|
fp->sc.sc_pc = regs[PC];
|
|
fp->sc.sc_npc = regs[nPC];
|
|
fp->sc.sc_psr = regs[PSR];
|
|
fp->sc.sc_g1 = regs[G1]; /* save for sigcleanup syscall number */
|
|
fp->sc.sc_o0 = regs[O0]; /* save for sigcleanup scp */
|
|
fp->sc.sc_wbcnt = pcb->pcb_wbcnt; /* save outstanding windows */
|
|
for (i = 0; i < pcb->pcb_wbcnt; i++) {
|
|
fp->sc.sc_spbuf[i] = pcb->pcb_spbuf[i];
|
|
bcopy((caddr_t)&pcb->pcb_wbuf[i],
|
|
(caddr_t)fp->sc.sc_wbuf[i],
|
|
sizeof (struct rwindow));
|
|
}
|
|
fp->args.sig = sig;
|
|
/*
|
|
* Since we flushed the user's windows and we are changing his
|
|
* stack pointer, the window that the user will return to will
|
|
* be restored from the save area in the frame we are setting up.
|
|
* We copy in save area for old stack pointer so that
|
|
* debuggers can do a proper stack backtrace from the signal handler.
|
|
*/
|
|
if (pcb->pcb_wbcnt == 0) {
|
|
bcopy((caddr_t)regs[SP], (caddr_t)&fp->rwin,
|
|
sizeof (struct rwindow));
|
|
}
|
|
switch (sig) {
|
|
|
|
case SIGILL:
|
|
case SIGFPE:
|
|
case SIGEMT:
|
|
case SIGBUS:
|
|
case SIGSEGV:
|
|
/*
|
|
* These signals get a code and an addr.
|
|
*/
|
|
fp->args.addr = u.u_addr;
|
|
fp->args.code = u.u_code;
|
|
u.u_addr = (char *)0;
|
|
u.u_code = 0;
|
|
break;
|
|
|
|
default:
|
|
fp->args.addr = (char *)0;
|
|
fp->args.code = 0;
|
|
break;
|
|
}
|
|
fp->args.scp = &fp->sc;
|
|
no_fault();
|
|
pcb->pcb_wbcnt = 0; /* let user go on */
|
|
regs[SP] = (int)fp;
|
|
regs[PC] = (int)p;
|
|
regs[nPC] = (int)p + 4;
|
|
}
|
|
|
|
/*
|
|
* Routine to cleanup state after a signal
|
|
* has been taken. Reset signal mask and
|
|
* notion of on signal stack from context
|
|
* left there by sendsig (above). Pop these
|
|
* values and perform rti.
|
|
*/
|
|
sigcleanup()
|
|
{
|
|
register struct sigcontext *scp;
|
|
register int *regs;
|
|
register int i, j;
|
|
register struct pcb *pcb = &u.u_pcb;
|
|
extern int nwindows;
|
|
|
|
regs = u.u_ar0;
|
|
scp = (struct sigcontext *)((caddr_t)regs[O0]);
|
|
if ((caddr_t)scp >= (caddr_t)KERNELBASE || ((int)scp & 0x3))
|
|
return;
|
|
if (on_fault())
|
|
return;
|
|
/* make sure pc's are aligned */
|
|
if ((scp->sc_pc & 0x3) || (scp->sc_npc & 0x3))
|
|
return;
|
|
/*
|
|
* Make sure we return to the old window we saved in sendsig.
|
|
*/
|
|
flush_user_windows();
|
|
u.u_onstack = scp->sc_onstack & 01;
|
|
u.u_procp->p_sigmask = scp->sc_mask & ~cantmask;
|
|
regs[SP] = scp->sc_sp;
|
|
regs[PC] = scp->sc_pc;
|
|
regs[nPC] = scp->sc_npc;
|
|
regs[PSR] = regs[PSR] & ~PSL_USERMASK | scp->sc_psr & PSL_USERMASK;
|
|
regs[G1] = scp->sc_g1; /* restore regs signal handler can't restore */
|
|
regs[O0] = scp->sc_o0;
|
|
if ((u_int)scp->sc_wbcnt < nwindows) { /* restore outstanding windows */
|
|
j = pcb->pcb_wbcnt;
|
|
for (i = 0; i < scp->sc_wbcnt; i++) {
|
|
pcb->pcb_spbuf[j + i] = scp->sc_spbuf[i];
|
|
bcopy((caddr_t)scp->sc_wbuf[i],
|
|
(caddr_t)&pcb->pcb_wbuf[j + i],
|
|
sizeof (struct rwindow));
|
|
}
|
|
pcb->pcb_wbcnt += scp->sc_wbcnt;
|
|
} else {
|
|
pcb->pcb_wbcnt = 0;
|
|
}
|
|
no_fault();
|
|
/*
|
|
* Avoid mucking with carry flag and return regs on return to user.
|
|
*/
|
|
u.u_eosys = JUSTRETURN;
|
|
}
|
|
|
|
int waittime = -1;
|
|
|
|
boot(howto)
|
|
int howto;
|
|
{
|
|
static short prevflag = 0;
|
|
static short bufprevflag = 0;
|
|
extern void vfs_syncall();
|
|
int s;
|
|
extern char *bootstr;
|
|
extern void prom_reboot();
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
xc_attention(); /* simplify the world */
|
|
#endif MULTIPROCESSOR
|
|
consdev = rconsdev;
|
|
consvp = rconsvp;
|
|
start_mon_clock();
|
|
#ifdef PROM_PARANOIA
|
|
prom_eprintf("PROM_PARANOIA: inside boot()\n");
|
|
#endif PROM_PARANOIA
|
|
if ((howto & RB_NOSYNC) == 0 && waittime < 0 && bfreelist[0].b_forw &&
|
|
bufprevflag == 0) {
|
|
bufprevflag = 1; /* prevent panic recursion */
|
|
waittime = 0;
|
|
s = spl0();
|
|
vfs_syncall();
|
|
(void) splx(s);
|
|
}
|
|
#ifdef NOPROM
|
|
asm("t 255");
|
|
#else
|
|
/* extreme priority; allow clock interrupts to monitor at level 14 */
|
|
/* but don't allow UART interrupts */
|
|
s = splzs();
|
|
if (howto & RB_HALT) {
|
|
halt((char *)NULL);
|
|
howto &= ~RB_HALT;
|
|
} else {
|
|
if (howto & RB_DUMP && prevflag == 0) {
|
|
if ((boothowto & RB_DEBUG) != 0 && nopanicdebug == 0) {
|
|
CALL_DEBUG();
|
|
}
|
|
prevflag = 1; /* prevent panic recursion */
|
|
dumpsys();
|
|
}
|
|
}
|
|
printf("rebooting...\n");
|
|
|
|
INT_REGS->sys_mset = IR_ENA_INT; /* disable all interrupts */
|
|
|
|
start_mon_clock();
|
|
if (howto & RB_STRING)
|
|
prom_reboot(bootstr);
|
|
else
|
|
prom_reboot(howto & RB_SINGLE ? "-s" : "");
|
|
/* should never get here */
|
|
(void) splx(s);
|
|
#endif NOPROM
|
|
}
|
|
|
|
/*
|
|
* Machine-dependent part of dump header initialization;
|
|
* mark the static pages used by the kernel.
|
|
*/
|
|
/* ARGSUSED */
|
|
dumphdr_machinit(dp)
|
|
struct dumphdr *dp;
|
|
{
|
|
u_int lomem;
|
|
u_int lophys;
|
|
u_int i;
|
|
struct memlist *pmem;
|
|
|
|
/*
|
|
* We mark all the pages below the first page (other than page 0)
|
|
* covered by a page structure.
|
|
* This gets us the message buffer and the kernel (text, data, bss),
|
|
* including the interrupt stack and proc[0]'s u area.
|
|
* (We also get page zero, which we may not need, but one page
|
|
* extra is no big deal.)
|
|
*/
|
|
#ifndef NOPROM
|
|
pmem = availmemory;
|
|
lophys = mmu_btop(pmem->address);
|
|
#else
|
|
/* for SAS, always start from pfn 0, all in one seg. */
|
|
lophys = 0;
|
|
#endif
|
|
lomem = memsegs->pages_base;
|
|
for (i = lophys; i < lomem; i++)
|
|
dump_addpage(i);
|
|
}
|
|
|
|
extern struct dev_reg obp_mailbox;
|
|
|
|
/*
|
|
* Machine-dependent portion of dump-checking;
|
|
* verify that a physical address is valid.
|
|
*/
|
|
int
|
|
dump_checksetbit_machdep(addr)
|
|
u_int addr;
|
|
{
|
|
struct memlist *pmem;
|
|
|
|
#ifndef NOPROM
|
|
for (pmem = availmemory; pmem; pmem = pmem->next) {
|
|
if (pmem->address <= addr &&
|
|
addr < (pmem->address + pmem->size))
|
|
return (1);
|
|
}
|
|
if ((addr & MMU_PAGEMASK) ==
|
|
((u_int)obp_mailbox.reg_addr & MMU_PAGEMASK))
|
|
return (1);
|
|
|
|
return (0);
|
|
#else
|
|
/* all SAS memory are contiguous, from 0 to availmem */
|
|
return ((addr >= 0) && (addr < availmem));
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Machine-dependent portion of dump-checking;
|
|
* mark all pages for dumping.
|
|
*/
|
|
dump_allpages_machdep()
|
|
{
|
|
u_int i, j;
|
|
struct memlist *pmem;
|
|
|
|
#ifndef NOPROM
|
|
for (pmem = availmemory; pmem; pmem = pmem->next) {
|
|
i = mmu_btop(pmem->address);
|
|
j = i + mmu_btop(pmem->size);
|
|
for (; i < j; i++)
|
|
dump_addpage(i);
|
|
}
|
|
#else
|
|
/* all SAS memory are contiguous, from 0 to availmem */
|
|
for (i = 0; i < mmu_btop(availmem); i++)
|
|
dump_addpage(i);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Dump a page frame.
|
|
*/
|
|
int
|
|
dump_page(vp, pg, bn)
|
|
struct vnode *vp;
|
|
int pg;
|
|
int bn;
|
|
{
|
|
register caddr_t addr;
|
|
register int err = 0;
|
|
struct pte pte;
|
|
#ifdef IOMMU
|
|
union iommu_pte *psbuspte, *pvmepte;
|
|
|
|
union iommu_pte* iom_ptefind();
|
|
extern void hat_cachesync();
|
|
#endif IOMMU
|
|
|
|
addr = &DVMA[mmu_ptob(dvmasize) - MMU_PAGESIZE];
|
|
|
|
*(u_int *)&pte = MMU_STD_INVALIDPTE;
|
|
pte.EntryType = MMU_ET_PTE;
|
|
pte.AccessPermissions = MMU_STD_SRWX;
|
|
pte.PhysicalPageNumber = pg;
|
|
|
|
/* this loads up host SRMMU */
|
|
mmu_setpte(addr, pte);
|
|
|
|
#ifdef IOMMU
|
|
psbuspte = iom_ptefind(dvmasize - 1, sbusmap);
|
|
if (cpu == CPU_SUN4M_690)
|
|
/* duplicate this mapping on IOMMU on both Sbus & VME */
|
|
pvmepte = iom_ptefind(dvmasize - 1, vme24map);
|
|
|
|
psbuspte->iopte_uint = *(u_int*) (&pte);
|
|
if (cpu == CPU_SUN4M_690) {
|
|
pvmepte->iopte_uint = *(u_int*) (&pte);
|
|
|
|
#ifdef IOC
|
|
ioc_setup(dvmasize - 1, IOC_LINE_NOIOC | IOC_LINE_WRITE);
|
|
#endif IOC
|
|
}
|
|
|
|
/*
|
|
* psbuspte = pvmepte, so only need to check for cache
|
|
* bit in one.
|
|
*/
|
|
if (cache && psbuspte->iopte.cache && vac) {
|
|
hat_cachesync((u_int) MAKE_PFNUM(&pte));
|
|
psbuspte->iopte.cache = 0;
|
|
if (cpu == CPU_SUN4M_690)
|
|
pvmepte->iopte.cache = 0;
|
|
}
|
|
|
|
/*
|
|
* now flush out the old TLBs in IOMMU.
|
|
*/
|
|
iommu_addr_flush((iopte_to_dvma(psbuspte)) & IOMMU_FLUSH_MSK);
|
|
if (cpu == CPU_SUN4M_690)
|
|
iommu_addr_flush((iopte_to_dvma(pvmepte)) & IOMMU_FLUSH_MSK);
|
|
|
|
#endif IOMMU
|
|
|
|
err = VOP_DUMP(vp, addr, bn, ctod(1));
|
|
|
|
return (err);
|
|
}
|
|
|
|
/*
|
|
* XXX This is grotty, but for historical reasons, xxdump() routines
|
|
* XXX (called by spec_dump) expect to be called with an object already
|
|
* XXX in DVMA space, and break if it isn't. This doesn't matter for
|
|
* XXX nfs_dump, but to be general we do it for everyone.
|
|
*
|
|
* Dump an arbitrary kernel-space object.
|
|
* We do this by mapping this object into DVMA space a page-worth's
|
|
* of bytes at a time, since this object may not be on a page boundary
|
|
* and may span multiple pages. We must be careful because the object
|
|
* (or trailing portion thereof) may not span a page boundary and the
|
|
* next virtual address may map to i/o space, which could cause
|
|
* heartache.
|
|
* We assume that dvmasize is at least two pages.
|
|
* And that MMU_PAGESIZE == IOMMU_PAGE_SIZE.
|
|
*/
|
|
int
|
|
dump_kaddr(vp, kaddr, bn, count)
|
|
struct vnode *vp;
|
|
caddr_t kaddr;
|
|
int bn;
|
|
int count;
|
|
{
|
|
register caddr_t addr;
|
|
register int err = 0;
|
|
struct pte ptea, pteb, tpte;
|
|
register int offset;
|
|
#ifdef IOMMU
|
|
union iommu_pte *psbuspte, *pvmepte;
|
|
|
|
union iommu_pte* iom_ptefind();
|
|
extern void hat_cachesync();
|
|
#endif IOMMU
|
|
|
|
offset = (u_int)kaddr & MMU_PAGEOFFSET;
|
|
addr = &DVMA[mmu_ptob(dvmasize) - 2 * MMU_PAGESIZE];
|
|
|
|
*(u_int *)&ptea = MMU_STD_INVALIDPTE;
|
|
*(u_int *)&pteb = MMU_STD_INVALIDPTE;
|
|
pteb.EntryType = MMU_ET_PTE;
|
|
pteb.AccessPermissions = MMU_STD_SRWX;
|
|
|
|
mmu_getpte(kaddr, &tpte);
|
|
|
|
#ifdef IOMMU
|
|
psbuspte = iom_ptefind(dvmasize - 2, sbusmap);
|
|
if (cpu == CPU_SUN4M_690)
|
|
pvmepte = iom_ptefind(dvmasize - 2, vme24map);
|
|
#endif IOMMU
|
|
|
|
pteb.PhysicalPageNumber = tpte.PhysicalPageNumber;
|
|
|
|
while (count > 0 && !err) {
|
|
ptea = pteb;
|
|
/* this loads up host MMU */
|
|
mmu_setpte(addr, ptea);
|
|
#ifdef IOMMU
|
|
/*
|
|
* now deal with IOMMU. but since the dump device
|
|
* could be either on Sbus or VME bus, we just setup
|
|
* DVMA mappings on both buses.
|
|
*/
|
|
psbuspte->iopte_uint = *(u_int*) (&tpte);
|
|
if (cpu == CPU_SUN4M_690) {
|
|
pvmepte->iopte_uint = *(u_int*) (&tpte);
|
|
|
|
#ifdef IOC
|
|
/*
|
|
* don't use IOC, so that we don't have to worry
|
|
* about invalidating the IOC before each I/O.
|
|
*/
|
|
ioc_setup(dvmasize - 2,
|
|
IOC_LINE_NOIOC | IOC_LINE_WRITE);
|
|
#endif IOC
|
|
}
|
|
|
|
/*
|
|
* psbuspte = pvmepte, so only need to check for cache
|
|
* bit in one.
|
|
*/
|
|
if (cache && psbuspte->iopte.cache && vac) {
|
|
hat_cachesync((u_int) MAKE_PFNUM(&tpte));
|
|
psbuspte->iopte.cache = 0;
|
|
if (cpu == CPU_SUN4M_690)
|
|
pvmepte->iopte.cache = 0;
|
|
}
|
|
|
|
/*
|
|
* now flush out the old TLBs in IOMMU.
|
|
*/
|
|
iommu_addr_flush((iopte_to_dvma(psbuspte)) & IOMMU_FLUSH_MSK);
|
|
if (cpu == CPU_SUN4M_690)
|
|
iommu_addr_flush((iopte_to_dvma(pvmepte)) &
|
|
IOMMU_FLUSH_MSK);
|
|
|
|
#endif IOMMU
|
|
mmu_getpte(kaddr + MMU_PAGESIZE, &tpte);
|
|
pteb.PhysicalPageNumber = (tpte.EntryType &&
|
|
bustype((int) tpte.PhysicalPageNumber) == BT_OBMEM) ?
|
|
tpte.PhysicalPageNumber : 0;
|
|
mmu_setpte(addr + MMU_PAGESIZE, pteb);
|
|
|
|
#ifdef IOMMU
|
|
psbuspte[1].iopte_uint = *(u_int*) (&tpte);
|
|
if (cpu == CPU_SUN4M_690) {
|
|
pvmepte[1].iopte_uint = *(u_int*) (&tpte);
|
|
|
|
#ifdef IOC
|
|
ioc_setup(dvmasize - 1,
|
|
IOC_LINE_NOIOC | IOC_LINE_WRITE);
|
|
#endif IOC
|
|
}
|
|
|
|
if (cache && psbuspte[1].iopte.cache && vac) {
|
|
hat_cachesync((u_int) MAKE_PFNUM(&tpte));
|
|
psbuspte[1].iopte.cache = 0;
|
|
if (cpu == CPU_SUN4M_690)
|
|
pvmepte[1].iopte.cache = 0;
|
|
}
|
|
|
|
/*
|
|
* now flush out the old TLBs in IOMMU.
|
|
*/
|
|
iommu_addr_flush((iopte_to_dvma(&psbuspte[1])) &
|
|
IOMMU_FLUSH_MSK);
|
|
if (cpu == CPU_SUN4M_690)
|
|
iommu_addr_flush((iopte_to_dvma(&pvmepte[1])) &
|
|
IOMMU_FLUSH_MSK);
|
|
|
|
#endif IOMMU
|
|
|
|
err = VOP_DUMP(vp, addr + offset, bn, ctod(1));
|
|
bn += ctod(1);
|
|
count -= ctod(1);
|
|
|
|
kaddr += MMU_PAGESIZE;
|
|
}
|
|
|
|
return (err);
|
|
}
|
|
|
|
/*
|
|
* Halt the machine and return to the monitor
|
|
*/
|
|
halt(s)
|
|
char *s;
|
|
{
|
|
#ifdef NOPROM
|
|
if (s)
|
|
printf("(%s) ", s);
|
|
printf("Halted\n\n");
|
|
asm("t 255");
|
|
#else
|
|
trigger_logan();
|
|
#ifdef MULTIPROCESSOR
|
|
xc_attention(); /* simplify the world */
|
|
#endif MULTIPROCESSOR
|
|
(void) spl7();
|
|
if (s)
|
|
prom_printf("(%s) ", s);
|
|
prom_printf("Halted\n\n");
|
|
prom_exit_to_mon();
|
|
#endif NOPROM
|
|
}
|
|
|
|
/*
|
|
* Common routine for bp_map and buscheck which will read in the
|
|
* pte's from the hardware mapping registers into the pte array given.
|
|
*/
|
|
static
|
|
read_hwmap(bp, npf, pte)
|
|
struct buf *bp;
|
|
int npf;
|
|
register struct pte *pte;
|
|
{
|
|
register addr_t addr;
|
|
struct as *as;
|
|
union ptpe *hpte;
|
|
u_int tpte;
|
|
|
|
if (bp->b_proc == (struct proc *)NULL ||
|
|
(bp->b_flags & B_REMAPPED) != 0 ||
|
|
(as = bp->b_proc->p_as) == (struct as *)NULL)
|
|
as = &kas;
|
|
addr = bp->b_un.b_addr;
|
|
hpte = NULL;
|
|
while (npf-- > 0) {
|
|
if ((hpte == NULL) ||
|
|
(((u_int)addr & (L3PTSIZE - 1)) < MMU_PAGESIZE))
|
|
hpte = hat_ptefind(as, addr);
|
|
/*
|
|
* The hardware ptes should be in place. Panic if we can't
|
|
* find them.
|
|
*/
|
|
if (hpte == NULL)
|
|
panic("read_hwmap no pte");
|
|
/*
|
|
* Read current ptes from the tables into pte array given.
|
|
* It should be valid, we panic if it's not.
|
|
*/
|
|
/* Viking bug 1101875. Don't do in-line non-cached loads.
|
|
* call get_mmu_entry().
|
|
*/
|
|
tpte = get_mmu_entry(&hpte->pte);
|
|
*pte = *(struct pte *) &tpte;
|
|
if (!pte_valid(pte))
|
|
panic("read_hwmap invalid pte");
|
|
/*
|
|
* We make the translation writable, even if the current
|
|
* mapping is read only. This is necessary because the
|
|
* new pte is blindly used in other places where it needs
|
|
* to be writable.
|
|
*/
|
|
pte->AccessPermissions = MMU_STD_SRWX; /* XX generous?? */
|
|
pte++;
|
|
addr += MMU_PAGESIZE;
|
|
hpte++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This number is derived from maxphys && klustsize
|
|
* are set above in startup() to limit the maximum
|
|
* transfer size to 124k. We add a pad here to account
|
|
* for non-page aligned transfers.
|
|
*/
|
|
#define MAXMBPHYS ((124 * 1024) + PAGESIZE)
|
|
|
|
/*
|
|
* Map the data referred to by the buffer bp into the kernel
|
|
* at kernel virtual address kaddr. Used to map in data for
|
|
* DVMA, among other things.
|
|
*/
|
|
|
|
/*
|
|
* PTECHUNKSIZE is just a convenient number of pages to deal with at a time.
|
|
* No way does it reflect anything but that.
|
|
*/
|
|
|
|
#define PTECHUNKSIZE 16
|
|
|
|
bp_map(bp, kaddr)
|
|
register struct buf *bp;
|
|
caddr_t kaddr;
|
|
{
|
|
auto struct buf bpcopy;
|
|
auto struct pte ptecache[PTECHUNKSIZE];
|
|
register struct pte *pte = &ptecache[0];
|
|
register struct pte *spte = (struct pte *) NULL;
|
|
register struct page *pp = (struct page *) NULL;
|
|
int npf, flags, cidx;
|
|
|
|
/* if kaddr >= DVMA, that's bp_iom_map()'s job */
|
|
if (kaddr >= DVMA)
|
|
panic("bp_map: bad kaddr\n");
|
|
|
|
/*
|
|
* Select where to find the pte values we need.
|
|
* They can either be gotten from a list of page
|
|
* structures hung off the bp, or are already
|
|
* available (in Sysmap), or can must be read from
|
|
* the hardware.
|
|
*/
|
|
|
|
if (bp->b_flags & B_PAGEIO) {
|
|
/*
|
|
* The way to get the pte's is to traverse
|
|
* the page structures and convert them to ptes.
|
|
*
|
|
* The original code commented against 'having
|
|
* to protect against interrupts messing up
|
|
* the array'. I don't think that that applies.
|
|
*/
|
|
pp = bp->b_pages;
|
|
} else if ((bp->b_flags & B_PHYS) == 0) {
|
|
/*
|
|
* If the address is between HEAPBASE and HEAPLIMIT
|
|
* the ptes are in Heapptes.
|
|
* If the address is between SYSBASE and Syslimit
|
|
* the ptes are in Sysmap.
|
|
* else we have to read the hardware for the page tables.
|
|
*/
|
|
u_int vaddr = (u_int) bp->b_un.b_addr;
|
|
if (vaddr >= HEAPBASE && vaddr < HEAPLIMIT)
|
|
spte = &Heapptes[btop(vaddr - HEAPBASE)];
|
|
else if ((vaddr >= SYSBASE) && (vaddr < (u_int) Syslimit)){
|
|
spte = &Sysmap[btop(vaddr - SYSBASE)];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the pte's aren't in Sysmap, or can't be gotten from page
|
|
* structures, then we have to read them from the hardware.
|
|
*/
|
|
|
|
if ((spte == (struct pte *) NULL) && (pp == (struct page *) NULL)) {
|
|
bpcopy = *bp; /* structure copy */
|
|
cidx = PTECHUNKSIZE;
|
|
}
|
|
|
|
/*
|
|
* We want to use mapin to set up the mappings now since some
|
|
* users of kernelmap aren't nice enough to unmap things
|
|
* when they are done and mapin handles this as a special case.
|
|
* If kaddr is in the kernelmap space, we use kseg so the
|
|
* software ptes will get updated. Otherwise we use kdvmaseg.
|
|
* We should probably check to make sure it is really in
|
|
* one of those two segments, but it's not worth the effort.
|
|
*/
|
|
flags = PTELD_NOSYNC;
|
|
|
|
/*
|
|
* Loop through the number of page frames.
|
|
* Don't use a predecrement because we use
|
|
* npf in the loop.
|
|
*/
|
|
|
|
npf = btoc(bp->b_bcount + ((int)bp->b_un.b_addr & PAGEOFFSET));
|
|
|
|
while (npf > 0) {
|
|
/*
|
|
* First, fetch the pte we're interested in.
|
|
*/
|
|
if (spte) {
|
|
pte = spte++;
|
|
} else if (pp) {
|
|
/*
|
|
* FIXME: probably should get rid of last arg.
|
|
* to hat_mempte() in vm_hat.c so that it will
|
|
* be the same with other machines.
|
|
*
|
|
* We stuff in KERNELBASE for now since we know
|
|
* PAGEIO only goes to KERNEL space.
|
|
*/
|
|
hat_mempte(pp, PROT_WRITE | PROT_READ, pte,
|
|
(addr_t) KERNELBASE);
|
|
pp = pp->p_next;
|
|
} else {
|
|
if (cidx == PTECHUNKSIZE) {
|
|
int np = MIN(npf, PTECHUNKSIZE);
|
|
read_hwmap(&bpcopy, np, ptecache);
|
|
bpcopy.b_un.b_addr += ctob(np);
|
|
cidx = 0;
|
|
}
|
|
pte = &ptecache[cidx++];
|
|
}
|
|
|
|
/*
|
|
* Now map it in
|
|
*/
|
|
segkmem_mapin(&kseg, kaddr, MMU_PAGESIZE,
|
|
PROT_READ | PROT_WRITE, MAKE_PFNUM(pte), flags);
|
|
/*
|
|
* adjust values of interest
|
|
*/
|
|
kaddr += MMU_PAGESIZE;
|
|
npf--;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Buscheck is called by mb_mapalloc and physio to check to see it the
|
|
* requested setup is a valid busmem type (i.e, vme16, vme24, vme32).
|
|
* Returns -1 if illegal mappings, returns 0 if all OBMEM and returns
|
|
* the starting page frame number for a legal "bus request".
|
|
*
|
|
* We insist that non-OBMEM mappings must be contiguous and of the
|
|
* same type (as determined from the starting page).
|
|
*/
|
|
int
|
|
buscheck(bp)
|
|
struct buf *bp;
|
|
{
|
|
auto struct buf bpcopy;
|
|
auto struct pte ptecache[PTECHUNKSIZE];
|
|
register struct pte *pte, *spte;
|
|
register int npf, cidx, res;
|
|
u_int pt, pf;
|
|
|
|
if (bp->b_flags & B_PAGEIO)
|
|
return (0); /* all OBMEM */
|
|
|
|
spte = (struct pte *) NULL;
|
|
|
|
/*
|
|
* If it's not a B_PHYS mapping, we assume that it's somewhere
|
|
* in the kernel's already mapped in address space. Some of these
|
|
* places already have a handy list of ptes available (Sysmap),
|
|
* else we have to read the hardware.
|
|
*
|
|
* For anything else, we have to read the hardware.
|
|
*/
|
|
|
|
if ((bp->b_flags & B_PHYS) == 0) {
|
|
u_int vaddr = (u_int) bp->b_un.b_addr;
|
|
/*
|
|
* If the address is between HEAPBASE and HEAPLIMIT
|
|
* the ptes are in Heapptes.
|
|
* If the address is between SYSBASE and Syslimit
|
|
* the ptes are in Sysmap.
|
|
* else we have to read the hardware for the page tables.
|
|
*/
|
|
if (vaddr >= HEAPBASE && vaddr < HEAPLIMIT)
|
|
spte = &Heapptes[btop(vaddr - HEAPBASE)];
|
|
else if ((vaddr >= SYSBASE) && (vaddr < (u_int) Syslimit)){
|
|
spte = &Sysmap[btop(vaddr - SYSBASE)];
|
|
}
|
|
}
|
|
|
|
if (spte == (struct pte *) NULL) {
|
|
bpcopy = *bp; /* structure copy */
|
|
cidx = PTECHUNKSIZE;
|
|
}
|
|
|
|
/*
|
|
* Set the result and page type to impossible values
|
|
*/
|
|
|
|
res = -1;
|
|
pf = pt = (u_int) -1;
|
|
|
|
/*
|
|
* ...and calculate the page frame count (allowing for rounding)
|
|
*/
|
|
|
|
npf = btoc(bp->b_bcount + ((int)bp->b_un.b_addr & PAGEOFFSET));
|
|
|
|
while (npf > 0) {
|
|
if (spte) {
|
|
pte = spte++;
|
|
} else {
|
|
if (cidx == PTECHUNKSIZE) {
|
|
int np = MIN(npf, PTECHUNKSIZE);
|
|
read_hwmap(&bpcopy, np, ptecache);
|
|
bpcopy.b_un.b_addr += ctob(np);
|
|
cidx = 0;
|
|
}
|
|
pte = &ptecache[cidx++];
|
|
}
|
|
|
|
if (pt != -1) {
|
|
/*
|
|
* Invalid or wrong page type (i.e., page type
|
|
* changed from first page)- error.
|
|
* If this isn't OBMEM, and the page frame numbers
|
|
* aren't contiguous, error also.
|
|
*/
|
|
if (!pte_valid(pte) ||
|
|
pt != bustype((int) pte->PhysicalPageNumber)) {
|
|
return (-1);
|
|
} else if (pt != BT_OBMEM &&
|
|
pte->PhysicalPageNumber != pf) {
|
|
return (-1);
|
|
}
|
|
} else {
|
|
if (!pte_valid(pte)) {
|
|
return (-1);
|
|
}
|
|
pf = pte->PhysicalPageNumber;
|
|
pt = bustype((int) pte->PhysicalPageNumber);
|
|
if (pt == BT_OBMEM) {
|
|
/*
|
|
* all Onboard memory memory so far
|
|
*/
|
|
res = 0;
|
|
} else {
|
|
/*
|
|
* Else save starting page frame number
|
|
*/
|
|
res = pf;
|
|
}
|
|
}
|
|
pf++;
|
|
npf--;
|
|
}
|
|
return (res);
|
|
}
|
|
|
|
/*
|
|
* Allocate 'size' units from the given map so that
|
|
* the vac alignment constraints for bp are maintained.
|
|
*
|
|
* Return 'addr' if successful, 0 if not.
|
|
*/
|
|
|
|
bp_alloc(map, bp, size)
|
|
struct map *map;
|
|
struct buf *bp;
|
|
int size;
|
|
{
|
|
register struct mapent *mp;
|
|
register int addr, mask, s;
|
|
int align = -1;
|
|
struct page *pp = NULL;
|
|
|
|
#ifdef IOC
|
|
int new_align;
|
|
int start_padding = 0;
|
|
#endif IOC
|
|
|
|
#ifdef VAC
|
|
if (vac) {
|
|
if ((bp->b_flags & B_PAGEIO) != 0) {
|
|
/*
|
|
* Peek at the first page's alignment.
|
|
* We could work harder and check the alignment
|
|
* of all the pages. If a conflict is found
|
|
* and the page is not kept (more than once
|
|
* if intrans), then try to do hat_pageunload()'s
|
|
* to allow the IO to be cached. However,
|
|
* this is very unlikely and not worth the
|
|
* extra work (for now at least).
|
|
*/
|
|
|
|
pp = bp->b_pages;
|
|
|
|
if (pp->p_mapping != NULL) {
|
|
align = mmu_btop((int)
|
|
sptetovaddr((struct spte *)
|
|
pp->p_mapping) &
|
|
(shm_alignment - 1));
|
|
}
|
|
} else if (bp->b_un.b_addr != NULL) {
|
|
align = mmu_btop((int)bp->b_un.b_addr &
|
|
(shm_alignment - 1));
|
|
}
|
|
}
|
|
#endif VAC
|
|
|
|
#ifdef IOC
|
|
/*
|
|
* due to 8k mapping to each IOC line, we need to align to 8K
|
|
* boundary.
|
|
*/
|
|
if (ioc) {
|
|
/*
|
|
* see if it can be IOC'ed.
|
|
*/
|
|
|
|
if (ioc_able(bp, map) != IOC_LINE_INVALID) {
|
|
if (align == -1) { /* vac must be off */
|
|
if ((bp->b_flags & B_PAGEIO) != 0) {
|
|
|
|
pp = bp->b_pages;
|
|
if (pp->p_mapping != NULL) {
|
|
align = mmu_btop((int)
|
|
sptetovaddr((struct spte *)
|
|
pp-> p_mapping) &
|
|
(IOC_LINE_MAP - 1));
|
|
} else {
|
|
align = 0;
|
|
}
|
|
} else if (bp->b_un.b_addr != NULL) {
|
|
align = mmu_btop((int)bp->b_un.b_addr
|
|
& (IOC_LINE_MAP - 1));
|
|
}
|
|
}
|
|
|
|
/* see if we need a padding at the beginning */
|
|
if ((new_align = ioc_adj_start(align)) != align) {
|
|
start_padding = align - new_align;
|
|
size += start_padding;
|
|
align = new_align;
|
|
}
|
|
|
|
/* see if we need a padding at the end */
|
|
if ((new_align = ioc_adj_end(align+size-1))
|
|
!= (align+size-1)) {
|
|
size += (new_align - (align+size-1));
|
|
}
|
|
}
|
|
}
|
|
#endif IOC
|
|
|
|
if (align == -1) {
|
|
s = splhigh();
|
|
align = (int)rmalloc(map, (long)size);
|
|
(void) splx(s);
|
|
return (align);
|
|
}
|
|
|
|
/*
|
|
* Look for a map segment containing a request that works.
|
|
* If none found, return failure.
|
|
* Since VAC has a much stronger alignment requirement,
|
|
* we'll use shm_alignment even ioc is on too.
|
|
*/
|
|
|
|
if (vac)
|
|
mask = mmu_btop(shm_alignment) - 1;
|
|
#ifdef IOC
|
|
else /* vac is off */
|
|
mask = IOC_PAGES_PER_LINE - 1;
|
|
#endif
|
|
|
|
for (mp = mapstart(map); mp->m_size; mp++) {
|
|
if (mp->m_size < size)
|
|
continue;
|
|
|
|
/*
|
|
* Find first addr >= mp->m_addr that
|
|
* fits the alignment constraints.
|
|
*/
|
|
addr = (mp->m_addr & ~mask) + align;
|
|
if (addr < mp->m_addr)
|
|
addr += mask + 1;
|
|
|
|
/*
|
|
* See if it fit within the map.
|
|
*/
|
|
if (addr + size <= mp->m_addr + mp->m_size)
|
|
break;
|
|
}
|
|
|
|
if (mp->m_size == 0)
|
|
return (0);
|
|
|
|
s = splhigh();
|
|
align = rmget(map, (long)size, (u_long)addr);
|
|
|
|
#ifdef IOC
|
|
if (align && start_padding)
|
|
align += start_padding;
|
|
#endif IOC
|
|
|
|
(void) splx(s);
|
|
return (align);
|
|
|
|
}
|
|
|
|
#ifdef IOMMU
|
|
int dvma_incoherent = 0;
|
|
/*
|
|
* NOTE: this routine assumes that IOMMU and SRMMU has the same page sizes.
|
|
*/
|
|
bp_iom_map(bp, iopfn, iom_flags, io_map)
|
|
register struct buf *bp;
|
|
int iopfn;
|
|
int iom_flags;
|
|
struct map *io_map;
|
|
{
|
|
auto struct buf bpcopy;
|
|
auto struct pte ptecache[PTECHUNKSIZE];
|
|
register struct pte *pte = &ptecache[0];
|
|
register struct pte *spte = (struct pte *) NULL;
|
|
register struct page *pp = (struct page *) NULL;
|
|
int npf, cidx;
|
|
int srmmu_flags, iosize;
|
|
union iommu_pte *p_iom_pte, *p_iom_start;
|
|
caddr_t kaddr;
|
|
/* struct modinfo *modinfop; */ /* apparently not used */
|
|
#ifdef VAC
|
|
extern void hat_cachesync();
|
|
#endif
|
|
|
|
#ifdef IOC
|
|
int ioc_pfn, ioc_set, aligned_pfn, do_ioc;
|
|
#endif
|
|
|
|
/*
|
|
* Select where to find the pte values we need.
|
|
* They can either be gotten from a list of page
|
|
* structures hung off the bp, or are already
|
|
* available (in Sysmap), or can must be read from
|
|
* the hardware.
|
|
*/
|
|
|
|
if (bp->b_flags & B_PAGEIO) {
|
|
/*
|
|
* The way to get the pte's is to traverse
|
|
* the page structures and convert them to ptes.
|
|
*
|
|
* The original code commented against 'having
|
|
* to protect against interrupts messing up
|
|
* the array'. I don't think that that applies.
|
|
*/
|
|
pp = bp->b_pages;
|
|
} else if ((bp->b_flags & B_PHYS) == 0) {
|
|
/*
|
|
* If the address is between HEAPBASE and HEAPLIMIT
|
|
* the ptes are in Heapptes.
|
|
* If the address is between SYSBASE and Syslimit
|
|
* the ptes are in Sysmap.
|
|
* else we have to read the hardware for the page tables.
|
|
*/
|
|
u_int vaddr = (u_int) bp->b_un.b_addr;
|
|
if (vaddr >= HEAPBASE && vaddr < HEAPLIMIT)
|
|
spte = &Heapptes[btop(vaddr - HEAPBASE)];
|
|
else if ((vaddr >= SYSBASE) && (vaddr < (u_int) Syslimit)){
|
|
spte = &Sysmap[btop(vaddr - SYSBASE)];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the pte's aren't in Sysmap, or can't be gotten from page
|
|
* structures, then we have to read them from the hardware.
|
|
*/
|
|
if ((spte == (struct pte *) NULL) && (pp == (struct page *) NULL)) {
|
|
bpcopy = *bp; /* structure copy */
|
|
cidx = PTECHUNKSIZE;
|
|
}
|
|
|
|
#ifdef IOC
|
|
if (iom_flags & MDR_LINE_NOIOC)
|
|
do_ioc = IOC_LINE_NOIOC;
|
|
else
|
|
do_ioc = ioc_able(bp, io_map);
|
|
#endif IOC
|
|
|
|
/*
|
|
* We want to use mapin to set up the mappings now since some
|
|
* users of kernelmap aren't nice enough to unmap things
|
|
* when they are done and mapin handles this as a special case.
|
|
* If kaddr is in the kernelmap space, we use kseg so the
|
|
* software ptes will get updated. Otherwise we use kdvmaseg.
|
|
* We should probably check to make sure it is really in
|
|
* one of those two segments, but it's not worth the effort.
|
|
*/
|
|
if (iom_flags & DVMA_PEEK) {
|
|
|
|
if (io_map == sbusmap || io_map == bigsbusmap ||
|
|
io_map == altsbusmap || io_map == sunpcmap ) {
|
|
/* SBUS && PEEK */
|
|
panic("bp_iom_map: SBUS && DVMA_PEEK!!\n");
|
|
}
|
|
|
|
srmmu_flags = PTELD_NOSYNC | PTELD_INTREP;
|
|
#ifdef IOC
|
|
/*
|
|
* FIXME: not sure if we need to notify hosts hat layer
|
|
* about IOC or not.
|
|
*/
|
|
if (do_ioc == IOC_LINE_LDIOC)
|
|
srmmu_flags |= PTELD_IOCACHE;
|
|
#endif
|
|
}
|
|
kaddr = &DVMA[iommu_ptob(iopfn)];
|
|
|
|
if ((p_iom_pte = iom_ptefind(iopfn, io_map)) == NULL)
|
|
panic("bp_iom_map: bad iopfn");
|
|
|
|
#ifdef IOC
|
|
ioc_pfn = iopfn;
|
|
ioc_set = 1;
|
|
#endif
|
|
iosize = npf = btoc(bp->b_bcount + ((int)bp->b_un.b_addr & PAGEOFFSET));
|
|
|
|
while (npf > 0) {
|
|
/*
|
|
* First, fetch the pte we're interested in.
|
|
*/
|
|
if (spte) {
|
|
pte = spte++;
|
|
} else if (pp) {
|
|
/*
|
|
* FIXME: probably should get rid of last arg.
|
|
* to hat_mempte() in vm_hat.c so that it will
|
|
* be the same with other machines.
|
|
*
|
|
* We stuff in KERNELBASE for now since we know
|
|
* PAGEIO only goes to KERNEL space.
|
|
*/
|
|
hat_mempte(pp, PROT_WRITE | PROT_READ, pte,
|
|
(addr_t) KERNELBASE);
|
|
pp = pp->p_next;
|
|
} else {
|
|
if (cidx == PTECHUNKSIZE) {
|
|
int np = MIN(npf, PTECHUNKSIZE);
|
|
read_hwmap(&bpcopy, np, ptecache);
|
|
bpcopy.b_un.b_addr += ctob(np);
|
|
cidx = 0;
|
|
}
|
|
pte = &ptecache[cidx++];
|
|
}
|
|
|
|
/*
|
|
* load iommu pte. We did not use iom_pteload()
|
|
* because iopte and srmmu pte fits each other
|
|
* just fine. This is much faster too.
|
|
*/
|
|
p_iom_pte-> iopte_uint = *(u_int*) pte;
|
|
|
|
/*
|
|
* If the kernel is not using the cache, then don't
|
|
* bother playing cache coherency games.
|
|
*/
|
|
if (!cache)
|
|
p_iom_pte-> iopte.cache = 0;
|
|
/* Is this code really necessary on ROSS machine */
|
|
#ifdef VAC
|
|
/*
|
|
* If cache coherency does not work on this configuration,
|
|
* or if we are disabling it, then synchronize the cache
|
|
* with memory and don't play coherency games.
|
|
*
|
|
* For debugging, we can prevent turning off the
|
|
* coherent transactions, and we can individually
|
|
*/
|
|
if (vac &&
|
|
#ifndef NOPROM
|
|
((mod_info->ccoher == 0) || (dvma_incoherent) ||
|
|
(bp-> b_flags & B_PAGEIO && iosize > 1)) &&
|
|
#else NOPROM
|
|
(module_type == ROSS605) &&
|
|
#endif NOPROM
|
|
p_iom_pte->iopte.cache) {
|
|
hat_cachesync((u_int) MAKE_PFNUM(pte));
|
|
p_iom_pte-> iopte.cache = 0;
|
|
}
|
|
#endif VAC
|
|
|
|
#ifdef IOC
|
|
/* setup IOC, we cheat here by setting every other page */
|
|
if (do_ioc != IOC_LINE_INVALID) {
|
|
if (ioc_set & 1)
|
|
ioc_setup(ioc_pfn, do_ioc |
|
|
((p_iom_pte-> iopte.write)?
|
|
IOC_LINE_WRITE : 0));
|
|
ioc_set++;
|
|
}
|
|
#endif IOC
|
|
/*
|
|
* Note we also load SRMMU pte here, we could use
|
|
* bp_map(), which would be too slow since it has to
|
|
* read maps all over again, and spin through each one of them
|
|
* like what we just did here.
|
|
*/
|
|
if (iom_flags & DVMA_PEEK) {
|
|
segkmem_mapin(&kdvmaseg, kaddr, MMU_PAGESIZE,
|
|
PROT_READ | PROT_WRITE, MAKE_PFNUM(pte),
|
|
srmmu_flags);
|
|
}
|
|
kaddr += MMU_PAGESIZE;
|
|
npf--;
|
|
p_iom_pte++; /* we know ioptes on IOMMU are contiguous */
|
|
#ifdef IOC
|
|
ioc_pfn++;
|
|
#endif IOC
|
|
}
|
|
|
|
#ifdef IOC
|
|
/*
|
|
* now we handle IOMMU's mappings to "paddings" due to IOC/IOMMU's
|
|
* different page sizes.
|
|
*/
|
|
if (do_ioc != IOC_LINE_INVALID) {
|
|
if ((aligned_pfn = ioc_adj_start(iopfn)) != iopfn) {
|
|
p_iom_start = iom_ptefind(aligned_pfn, io_map);
|
|
p_iom_start-> iopte_uint = p_iom_start[1].iopte_uint;
|
|
}
|
|
|
|
/*
|
|
* NOTE: it should really read:
|
|
* ioc_adj_end(iopfn+iosize+1-1) != iopfn+iosize+1-1.
|
|
* +1: for red zone, -1: for pfn = start + lng -1.
|
|
*/
|
|
|
|
if ((aligned_pfn = ioc_adj_end(iopfn + iosize)) !=
|
|
(iopfn + iosize)) {
|
|
p_iom_pte->iopte_uint = (p_iom_pte - 1)->iopte_uint;
|
|
|
|
/*
|
|
* if DVMA starts with odd pages, and size is even
|
|
* pages, due to the alignment/padding, the last IOC
|
|
* line is NOT set up yet.
|
|
*/
|
|
if (!(iosize & 0x1))
|
|
ioc_setup(ioc_pfn,
|
|
do_ioc | ((p_iom_pte-> iopte.write)?
|
|
IOC_LINE_WRITE : 0));
|
|
p_iom_pte++;
|
|
}
|
|
|
|
}
|
|
#endif IOC
|
|
|
|
/* red zone on IOMMU */
|
|
p_iom_pte-> iopte_uint = 0;
|
|
|
|
}
|
|
#endif IOMMU
|
|
|
|
/*
|
|
* Called to convert bp for pageio/physio to a kernel addressable location.
|
|
* We allocate virtual space from the kernelmap and then use bp_map to do
|
|
* most of the real work.
|
|
*/
|
|
bp_mapin(bp)
|
|
register struct buf *bp;
|
|
{
|
|
int npf, o;
|
|
long a;
|
|
caddr_t kaddr;
|
|
int s;
|
|
|
|
if ((bp->b_flags & (B_PAGEIO | B_PHYS)) == 0 ||
|
|
(bp->b_flags & B_REMAPPED) != 0)
|
|
return; /* no pageio/physio or already mapped in */
|
|
|
|
if ((bp->b_flags & (B_PAGEIO | B_PHYS)) == (B_PAGEIO | B_PHYS))
|
|
panic("bp_mapin");
|
|
|
|
o = (int)bp->b_un.b_addr & PAGEOFFSET;
|
|
npf = btoc(bp->b_bcount + o);
|
|
|
|
/*
|
|
* Allocate kernel virtual space for remapping.
|
|
*/
|
|
if ((a = bp_alloc(kernelmap, bp, npf)) == 0) {
|
|
s= splhigh();
|
|
while ((a = bp_alloc(kernelmap, bp, npf)) == 0) {
|
|
cleanup();
|
|
mapwant(kernelmap)++;
|
|
(void) sleep((caddr_t)kernelmap, PSWP);
|
|
}
|
|
(void) splx(s);
|
|
}
|
|
kaddr = Sysbase + mmu_ptob(a);
|
|
|
|
/* map the bp into the virtual space we just allocated */
|
|
bp_map(bp, kaddr);
|
|
|
|
bp->b_flags |= B_REMAPPED;
|
|
bp->b_un.b_addr = kaddr + o;
|
|
}
|
|
|
|
/*
|
|
* bp_mapout will release all the resources associated with a bp_mapin call.
|
|
* We call hat_unload to release the work done by bp_map which will insure
|
|
* that the reference and modified bits from this mapping are not OR'ed in.
|
|
*/
|
|
bp_mapout(bp)
|
|
register struct buf *bp;
|
|
{
|
|
int npf;
|
|
u_long a;
|
|
addr_t addr;
|
|
int s;
|
|
|
|
if (bp->b_flags & B_REMAPPED) {
|
|
addr = (addr_t)((int)bp->b_un.b_addr & PAGEMASK);
|
|
bp->b_un.b_addr = (caddr_t)((int)bp->b_un.b_addr & PAGEOFFSET);
|
|
npf = mmu_btopr(bp->b_bcount + (int)bp->b_un.b_addr);
|
|
hat_unload(&kseg, addr, (u_int)mmu_ptob(npf));
|
|
a = mmu_btop(addr - Sysbase);
|
|
bzero((caddr_t)&Usrptmap[a], (u_int)(sizeof (Usrptmap[0])*npf));
|
|
s = splhigh();
|
|
rmfree(kernelmap, (long)npf, a);
|
|
(void) splx(s);
|
|
bp->b_flags &= ~B_REMAPPED;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Explicitly set these so they end up in the data segment.
|
|
* We clear the bss *after* initializing these variables in locore.s.
|
|
*/
|
|
char mon_clock_on = 0; /* disables profiling */
|
|
extern trapvec kclock14_vec; /* kernel clock14 vector code (see locore.s) */
|
|
extern trapvec mon_clock14_vec; /* monitor clock14 vector code (see locore.s) */
|
|
|
|
/*
|
|
* at startup, we have the kernel vector installed in
|
|
* the scb, but the hardware is like when the monitor
|
|
* clock is going, so we need to make it all consistant.
|
|
* when we are done, things are turned off, because we
|
|
* can't change the mappings on the scb (yet).
|
|
*/
|
|
|
|
init_mon_clock()
|
|
{
|
|
kclock14_vec = scb.interrupts[14 - 1];
|
|
#ifndef NOPROM
|
|
mon_clock_on = 0; /* enable profiling */
|
|
set_clk_mode((u_long) 0,
|
|
(u_long) IR_ENA_CLK14); /* disable level 14 clk intr */
|
|
#endif NOPROM
|
|
}
|
|
|
|
start_mon_clock()
|
|
{
|
|
#ifndef NOPROM
|
|
if (!mon_clock_on) {
|
|
mon_clock_on = 1; /* disable profiling */
|
|
write_scb_int(14, &mon_clock14_vec); /* install mon vector */
|
|
/* enable level 14 clk intr */
|
|
set_clk_mode((u_long) IR_ENA_CLK14, (u_long) 0);
|
|
}
|
|
#endif NOPROM
|
|
}
|
|
|
|
stop_mon_clock()
|
|
{
|
|
#ifndef NOPROM
|
|
if (mon_clock_on) {
|
|
mon_clock_on = 0; /* enable profiling */
|
|
/* disable level 14 clk intr */
|
|
set_clk_mode((u_long) 0, (u_long) IR_ENA_CLK14);
|
|
write_scb_int(14, &kclock14_vec); /* install kernel vector */
|
|
}
|
|
#endif NOPROM
|
|
}
|
|
|
|
/*
|
|
* Write the scb, which is the first page of the kernel.
|
|
#ifndef PC_vscb
|
|
* Normally it is write protected, we provide a function
|
|
* to fiddle with the mapping temporarily.
|
|
* 1) lock out interrupts
|
|
* 2) save the old pte value of the scb page
|
|
* 3) set the pte so it is writable
|
|
* 4) write the desired vector into the scb
|
|
* 5) restore the pte to its old value
|
|
* 6) restore interrupts to their previous state
|
|
#else PC_vscb
|
|
* Since we are using the scb mapping in the PerCPU area,
|
|
* it is writable, so no PTE mods are required.
|
|
* %%% do we need to spl8 or not? if so, do we need to
|
|
* worry about the other cpu taking this interrupt while
|
|
* we are modifying the vector?
|
|
#endif PC_vscb
|
|
*/
|
|
write_scb_int(level, ip)
|
|
register int level;
|
|
struct trapvec *ip;
|
|
{
|
|
/* disable ints while mucking with SCB entries. */
|
|
register int s = spl8();
|
|
#ifndef PC_vscb
|
|
register union ptpe *ptpe;
|
|
register int sv_perm;
|
|
register trapvec *sp;
|
|
|
|
sp = &scb.interrupts[level - 1];
|
|
|
|
/* save old mapping */
|
|
ptpe = hat_ptefind(&kas, (addr_t)sp);
|
|
sv_perm = ptpe->pte.AccessPermissions;
|
|
|
|
/* allow writes */
|
|
pte_setprot(ptpe, MMU_STD_SRWX);
|
|
|
|
/* write out new vector code */
|
|
*sp = *ip;
|
|
|
|
/*
|
|
* Yet another disgusting hack. We've modified instructions, so
|
|
* we really need to do an iflush to knock them out of the I-cache.
|
|
* However, on Ross, iflush gives us an illegal instruction trap.
|
|
* We can solve this by (1) not doing the flush on Ross, or (2)
|
|
* fixing trap.c to ignore illegal instruction traps from iflushes.
|
|
* (1) seems simpler and thus less dangerous, so...
|
|
*/
|
|
if (mod_info[0].mod_type == CPU_ROSS605 ||
|
|
mod_info[0].mod_type == CPU_ROSS625) {
|
|
vac_flush(sp, sizeof(struct trapvec));
|
|
} else {
|
|
for (i = 0; i < sizeof(struct trapvec); i += sizeof(int))
|
|
flush((int)sp + i);
|
|
}
|
|
|
|
/* restore old mapping */
|
|
pte_setprot(ptpe, sv_perm);
|
|
|
|
#else PC_vscb
|
|
int i;
|
|
register trapvec *sp = &vscb.interrupts[level-1];
|
|
struct scb *scbp;
|
|
trapvec *sp2;
|
|
|
|
scbp = (struct scb *)(gettbr() & 0xFFFFF000);
|
|
sp2 = &scbp->interrupts[level - 1];
|
|
|
|
*sp = *ip;
|
|
|
|
/* Flush hack - see above */
|
|
if (mod_info[0].mod_type == CPU_ROSS605) {
|
|
/*
|
|
* Combined I+D Virtual address cache
|
|
*/
|
|
vac_flush(sp, sizeof(struct trapvec));
|
|
} else if (mod_info[0].mod_type == CPU_ROSS625) {
|
|
/*
|
|
* iflush for internal icache.
|
|
* Vac flush for Combined I+D Virtual address cache
|
|
*/
|
|
vac_flush(sp, sizeof(struct trapvec));
|
|
for (i = 0; i < sizeof(struct trapvec); i += sizeof(int)) {
|
|
flush((int)sp + i);
|
|
flush((int)sp2 + i);
|
|
}
|
|
} else {
|
|
for (i = 0; i < sizeof(struct trapvec); i += sizeof(int)) {
|
|
flush((int)sp + i);
|
|
flush((int)sp2 + i);
|
|
}
|
|
}
|
|
|
|
#endif PC_vscb
|
|
|
|
(void) splx(s);
|
|
}
|
|
|
|
/*
|
|
* Handler for monitor vector cmd -
|
|
* For now we just implement the old "g0" and "g4"
|
|
* commands and a printf hack.
|
|
*
|
|
* NOTE: everyone is in the boot prom, so we can snatch
|
|
* the klock (as long as we restore its value when done).
|
|
*/
|
|
void
|
|
v_handler(str)
|
|
char *str;
|
|
{
|
|
char *sargv[8];
|
|
#ifdef MULTIPROCESSOR
|
|
extern int klock;
|
|
extern int xc_ready;
|
|
int klock_save;
|
|
int xc_ready_save;
|
|
#endif MULTIPROCESSOR
|
|
|
|
struct cmd_info {
|
|
char *cmd;
|
|
int func;
|
|
};
|
|
#define ENDADDR(a) &a[sizeof (a) / sizeof (a[0])]
|
|
static struct cmd_info vx_cmd[] = {
|
|
"sync", 0,
|
|
};
|
|
#define vx_cmd_end ENDADDR(vx_cmd)
|
|
|
|
register struct cmd_info *cp;
|
|
register int func;
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
klock_save = klock_steal();
|
|
xc_ready_save = xc_ready;
|
|
xc_ready = 0; /* avoid using xc mechanism */
|
|
#endif MULTIPROCESSOR
|
|
|
|
parse_str(str, sargv);
|
|
|
|
func = -1;
|
|
|
|
for (cp = (struct cmd_info *)vx_cmd;
|
|
cp < (struct cmd_info*)vx_cmd_end;
|
|
cp++) {
|
|
if (strcmp(sargv[0], cp->cmd) == 0) {
|
|
func = cp->func;
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (func) {
|
|
case 0: /* sync */
|
|
panic("zero");
|
|
/* NOTREACHED */
|
|
|
|
default:
|
|
prom_printf("Don't understand '%s'\n", str);
|
|
}
|
|
#ifdef MULTIPROCESSOR
|
|
xc_ready = xc_ready_save;
|
|
klock = klock_save;
|
|
#endif MULTIPROCESSOR
|
|
}
|
|
|
|
#define isspace(c) ((c) == ' ' || (c) == '\t' || (c) == '\n' || \
|
|
(c) == '\r' || (c) == '\f' || (c) == '\013')
|
|
|
|
parse_str(str, args)
|
|
register char *str;
|
|
register char *args[];
|
|
{
|
|
register int i;
|
|
|
|
while (*str && isspace(*str))
|
|
str++;
|
|
for (i = 0; (i < 8) && (*str); /* empty */) {
|
|
args[i++] = str;
|
|
while (*str && (!isspace(*str)))
|
|
str++;
|
|
if (*str)
|
|
*str++ = '\0';
|
|
while (*str && isspace(*str))
|
|
str++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* a macro used by async() to
|
|
* atomically set system_fatal and
|
|
* log info about the fault,
|
|
* to be printed later when we panic.
|
|
*/
|
|
#ifdef MULTIPROCESSOR
|
|
|
|
#define SYS_FATAL_FLT(TYPE) \
|
|
if (atom_tas(system_fatal)) { \
|
|
PerCPU[cpuid].sys_fatal_flt.aflt_type = TYPE; \
|
|
PerCPU[cpuid].sys_fatal_flt.aflt_subtype = subtype; \
|
|
PerCPU[cpuid].sys_fatal_flt.aflt_stat = afsr; \
|
|
PerCPU[cpuid].sys_fatal_flt.aflt_addr0 = afar0; \
|
|
PerCPU[cpuid].sys_fatal_flt.aflt_addr1 = afar1; \
|
|
PerCPU[cpuid].sys_fatal_flt.aflt_err0 = aerr0; \
|
|
PerCPU[cpuid].sys_fatal_flt.aflt_err1 = aerr1; \
|
|
}
|
|
#else MULTIPROCESSOR
|
|
|
|
#define SYS_FATAL_FLT(TYPE) \
|
|
if (atom_tas(system_fatal)) { \
|
|
sys_fatal_flt.aflt_type = TYPE; \
|
|
sys_fatal_flt.aflt_subtype = subtype; \
|
|
sys_fatal_flt.aflt_stat = afsr; \
|
|
sys_fatal_flt.aflt_addr0 = afar0; \
|
|
sys_fatal_flt.aflt_addr1 = afar1; \
|
|
sys_fatal_flt.aflt_err0 = aerr0; \
|
|
sys_fatal_flt.aflt_err1 = aerr1; \
|
|
}
|
|
#endif MULTIPROCESSOR
|
|
|
|
extern void mmu_log_module_err();
|
|
u_int report_ce_log = 0;
|
|
u_int log_ce_error = 0;
|
|
|
|
/*
|
|
* Asynchronous fault handler.
|
|
* This routine is called to handle a hard level 15 interrupt,
|
|
* which is broadcasted to all CPUs.
|
|
* All fatal failures are completely handled by this routine
|
|
* (by panicing). Since handling non-fatal failures would access
|
|
* data structures which are not consistent at the time of this
|
|
* interrupt, these non-fatal failures are handled later in a
|
|
* soft interrupt at a lower level.
|
|
*/
|
|
|
|
sun4m_l15_async_fault(sipr)
|
|
u_int sipr; /* System Interrupt Pending Register */
|
|
{
|
|
u_int afsr; /* an Asynchronous Fault Status Register */
|
|
u_int afar0; /* first Asynchronous Fault Address Reg. */
|
|
u_int afar1 = 0; /* second Asynchronous Fault Address Reg. */
|
|
u_int aerr0; /* MXCC error register <63:32> */
|
|
u_int aerr1; /* MXCC error register <31:0> */
|
|
u_int ftype; /* fault type */
|
|
|
|
extern void prom_reboot();
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
register in_lock;
|
|
|
|
in_lock = klock_knock();
|
|
#endif MULTIPROCESSOR
|
|
/*
|
|
* Handle the nofault case by setting pokefault so
|
|
* that the probe routine knows that something went
|
|
* wrong. It is the responsibility of the probe routine
|
|
* to reset this variable.
|
|
* Yes, it is OK for all CPUs to do this.
|
|
*/
|
|
if (nofault)
|
|
pokefault = 1;
|
|
/*
|
|
* Don't let any of those sneaky other processors clear the sipr,
|
|
* atleast until we get to here, grumble!
|
|
*/
|
|
#ifdef MULTIPROCESSOR
|
|
if (cpuid == 0) {
|
|
register int ps = procset;
|
|
register int i;
|
|
for (i= 1; i<nmod; i++)
|
|
if (ps & (1<<i))
|
|
atom_wfs(PerCPU[i].aflt_sync);
|
|
for (i=1; i<nmod; i++)
|
|
if (ps & (1<<i))
|
|
atom_clr(PerCPU[i].aflt_sync);
|
|
} else {
|
|
atom_set(PerCPU[cpuid].aflt_sync);
|
|
atom_wfc(PerCPU[cpuid].aflt_sync);
|
|
}
|
|
#endif MULTIPROCESSOR
|
|
|
|
/*
|
|
* If NONE of the interesting bits is set, then someone else
|
|
* took a watchdog and managed to clear his error. Dive back
|
|
* into the boot prom.
|
|
*/
|
|
if ((sipr & SIR_ASYNCFLT) == 0) {
|
|
#ifdef MULTIPROCESSOR
|
|
if (in_lock)
|
|
#endif MULTIPROCESSOR
|
|
printf("Level 15 Error: Watchdog Reset\n");
|
|
(void) prom_stopcpu(0);
|
|
}
|
|
|
|
/*
|
|
* Heisenberg's uncertainty principle as applied to
|
|
* mem I/F asynchronous faults: Since the vac flush
|
|
* code that maintains consistency across various CPUs
|
|
* must be fast, it does not flush write buffers whenever
|
|
* it changes contexts temporarly. A side effect is that
|
|
* it is not possible to tell which context is responsible
|
|
* for mem I/F faults. Thus, if we ever wanted to do
|
|
* something other than panicing for timeout or bus errors,
|
|
* (for user accesses), we would still be forced to panic
|
|
* when the m-bus address does not have a page struct
|
|
* associated with it.
|
|
*/
|
|
#ifdef MULTIPROCESSOR
|
|
if (in_lock) {
|
|
#endif MULTIPROCESSOR
|
|
/*
|
|
* Handle ECC asynchronous faults first, because there is
|
|
* a minute possibility that this handler could cause
|
|
* further ECC faults (when storing non-fatal fault info
|
|
* in memory) before the first ECC fault is handled.
|
|
*/
|
|
if (sipr & SIR_ECCERROR)
|
|
l15_ecc_async_flt();
|
|
if (sipr & SIR_M2SWRITE)
|
|
l15_mts_async_flt();
|
|
#ifdef VME
|
|
if (sipr & SIR_VMEERROR)
|
|
l15_vme_async_flt();
|
|
#endif VME
|
|
if (sipr & SIR_MODERROR)
|
|
atom_set(module_error);
|
|
#ifdef MULTIPROCESSOR
|
|
}
|
|
|
|
if (cpuid == 0) {
|
|
register int ps = procset;
|
|
register int i;
|
|
for (i = 1; i < nmod; i++)
|
|
if (ps & (1<<i))
|
|
atom_wfs(PerCPU[i].aflt_sync);
|
|
for (i = 1; i < nmod; i++)
|
|
if (ps & (1<<i))
|
|
atom_clr(PerCPU[i].aflt_sync);
|
|
} else {
|
|
atom_set(PerCPU[cpuid].aflt_sync);
|
|
atom_wfc(PerCPU[cpuid].aflt_sync);
|
|
}
|
|
#endif MULTIPROCESSOR
|
|
|
|
if (atom_setp(module_error))
|
|
l15_mod_async_flt();
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
if (cpuid == 0) {
|
|
register int ps = procset;
|
|
register int i;
|
|
for (i = 1; i < nmod; i++)
|
|
if (ps & (1<<i))
|
|
atom_wfs(PerCPU[i].aflt_sync);
|
|
atom_clr(module_error);
|
|
for (i = 1; i < nmod; i++)
|
|
if (ps & (1<<i))
|
|
atom_clr(PerCPU[i].aflt_sync);
|
|
} else {
|
|
atom_set(PerCPU[cpuid].aflt_sync);
|
|
atom_wfc(PerCPU[cpuid].aflt_sync);
|
|
}
|
|
#endif MULTIPROCESSOR
|
|
|
|
/*
|
|
* if faults are expected, no further processing
|
|
* is necessary.
|
|
*/
|
|
if (nofault)
|
|
return;
|
|
|
|
/*
|
|
* If system_fatal is set, then cpu with klock takes care of
|
|
* panicing. All other CPUs return to the boot prom
|
|
* for re-start recovery.
|
|
*/
|
|
#ifdef MULTIPROCESSOR
|
|
if ((atom_setp(system_fatal)) && (in_lock)) {
|
|
afsr = PerCPU[cpuid].sys_fatal_flt.aflt_stat;
|
|
afar0 = PerCPU[cpuid].sys_fatal_flt.aflt_addr0;
|
|
afar1 = PerCPU[cpuid].sys_fatal_flt.aflt_addr1;
|
|
aerr0 = PerCPU[cpuid].sys_fatal_flt.aflt_err0;
|
|
aerr1 = PerCPU[cpuid].sys_fatal_flt.aflt_err1;
|
|
ftype = PerCPU[cpuid].sys_fatal_flt.aflt_type;
|
|
|
|
#else MULTIPROCESSOR
|
|
if (atom_setp(system_fatal)) {
|
|
afsr = sys_fatal_flt.aflt_stat;
|
|
afar0 = sys_fatal_flt.aflt_addr0;
|
|
afar1 = sys_fatal_flt.aflt_addr1;
|
|
aerr0 = sys_fatal_flt.aflt_err0;
|
|
aerr1 = sys_fatal_flt.aflt_err1;
|
|
ftype = sys_fatal_flt.aflt_type;
|
|
|
|
#endif MULTIPROCESSOR
|
|
printf("fatal system fault: sipr=%x\n", sipr);
|
|
|
|
switch (ftype) {
|
|
case AFLT_MODULE:
|
|
printf("Async Fault from module: ");
|
|
printf("afsr=%x afar=%x\n", afsr, afar0);
|
|
mmu_log_module_err(afsr, afar0, aerr0, aerr1);
|
|
break;
|
|
|
|
case AFLT_MEM_IF:
|
|
log_ce_error = 1;
|
|
log_mem_err(afsr, afar0, afar1, (u_int) 0);
|
|
printf("Control Registers:\n");
|
|
printf("\tefsr = 0x%x, efar0 = 0x%x ", afsr, afar0);
|
|
printf("efar1 = 0x%x\n", afar1);
|
|
panic("memory error");
|
|
/* NOTREACHED */
|
|
|
|
case AFLT_M_TO_S:
|
|
printf("Async Fault from M-to-S: ");
|
|
printf("afsr=%x afar=%x\n", afsr, afar0);
|
|
log_mtos_err(afsr, afar0);
|
|
break;
|
|
|
|
#ifdef VME
|
|
case AFLT_S_TO_VME:
|
|
printf("Async Fault from S-to-VME: ");
|
|
printf("afsr=%x afar=%x\n", afsr, afar0);
|
|
log_stovme_err(afsr, afar0);
|
|
break;
|
|
#endif VME
|
|
|
|
default:
|
|
printf("Unknown Fault type %d; ", ftype);
|
|
printf("afsr=%x afar=%x,%x\n", afsr, afar0, afar1);
|
|
break;
|
|
}
|
|
panic("Fatal Asynchronous Fault\n");
|
|
/*
|
|
* never get here from panic
|
|
*/
|
|
atom_clr(system_fatal);
|
|
/* NOTREACHED */
|
|
#ifdef MULTIPROCESSOR
|
|
} else if ((atom_setp(system_fatal)) && (!(in_lock))) {
|
|
/*
|
|
* synchronization point: let the cpu w/kernel lock panic
|
|
*/
|
|
atom_wfc(system_fatal);
|
|
(void) prom_stopcpu(0);
|
|
#endif MULTIPROCESSOR
|
|
}
|
|
}
|
|
|
|
|
|
#if defined(SUN4M_35)
|
|
/*
|
|
* microSPARC asynchronous fault handler
|
|
*
|
|
* This routine is called to handle a hard level 15 interrupt.
|
|
* All fatal failures are completely handled by this routine
|
|
* (by panicing). Since handling non-fatal failures would access
|
|
* data structures which are not consistent at the time of this
|
|
* interrupt, these non-fatal failures are handled later in a
|
|
* soft interrupt at a lower level.
|
|
*
|
|
* For some reason the "memory fault" register addresses
|
|
* changed between msparc1 and msparc2 thus we must
|
|
* special case msparc1 machines.
|
|
*/
|
|
|
|
void
|
|
msparc_l15_async_fault()
|
|
{
|
|
u_int sipr; /* System Interrupt Pending Register */
|
|
u_int afsr; /* Asynchronous Fault Status Register */
|
|
u_int afar; /* Asynchronous Fault Address Reg. */
|
|
u_int mfsr; /* Memory Fault Status Reg. */
|
|
u_int mfar; /* Memory Fault Address Reg. */
|
|
extern int tsunami; /* msparc1 */
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
extern struct sbus_vme_mm *mm_find_paddr();
|
|
extern struct sbus_vme_mm *mm_base;
|
|
register in_lock;
|
|
|
|
in_lock = klock_knock();
|
|
#ifdef lint
|
|
in_lock = in_lock;
|
|
#endif lint
|
|
|
|
#endif MULTIPROCESSOR
|
|
|
|
/* read the System Interrupt Pending Register */
|
|
sipr = *(u_int *)SIPR_VADDR;
|
|
|
|
/*
|
|
* If NONE of the interesting bits is set, then panic
|
|
*/
|
|
if ((sipr & SIR_MODERROR) == 0)
|
|
panic("unknown level-15 interrupt");
|
|
|
|
/*
|
|
* Handle the nofault case by setting pokefault so that the probe
|
|
* routine knows that something went wrong. It is the responsibility
|
|
* of the probe routine to reset this variable.
|
|
*/
|
|
if (pokefault == -1) {
|
|
/*
|
|
* Clear and acknowledge the error.
|
|
*/
|
|
if (tsunami) {
|
|
mfar = *(u_int *) TSU_MFAR_VADDR;
|
|
mfsr = *(u_int *) TSU_MFSR_VADDR;
|
|
} else {
|
|
mfar = *(u_int *) MFAR_VADDR;
|
|
mfsr = *(u_int *) MFSR_VADDR;
|
|
}
|
|
afar = *(u_int *) AFAR_VADDR;
|
|
afsr = *(u_int *) AFSR_VADDR;
|
|
pokefault = 1;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Handle Parity asynchronous faults first, because there is
|
|
* a minute possibility that this handler could cause
|
|
* further Parity faults (when storing non-fatal fault info
|
|
* in memory) before the first parity fault is handled.
|
|
*/
|
|
|
|
if (tsunami) {
|
|
mfar = *(u_int *) TSU_MFAR_VADDR;
|
|
mfsr = *(u_int *) TSU_MFSR_VADDR;
|
|
} else {
|
|
mfar = *(u_int *) MFAR_VADDR;
|
|
mfsr = *(u_int *) MFSR_VADDR;
|
|
}
|
|
|
|
if (mfsr & MFSR_ERR) {
|
|
printf("Async memory fault mfsr=0x%x mfar=0x%x\n",
|
|
mfsr, mfar);
|
|
panic("async memory fault");
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
/*
|
|
* Asynchronous Faults (SBus store errors)
|
|
*/
|
|
afar = *(u_int *) AFAR_VADDR;
|
|
afsr = *(u_int *) AFSR_VADDR;
|
|
|
|
if (afsr & AFSR_ERR) {
|
|
if (afsr & AFSR_S) {
|
|
#ifdef MULTIPROCESSOR
|
|
if ((afsr & (AFSR_TO|AFSR_BE)) &&
|
|
(mm_find_paddr(MM_SBUS_DEVS, afar, mm_base) !=
|
|
NULL)) {
|
|
handle_aflt(cpuid, AFLT_M_TO_S,
|
|
afsr, afar, 0);
|
|
return;
|
|
}
|
|
#endif
|
|
} else {
|
|
if (afsr & (AFSR_TO|AFSR_BE)) {
|
|
handle_aflt(cpuid, AFLT_M_TO_S,
|
|
afsr, afar, 0);
|
|
return;
|
|
}
|
|
}
|
|
printf("Async fault afsr=0x%x afar=0x%x\n", afsr, afar);
|
|
panic("async hardware fault");
|
|
/*NOTREACHED*/
|
|
}
|
|
panic("unknown async fault");
|
|
/* NOTREACHED */
|
|
}
|
|
#endif SUN4M_35
|
|
|
|
l15_ecc_async_flt()
|
|
{
|
|
u_int afsr; /* an Asynchronous Fault Status Register */
|
|
u_int afar0; /* first Asynchronous Fault Address Reg. */
|
|
u_int afar1; /* second Asynchronous Fault Address Reg. */
|
|
u_int subtype = 0; /* module error fault subtype */
|
|
u_int aerr0 = 0; /* MXCC error register <63:32> */
|
|
u_int aerr1 = 0; /* MXCC error register <31:0> */
|
|
extern u_int get_efsr_vaddr(), get_efar0_vaddr(), get_efar1_vaddr();
|
|
|
|
/* Protect mem-cntrl register loads from conditions in 1101875. */
|
|
/* gather ECC error information */
|
|
afsr = get_efsr_vaddr();
|
|
afar0 = get_efar0_vaddr();
|
|
afar1 = get_efar1_vaddr();
|
|
|
|
/* unlock these registers */
|
|
*(u_int *)EFSR_VADDR = 0;
|
|
|
|
/*
|
|
* Multiple errors, uncorrectable errors,
|
|
* and timeout errors are fatal to the system.
|
|
* One could argue that uncorrectable errors
|
|
* could simply be handled by killing off
|
|
* all processes which have mappings to the
|
|
* page affected, but this doesn't make
|
|
* much sence for ECC. Besides, that
|
|
* approach might cause flakey behaviour
|
|
* when certain essential system daemons
|
|
* are killed off.
|
|
*/
|
|
if (nofault) {
|
|
;
|
|
#ifdef SUN4M_690
|
|
} else if ((cpu == CPU_SUN4M_690) && (afsr & EFSR_TO)) {
|
|
SYS_FATAL_FLT(AFLT_MEM_IF);
|
|
#endif SUN4M_690
|
|
#ifdef SUN4M_50
|
|
} else if ((cpu == CPU_SUN4M_50) &&
|
|
(afsr & (EFSR_SE | EFSR_GE))) {
|
|
SYS_FATAL_FLT(AFLT_MEM_IF);
|
|
#endif SUN4M_50
|
|
} else if (afsr & EFSR_UE) {
|
|
if (afar0 & EFAR0_S) {
|
|
SYS_FATAL_FLT(AFLT_MEM_IF);
|
|
} else {
|
|
handle_aflt(cpuid, AFLT_MEM_IF, afsr, afar0, afar1);
|
|
}
|
|
} else if (afsr & EFSR_CE) {
|
|
handle_aflt(cpuid, AFLT_MEM_IF, afsr, afar0, afar1);
|
|
} else {
|
|
SYS_FATAL_FLT(AFLT_MEM_IF);
|
|
}
|
|
}
|
|
|
|
l15_mts_async_flt()
|
|
{
|
|
u_int afsr; /* an Asynchronous Fault Status Register */
|
|
u_int afar0; /* first Asynchronous Fault Address Reg. */
|
|
u_int afar1 = 0; /* second Asynchronous Fault Address Reg. */
|
|
u_int subtype = 0; /* module error fault subtype */
|
|
u_int aerr0 = 0; /* MXCC error register <63:32> */
|
|
u_int aerr1 = 0; /* MXCC error register <31:0> */
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
extern struct sbus_vme_mm *mm_find_paddr();
|
|
extern struct sbus_vme_mm *mm_base;
|
|
#endif MULTIPROCESSOR
|
|
|
|
/* gather MtoS error information */
|
|
afsr = *(u_int *)MTS_AFSR_VADDR;
|
|
afar0 = *(u_int *)MTS_AFAR_VADDR;
|
|
|
|
/* unlock these registers */
|
|
*(u_int *)MTS_AFSR_VADDR = 0;
|
|
|
|
if (nofault) {
|
|
;
|
|
#ifdef MULTIPROCESSOR
|
|
} else if (afsr & MTSAFSR_S) {
|
|
if (((afsr & MTSAFSR_TO) || (afsr & MTSAFSR_BERR)) &&
|
|
(mm_find_paddr(MM_SBUS_DEVS, afar0, mm_base) != NULL)) {
|
|
handle_aflt(cpuid, AFLT_M_TO_S, afsr, afar0, 0);
|
|
} else {
|
|
SYS_FATAL_FLT(AFLT_M_TO_S);
|
|
}
|
|
#endif MULTIPROCESSOR
|
|
#ifdef notneeded
|
|
/* The consensus seems to be that multiple errors
|
|
** need not be fatal and that taking this out has
|
|
** no unwanted side effects. However, leaving this
|
|
** here for reference, just in case.... -jackv
|
|
*/
|
|
} else if (afsr & (MTSAFSR_ME | MTSAFSR_S)) {
|
|
SYS_FATAL_FLT(AFLT_M_TO_S);
|
|
#endif
|
|
} else {
|
|
handle_aflt(cpuid, AFLT_M_TO_S, afsr, afar0, 0);
|
|
}
|
|
}
|
|
|
|
#ifdef VME
|
|
l15_vme_async_flt()
|
|
{
|
|
u_int afsr; /* an Asynchronous Fault Status Register */
|
|
u_int afar0; /* first Asynchronous Fault Address Reg. */
|
|
u_int afar1 = 0; /* second Asynchronous Fault Address Reg. */
|
|
u_int subtype = 0; /* module error fault subtype */
|
|
u_int aerr0 = 0; /* MXCC error register <63:32> */
|
|
u_int aerr1 = 0; /* MXCC error register <31:0> */
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
extern struct sbus_vme_mm *mm_find_paddr();
|
|
extern struct sbus_vme_mm *mm_base;
|
|
#endif MULTIPROCESSOR
|
|
|
|
/* gather VME error information */
|
|
afsr = *(u_int *)VFSR_VADDR;
|
|
afar0 = *(u_int *)VFAR_VADDR;
|
|
|
|
/* unlock these registers */
|
|
*(u_int *)VFSR_VADDR = 0;
|
|
|
|
if (nofault) {
|
|
;
|
|
#ifdef MULTIPROCESSOR
|
|
} else if ((afsr & VFSR_S) && (!(afsr & VFSR_WB))) {
|
|
if (mm_find_paddr(MM_VME_DEVS, afar0, mm_base) != NULL) {
|
|
handle_aflt(cpuid, AFLT_S_TO_VME, afsr, afar0, afar1);
|
|
} else {
|
|
SYS_FATAL_FLT(AFLT_S_TO_VME)
|
|
}
|
|
#endif MULTIPROCESSOR
|
|
} else if (afsr & (VFSR_ME | VFSR_S | VFSR_WB)) {
|
|
SYS_FATAL_FLT(AFLT_S_TO_VME)
|
|
} else {
|
|
handle_aflt(cpuid, AFLT_S_TO_VME, afsr, afar0, afar1);
|
|
}
|
|
}
|
|
#endif VME
|
|
|
|
extern int vac_parity_chk_dis();
|
|
|
|
l15_mod_async_flt()
|
|
{
|
|
u_int afsr; /* an Asynchronous Fault Status Register */
|
|
u_int afar0; /* first Asynchronous Fault Address Reg. */
|
|
u_int afar1 = 0; /* second Asynchronous Fault Address Reg. */
|
|
u_int subtype = 0; /* module error fault subtype */
|
|
u_int aerr0 = 0; /* MXCC error register <63:32> */
|
|
u_int aerr1 = 0; /* MXCC error register <31:0> */
|
|
u_int aflt_regs[4];
|
|
int is_watchdog;
|
|
int parity_err;
|
|
|
|
/*
|
|
* If the mmu's "watchdog reset" bit is set,
|
|
* dive back into the prom.
|
|
*/
|
|
if (mmu_chk_wdreset() != 0) {
|
|
(void) prom_stopcpu(0);
|
|
}
|
|
|
|
/*
|
|
* read the asynchronous fault registers for all
|
|
* the MMU/cache modules of this cpu. note that
|
|
* this code assumes there is a maximum of two
|
|
* such modules, because this is a hardware limit
|
|
* for Sun-4M.
|
|
* ROSS:
|
|
* mod0: afsr --> aflt_regs[0]
|
|
* afar --> aflt_regs[1]
|
|
* mod1: afsr --> aflt_regs[2] (-1 if mod1 info not exist)
|
|
* afar --> aflt_regs[3]
|
|
* VIKING only:
|
|
* mod0: mfsr --> aflt_regs[0]
|
|
* mfar --> aflt_regs[1]
|
|
* VIKING/MXCC:
|
|
* mod0: mfsr --> aflt_regs[0]
|
|
* mfar --> aflt_regs[1]
|
|
* error register (<63:32>) --> aflt_regs[2]
|
|
* error register (<31:0>) --> aflt_regs[3]
|
|
*/
|
|
(void) mmu_getasyncflt((u_int *)aflt_regs);
|
|
|
|
is_watchdog = 1;
|
|
|
|
switch (mod_info[0].mod_type) {
|
|
case CPU_VIKING:
|
|
/*
|
|
* If there is a viking only module, the async fault (l15)
|
|
* will occur in the case of watchdog reset (which has
|
|
* been taken care of) or SB error.
|
|
* In the case of viking only system, a l15 mod error
|
|
* will be broadcated for SB error after SB trap
|
|
* currently, we panic the system when trap occurs
|
|
* ignore SB case for now.
|
|
*/
|
|
is_watchdog = 0;
|
|
break;
|
|
|
|
case CPU_VIKING_MXCC:
|
|
/*
|
|
* a store buffer error (SB; bit 15 of MFSR) will
|
|
* cause both a trap 0x2b and a broadcast l15
|
|
* (module error) interrupt. The trap will be taken
|
|
* first, but afterwards there will be a pending
|
|
* l15 interrupt waiting for this module.
|
|
* We may have to do something for SB error here.
|
|
* But right now, we just assume that trap handler
|
|
* will take care of the SB error. ignore SB
|
|
* error for now.
|
|
*/
|
|
|
|
/*
|
|
* We check the AFSR for this module to see if it
|
|
* has a valid asynchronous fault.
|
|
* The Async fault information is kept in MXCC error register
|
|
*/
|
|
if (((afsr = aflt_regs[2]) != -1) && (afsr & MXCC_ERR_EV)) {
|
|
afar0 = aflt_regs[3];
|
|
parity_err = vac_parity_chk_dis(aflt_regs[0], afsr);
|
|
if (nofault && !parity_err) {
|
|
;
|
|
} else {
|
|
SYS_FATAL_FLT(AFLT_MODULE)
|
|
}
|
|
is_watchdog = 0;
|
|
}
|
|
break;
|
|
|
|
/*
|
|
* check second module here
|
|
*/
|
|
|
|
default: /* ROSS case */
|
|
/*
|
|
* Check the first (and possibly only) module for
|
|
* this CPU. This module has the lowest MID
|
|
*/
|
|
if ((afsr = aflt_regs[0]) & AFSREG_AFV) {
|
|
afar0 = aflt_regs[1];
|
|
subtype = AFLT_LOMID;
|
|
|
|
/*
|
|
* MODULE asynchronous faults are always
|
|
* fatal, unless we have nofault set,
|
|
* because we can't tell whether it was
|
|
* due to a supervisor access or not, and
|
|
* because even a user access might mean
|
|
* that the cache consistency/snooping
|
|
* could have been compromised.
|
|
*/
|
|
if (nofault) {
|
|
;
|
|
} else {
|
|
SYS_FATAL_FLT(AFLT_MODULE)
|
|
}
|
|
is_watchdog = 0;
|
|
}
|
|
|
|
/*
|
|
* If there is only one module for this CPU,
|
|
* mmu_getasyncflt will store a -1 in aflt_regs[2].
|
|
* If not -1 then we check the AFSR for this second
|
|
* module to see if it has a valid asynchronous
|
|
* fault.
|
|
*/
|
|
if (((afsr = aflt_regs[2]) != -1) && (afsr & AFSREG_AFV)) {
|
|
afar0 = aflt_regs[3];
|
|
|
|
subtype = AFLT_HIMID;
|
|
|
|
if (nofault) {
|
|
;
|
|
} else {
|
|
SYS_FATAL_FLT(AFLT_MODULE)
|
|
}
|
|
is_watchdog = 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* If we had a "MODERR" bit set, and did not
|
|
* see our watchdog bit, and did not see any
|
|
* async fault valid stuff, then assume someone
|
|
* else got a watchdog. Bletch.
|
|
*/
|
|
if (is_watchdog) {
|
|
(void) prom_stopcpu(0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This routine is called by autovectoring a soft level 12
|
|
* interrupt, which was sent by handle_aflt to process a
|
|
* non-fatal asynchronous fault.
|
|
*/
|
|
|
|
int
|
|
sun4m_process_aflt()
|
|
{
|
|
u_int tail;
|
|
u_int afsr;
|
|
u_int afar0;
|
|
u_int afar1;
|
|
int ce;
|
|
extern int report_ce_console;
|
|
extern int noprintf;
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
struct sbus_vme_mm *mmp, *smmp;
|
|
extern struct sbus_vme_mm *mm_find_paddr();
|
|
extern struct sbus_vme_mm *mm_base;
|
|
#endif MULTIPROCESSOR
|
|
|
|
ce = 0;
|
|
/*
|
|
* While there are asynchronous faults which have
|
|
* accumulated, process them.
|
|
* There shouldn't be a problem with the race condition
|
|
* where this loop has just encountered the exit
|
|
* condition where a_head == a_tail, but before process_aflt
|
|
* returns it is interrupted by a level 15 interrupt
|
|
* which stores another asynchronous fault to process.
|
|
* In this (rare) case, we will just take another level 13
|
|
* interrupt after returning from the current one.
|
|
*/
|
|
#ifdef MULTIPROCESSOR
|
|
while (PerCPU[cpuid].a_head != PerCPU[cpuid].a_tail) {
|
|
/*
|
|
* Point to the next asynchronous fault to process.
|
|
*/
|
|
tail = ++(PerCPU[cpuid].a_tail) % MAX_AFLTS;
|
|
PerCPU[cpuid].a_tail = tail;
|
|
|
|
afsr = PerCPU[cpuid].a_flts[tail].aflt_stat;
|
|
afar0 = PerCPU[cpuid].a_flts[tail].aflt_addr0;
|
|
afar1 = PerCPU[cpuid].a_flts[tail].aflt_addr1;
|
|
|
|
/*
|
|
* Process according to the type of write buffer
|
|
* fault.
|
|
*/
|
|
switch (PerCPU[cpuid].a_flts[tail].aflt_type) {
|
|
#else MULTIPROCESSOR
|
|
while (a_head[cpuid] != a_tail[cpuid]) {
|
|
/*
|
|
* Point to the next asynchronous fault to process.
|
|
*/
|
|
tail = ++(a_tail[cpuid]) % MAX_AFLTS;
|
|
a_tail[cpuid] = tail;
|
|
|
|
afsr = a_flts[cpuid][tail].aflt_stat;
|
|
afar0 = a_flts[cpuid][tail].aflt_addr0;
|
|
afar1 = a_flts[cpuid][tail].aflt_addr1;
|
|
|
|
/*
|
|
* Process according to the type of write buffer
|
|
* fault.
|
|
*/
|
|
switch (a_flts[cpuid][tail].aflt_type) {
|
|
#endif MULTIPROCESSOR
|
|
|
|
case AFLT_MEM_IF:
|
|
if (afsr & EFSR_CE) {
|
|
ce = 1;
|
|
if (report_ce_log || report_ce_console)
|
|
log_ce_error = 1;
|
|
}
|
|
log_mem_err(afsr, afar0, afar1, (u_int) 0);
|
|
if (afsr & EFSR_UE){
|
|
if (fix_nc_ecc(afsr, afar0, afar1) == -1) {
|
|
printf("AFSR = 0x%x, ", afsr);
|
|
printf("AFAR0 = 0x%x, AFAR1 = 0x%x\n",
|
|
afar0, afar1);
|
|
panic("Asynchronous fault");
|
|
|
|
}
|
|
}
|
|
|
|
break;
|
|
case AFLT_M_TO_S:
|
|
printf("M-to-S Asynchronous fault ");
|
|
printf("due to user write - non-fatal\n");
|
|
log_mtos_err(afsr, afar0);
|
|
/*
|
|
* We want to kill off the process that
|
|
* performed the write that caused this
|
|
* asynchronous fault. Since all asynchronous
|
|
* faults are handled here before we switch
|
|
* processes, we know which process is
|
|
* responsible. The case where the cache
|
|
* flush code causes write buffers to fill
|
|
* (and async. fault later occurrs in the
|
|
* orig. context) is not a problem, because
|
|
* only the AFLT_MEM_IF case is affected
|
|
* since only main memory is cached in sun4m
|
|
*/
|
|
#ifdef MULTIPROCESSOR
|
|
/*
|
|
* XXX - check hat_oncpu to see if proc was running
|
|
*/
|
|
smmp = mm_base;
|
|
while ((mmp = mm_find_paddr(MM_SBUS_DEVS, afar0, smmp))
|
|
!= NULL) {
|
|
psignal(mmp->mm_p, SIGBUS);
|
|
uunix->u_code = FC_HWERR;
|
|
uunix->u_addr = (char *)afar0;
|
|
smmp = mmp->mm_next;
|
|
}
|
|
#else MULTIPROCESSOR
|
|
psignal(uunix->u_procp, SIGBUS);
|
|
uunix->u_code = FC_HWERR;
|
|
uunix->u_addr = (char *)afar0;
|
|
#endif MULTIPROCESSOR
|
|
break;
|
|
#ifdef VME
|
|
case AFLT_S_TO_VME:
|
|
/*
|
|
* There's not much we can do in this case,
|
|
* since we can't even tell which CPU issued
|
|
* the write which caused the asynchronous
|
|
* fault.
|
|
*/
|
|
printf("VME Asynchronous fault ");
|
|
printf("due to user write - non-fatal\n");
|
|
log_stovme_err(afsr, afar0);
|
|
#ifdef MULTIPROCESSOR
|
|
/*
|
|
* XXX - check hat_oncpu to see if proc was running
|
|
*/
|
|
smmp = mm_base;
|
|
while ((mmp = mm_find_paddr(MM_VME_DEVS, afar0, smmp))
|
|
!= NULL) {
|
|
psignal(mmp->mm_p, SIGBUS);
|
|
uunix->u_code = FC_HWERR;
|
|
uunix->u_addr = (char *)afar0;
|
|
smmp = mmp->mm_next;
|
|
}
|
|
#else MULTIPROCESSOR
|
|
psignal(uunix->u_procp, SIGBUS);
|
|
uunix->u_code = FC_HWERR;
|
|
uunix->u_addr = (char *)afar0;
|
|
#endif MULTIPROCESSOR
|
|
break;
|
|
#endif VME
|
|
|
|
default:
|
|
/*
|
|
* Since these are supposed to be non-fatal
|
|
* asynchronous faults, just ignore any
|
|
* bogus ones.
|
|
*/
|
|
break;
|
|
}
|
|
/*
|
|
* Nitty-Gritty details can be obtained from the
|
|
* asynchronous faults registers, and may be useful
|
|
* by an "expert."
|
|
*/
|
|
if (!ce || report_ce_console){ /* Do not report CE ecc error */
|
|
printf("AFSR = 0x%x, AFAR0 = 0x%x, AFAR1 = 0x%x\n",
|
|
afsr, afar0, afar1);
|
|
} else {
|
|
if (log_ce_error) {
|
|
noprintf = 1;
|
|
printf("AFSR = 0x%x, AFAR0 = 0x%x, AFAR1 = 0x%x\n",
|
|
afsr, afar0, afar1);
|
|
noprintf = 0;
|
|
}
|
|
}
|
|
ce = 0;
|
|
log_ce_error = 0;
|
|
}
|
|
return (1);
|
|
}
|
|
|
|
int
|
|
msparc_process_aflt()
|
|
{
|
|
u_int tail;
|
|
u_int afsr;
|
|
u_int afar;
|
|
extern int report_ce_console;
|
|
extern int noprintf;
|
|
struct sbus_vme_mm *mmp, *smmp;
|
|
extern struct sbus_vme_mm *mm_find_paddr();
|
|
extern struct sbus_vme_mm *mm_base;
|
|
|
|
/*
|
|
* While there are asynchronous faults which have
|
|
* accumulated, process them.
|
|
*
|
|
* There shouldn't be a problem with the race condition
|
|
* where this loop has just encountered the exit
|
|
* condition where a_head == a_tail, but before process_aflt
|
|
* returns it is interrupted by a level 15 interrupt
|
|
* which stores another asynchronous fault to process.
|
|
* In this (rare) case, we will just take another level 12
|
|
* interrupt after returning from the current one.
|
|
*/
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
while (PerCPU[cpuid].a_head != PerCPU[cpuid].a_tail) {
|
|
/*
|
|
* Point to the next asynchronous fault to process.
|
|
*/
|
|
tail = ++(PerCPU[cpuid].a_tail) % MAX_AFLTS;
|
|
PerCPU[cpuid].a_tail = tail;
|
|
|
|
afsr = PerCPU[cpuid].a_flts[tail].aflt_stat;
|
|
afar = PerCPU[cpuid].a_flts[tail].aflt_addr0;
|
|
|
|
/*
|
|
* Process according to the type of write buffer
|
|
* fault.
|
|
*/
|
|
switch (PerCPU[cpuid].a_flts[tail].aflt_type) {
|
|
#else MULTIPROCESSOR
|
|
while (a_head[cpuid] != a_tail[cpuid]) {
|
|
/*
|
|
* Point to the next asynchronous fault to process.
|
|
*/
|
|
tail = ++(a_tail[cpuid]) % MAX_AFLTS;
|
|
a_tail[cpuid] = tail;
|
|
|
|
afsr = a_flts[cpuid][tail].aflt_stat;
|
|
afar = a_flts[cpuid][tail].aflt_addr0;
|
|
|
|
/*
|
|
* Process according to the type of write buffer
|
|
* fault.
|
|
*/
|
|
switch (a_flts[cpuid][tail].aflt_type) {
|
|
#endif MULTIPROCESSOR
|
|
|
|
case AFLT_M_TO_S:
|
|
printf("SBus Asynchronous fault ");
|
|
printf("due to user write - non-fatal\n");
|
|
msparc_log_sbus_err(afsr, afar);
|
|
/*
|
|
* We want to kill off the process that
|
|
* performed the write that caused this
|
|
* asynchronous fault. Since all asynchronous
|
|
* faults are handled here before we switch
|
|
* processes, we know which process is
|
|
* responsible.
|
|
*/
|
|
#ifdef MULTIPROCESSOR
|
|
/*
|
|
* XXX - check hat_oncpu to see if proc was running
|
|
*/
|
|
smmp = mm_base;
|
|
while ((mmp = mm_find_paddr(MM_SBUS_DEVS, afar, smmp))
|
|
!= NULL) {
|
|
psignal(mmp->mm_p, SIGBUS);
|
|
uunix->u_code = FC_HWERR;
|
|
uunix->u_addr = (char *)afar;
|
|
smmp = mmp->mm_next;
|
|
}
|
|
#else MULTIPROCESSOR
|
|
psignal(uunix->u_procp, SIGBUS);
|
|
uunix->u_code = FC_HWERR;
|
|
uunix->u_addr = (char *)afar;
|
|
#endif MULTIPROCESSOR
|
|
break;
|
|
default:
|
|
/*
|
|
* Since these are supposed to be non-fatal
|
|
* asynchronous faults, just ignore any
|
|
* bogus ones.
|
|
*/
|
|
break;
|
|
}
|
|
/*
|
|
* Nitty-Gritty details can be obtained from the
|
|
* asynchronous faults registers, and may be useful
|
|
* by an "expert."
|
|
*/
|
|
printf("AFSR=0x%x, AFAR=0x%x\n", afsr, afar);
|
|
}
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* async() calls this routine to store info. for a particular
|
|
* non-fatal asynchronous fault in this CPU's queue of
|
|
* asynchronous faults to be handled, and to invoke a level-12
|
|
* interrupt thread to process this fault.
|
|
*/
|
|
handle_aflt(cpu_id, type, afsr, afar0, afar1)
|
|
int cpu_id; /* CPU responsible for this fault */
|
|
int type; /* type of asynchronous fault */
|
|
u_int afsr; /* Asynchronous Fault Status Register */
|
|
u_int afar0; /* Asynchronous Fault Address Register 0 */
|
|
u_int afar1; /* Asynchronous Fault Address Register 1 */
|
|
{
|
|
int head;
|
|
|
|
/*
|
|
* Increment the head of the circular queue of fault
|
|
* structures by 1 (modulo MAX_AFLTS).
|
|
*/
|
|
#ifdef MULTIPROCESSOR
|
|
head = ++(PerCPU[cpu_id].a_head) % MAX_AFLTS;
|
|
PerCPU[cpu_id].a_head = head;
|
|
if (head == PerCPU[cpu_id].a_tail)
|
|
panic("Overflow of asynchronous faults.\n");
|
|
/* NOTREACHED */
|
|
|
|
/*
|
|
* Store the asynchronous fault information supplied.
|
|
*/
|
|
PerCPU[cpu_id].a_flts[head].aflt_type = type;
|
|
PerCPU[cpu_id].a_flts[head].aflt_stat = afsr;
|
|
PerCPU[cpu_id].a_flts[head].aflt_addr0 = afar0;
|
|
PerCPU[cpu_id].a_flts[head].aflt_addr1 = afar1;
|
|
#else MULTIPROCESSOR
|
|
head = ++(a_head[cpu_id]) % MAX_AFLTS;
|
|
a_head[cpu_id] = head;
|
|
if (head == a_tail[cpu_id])
|
|
panic("Overflow of asynchronous faults.\n");
|
|
/* NOTREACHED */
|
|
|
|
/*
|
|
* Store the asynchronous fault information supplied.
|
|
*/
|
|
a_flts[cpu_id][head].aflt_type = type;
|
|
a_flts[cpu_id][head].aflt_stat = afsr;
|
|
a_flts[cpu_id][head].aflt_addr0 = afar0;
|
|
a_flts[cpu_id][head].aflt_addr1 = afar1;
|
|
#endif MULTIPROCESSOR
|
|
|
|
/*
|
|
* Send a soft directed interrupt at level AFLT_HANDLER_LEVEL
|
|
* to onesself to process this non-fatal asynchronous
|
|
* fault. This soft int will be forwarded to the holder
|
|
* of the kernel lock, if necessary.
|
|
*/
|
|
send_dirint(cpu_id, AFLT_HANDLER_LEVEL);
|
|
}
|
|
|
|
char *nameof_siz[] = {
|
|
"byte", "short", "word", "double", "quad", "line", "[siz=6]", "[siz=7]",
|
|
};
|
|
|
|
char *nameof_ssiz[] = {
|
|
"word", "byte", "short", "[siz=3]", "quad", "line", "[siz=6]", "double",
|
|
};
|
|
|
|
log_mtos_err(afsr, afar0)
|
|
u_int afsr; /* Asynchronous Fault Status Register */
|
|
u_int afar0; /* Asynchronous Fault Address Register */
|
|
{
|
|
if (afsr & MTSAFSR_ME)
|
|
printf("\tMultiple Errors\n");
|
|
if (afsr & MTSAFSR_S)
|
|
printf("\tError during Supv mode cycle\n");
|
|
else
|
|
printf("\tError during User mode cycle\n");
|
|
if (afsr & MTSAFSR_LE)
|
|
printf("\tLate Error\n");
|
|
if (afsr & MTSAFSR_TO)
|
|
printf("\tTimeout Error\n");
|
|
if (afsr & MTSAFSR_BERR)
|
|
printf("\tBus Error\n");
|
|
printf("\tRequested transaction: %s%s at %x:%x for mid %x\n",
|
|
afsr&MTSAFSR_S ? "supv " : "user ",
|
|
nameof_siz[(afsr & MTSAFSR_SIZ) >> MTSAFSR_SIZ_SHFT],
|
|
afsr & MTSAFSR_PA, afar0,
|
|
(afsr&MTSAFSR_MID) >> MTSAFSR_MID_SHFT);
|
|
printf("\tSpecific cycle: %s at %x:%x\n",
|
|
nameof_ssiz[(afsr & MTSAFSR_SSIZ) >> MTSAFSR_SSIZ_SHFT],
|
|
afsr & MTSAFSR_PA,
|
|
((afar0 & ~0x1F) | ((afsr & MTSAFSR_SA) >> MTSAFSR_SA_SHFT)));
|
|
}
|
|
|
|
msparc_log_sbus_err(afsr, afar)
|
|
u_int afsr; /* Asynchronous Fault Status Register */
|
|
u_int afar; /* Asynchronous Fault Address Register */
|
|
{
|
|
if (afsr & AFSR_ME)
|
|
printf("\tMultiple Errors\n");
|
|
if (afsr & AFSR_S)
|
|
printf("\tError during Supv mode cycle\n");
|
|
else
|
|
printf("\tError during User mode cycle\n");
|
|
if (afsr & AFSR_LE)
|
|
printf("\tLate Error\n");
|
|
if (afsr & AFSR_TO)
|
|
printf("\tTimeout Error\n");
|
|
if (afsr & AFSR_BE)
|
|
printf("\tBus Error\n");
|
|
|
|
printf("\tRequested transaction: %s%s at %x for mid %x\n",
|
|
(afsr & AFSR_S) ? "supv " : "user ",
|
|
nameof_ssiz[(afsr & AFSR_SIZ) >> MTSAFSR_SIZ_SHFT], afar,
|
|
(afsr & AFSR_MID) >> MTSAFSR_MID_SHFT);
|
|
}
|
|
|
|
#ifdef VME
|
|
log_stovme_err(afsr, afar0)
|
|
u_int afsr; /* Asynchronous Fault Status Register */
|
|
u_int afar0; /* Asynchronous Fault Address Register */
|
|
{
|
|
if (afsr & VFSR_TO) {
|
|
printf("\tVME address %x Timed Out\n", afar0);
|
|
} else
|
|
if (afsr & VFSR_BERR) {
|
|
printf("\tVME address %x received a BERR.\n", afar0);
|
|
} else
|
|
if (afsr & VFSR_WB) {
|
|
printf("\tIO Cache Write-Back Error at address %x\n", afar0);
|
|
} else
|
|
printf("\tStrange S-to-VME error at %x\n", afar0);
|
|
}
|
|
#endif VME
|
|
|
|
/*
|
|
* This table used to determine which bit(s) is(are) bad when an ECC
|
|
* error occurrs. The array is indexed by the 8-bit syndrome which
|
|
* comes from the ECC Memory Fault Status Register. The entries
|
|
* of this array have the following semantics:
|
|
*
|
|
* 00-63 The number of the bad bit, when only one bit is bad.
|
|
* 64 ECC bit C0 is bad.
|
|
* 65 ECC bit C1 is bad.
|
|
* 66 ECC bit C2 is bad.
|
|
* 67 ECC bit C3 is bad.
|
|
* 68 ECC bit C4 is bad.
|
|
* 69 ECC bit C5 is bad.
|
|
* 70 ECC bit C6 is bad.
|
|
* 71 ECC bit C7 is bad.
|
|
* 72 Two bits are bad.
|
|
* 73 Three bits are bad.
|
|
* 74 Four bits are bad.
|
|
* 75 More than Four bits are bad.
|
|
* 76 NO bits are bad.
|
|
* Based on "Galaxy Memory Subsystem SPECIFICATION" rev 0.6, pg. 28.
|
|
*/
|
|
char ecc_syndrome_tab[] =
|
|
{
|
|
76, 64, 65, 72, 66, 72, 72, 73, 67, 72, 72, 73, 72, 73, 73, 74,
|
|
68, 72, 72, 32, 72, 57, 75, 72, 72, 37, 49, 72, 40, 72, 72, 44,
|
|
69, 72, 72, 33, 72, 61, 4, 72, 72, 75, 53, 72, 45, 72, 72, 41,
|
|
72, 0, 1, 72, 10, 72, 72, 75, 15, 72, 72, 75, 72, 73, 73, 72,
|
|
70, 72, 72, 42, 72, 59, 39, 72, 72, 75, 51, 72, 34, 72, 72, 46,
|
|
72, 25, 29, 72, 27, 74, 72, 75, 31, 72, 74, 75, 72, 75, 75, 72,
|
|
72, 75, 36, 72, 7, 72, 72, 54, 75, 72, 72, 62, 72, 48, 56, 72,
|
|
73, 72, 72, 75, 72, 75, 22, 72, 72, 18, 75, 72, 73, 72, 72, 75,
|
|
71, 72, 72, 47, 72, 63, 75, 72, 72, 6, 55, 72, 35, 72, 72, 43,
|
|
72, 5, 75, 72, 75, 72, 72, 50, 38, 72, 72, 58, 72, 52, 60, 72,
|
|
72, 17, 21, 72, 19, 74, 72, 75, 23, 72, 74, 75, 72, 75, 75, 72,
|
|
73, 72, 72, 75, 72, 75, 30, 72, 72, 26, 75, 72, 73, 72, 72, 75,
|
|
72, 8, 13, 72, 2, 72, 72, 73, 3, 72, 72, 73, 72, 75, 75, 72,
|
|
73, 72, 72, 73, 72, 75, 16, 72, 72, 20, 75, 72, 75, 72, 72, 75,
|
|
73, 72, 72, 73, 72, 75, 24, 72, 72, 28, 75, 72, 75, 72, 72, 75,
|
|
74, 12, 9, 72, 14, 72, 72, 75, 11, 72, 72, 75, 72, 75, 75, 74,
|
|
};
|
|
|
|
|
|
extern int noprintf;
|
|
|
|
#define MAX_CE_ERR 255
|
|
|
|
struct ce_info {
|
|
char name[8];
|
|
u_int cnt;
|
|
};
|
|
|
|
#define MAX_SIMM 256
|
|
struct ce_info mem_ce_simm[MAX_SIMM] = 0;
|
|
|
|
|
|
log_ce_mem_err(afsr, afar0, afar1, get_unum)
|
|
u_int afsr; /* ECC Memory Fault Status Register */
|
|
u_int afar0; /* ECC Memory Fault Address Register 0 */
|
|
u_int afar1; /* ECC Memory Fault Address Register 1 */
|
|
char *(*get_unum) ();
|
|
{
|
|
int i, size, block, offset;
|
|
u_int aligned_afar1;
|
|
char *unum;
|
|
|
|
u_int syn_code; /* Code from ecc_syndrome_tab */
|
|
|
|
/*
|
|
* Use the 8-bit syndrome in the EFSR to index
|
|
* ecc_syndrome_tab to get code indicating which bit(s)
|
|
* is(are) bad.
|
|
*/
|
|
syn_code = ecc_syndrome_tab[((afsr & EFSR_SYND) >> EFSR_SYND_SHFT)];
|
|
|
|
/*
|
|
* The EFAR contains the address of an 8-byte group. For 16 and 32
|
|
* bytes brust, the address is the start of the brust group. So
|
|
* added the proper offset according to the info given in the
|
|
* EFSR and the syndrome table.
|
|
*/
|
|
|
|
offset = 0;
|
|
/*
|
|
* Which of the 8 byte group during 16 bytes and 32 bytes
|
|
* transfer
|
|
*/
|
|
block = (afsr & EFSR_DW) >> 4;
|
|
|
|
size = (afar0 & EFAR0_SIZ) >> 8;
|
|
switch (size){
|
|
case 4:
|
|
case 5: offset = block*8;
|
|
break;
|
|
default: offset = 0; break;
|
|
}
|
|
|
|
/*
|
|
* For some reason, the EFAR sometimes contains a non-8 byte aligned
|
|
* address. We should ignore the lowest 3 bits
|
|
*/
|
|
aligned_afar1 = afar1 & 0xFFFFFFF8;
|
|
|
|
if (syn_code < 72) {
|
|
/*
|
|
* Syn_code contains the bit no. of the group that is bad.
|
|
*/
|
|
if (syn_code < 64)
|
|
offset = offset + (7 - syn_code/8);
|
|
else offset = offset + (7 - syn_code%8);
|
|
|
|
if ((unum = (*get_unum)(aligned_afar1 + (u_int) offset,
|
|
afar0 & EFAR0_PA)) == (char *)0)
|
|
printf("simm decode function failed\n");
|
|
} else {
|
|
for (i = 0; i < 8; i++){
|
|
if ((unum = (*get_unum)(aligned_afar1 + offset +
|
|
(u_int) i, afar0 & EFAR0_PA)) == (char *)0)
|
|
printf("simm decode function failed\n");
|
|
}
|
|
}
|
|
for (i = 0; i < MAX_SIMM; i++) {
|
|
if (mem_ce_simm[i].name[0] == NULL) {
|
|
(void) strcpy(mem_ce_simm[i].name, unum);
|
|
mem_ce_simm[i].cnt++;
|
|
break;
|
|
} else if (!strcmp(unum, mem_ce_simm[i].name)) {
|
|
if (++mem_ce_simm[i].cnt > MAX_CE_ERR) {
|
|
printf("Multiple softerrors: ");
|
|
printf("Seen %x Corrected Softerrors ",
|
|
mem_ce_simm[i].cnt);
|
|
printf("from SIMM %s\n", unum);
|
|
printf("Consider replacing the SIMM.\n");
|
|
mem_ce_simm[i].cnt = 0;
|
|
log_ce_error = 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (i >= MAX_SIMM)
|
|
printf("Softerror: mem_ce_simm[] out of space.\n");
|
|
if (log_ce_error) {
|
|
if (syn_code < 72)
|
|
printf("\tCorrected SIMM at: %s\n", unum);
|
|
else
|
|
printf("\tPossible Corrected SIMM at: %s\n", unum);
|
|
printf("\toffset is %d\n", offset);
|
|
if (syn_code < 64)
|
|
printf("\tBit %2d was corrected\n", syn_code);
|
|
else if (syn_code < 72)
|
|
printf("\tECC Bit %2d was corrected\n", syn_code - 64);
|
|
else{
|
|
switch (syn_code) {
|
|
case 72:
|
|
printf("\tTwo bits were corrected\n");
|
|
break;
|
|
case 73:
|
|
printf("\tThree bits were corrected\n");
|
|
break;
|
|
case 74:
|
|
printf("\tFour bits were corrected\n");
|
|
break;
|
|
case 75:
|
|
printf("\tMore than Four bits were corrected\n");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
log_ue_mem_err(afar0, afar1, get_unum)
|
|
u_int afar0; /* ECC Memory Fault Address Register 0 */
|
|
u_int afar1; /* ECC Memory Fault Address Register 1 */
|
|
char *(*get_unum) ();
|
|
{
|
|
char *unum;
|
|
|
|
if ((unum = (*get_unum)(afar1, afar0 & EFAR0_PA)) == (char *)0)
|
|
printf("simm decode function failed\n");
|
|
else
|
|
printf("\tUncorrected SIMM at: %s\n", unum);
|
|
}
|
|
|
|
log_mem_err(afsr, afar0, afar1, ue_on_read)
|
|
u_int afsr; /* ECC Memory Fault Status Register */
|
|
u_int afar0; /* ECC Memory Fault Address Register 0 */
|
|
u_int afar1; /* ECC Memory Fault Address Register 1 */
|
|
u_int ue_on_read;
|
|
{
|
|
char *(*get_unum) ();
|
|
|
|
if (!ue_on_read) {
|
|
/*
|
|
* don't printf any corrected ecc info on the console
|
|
* only log to the log file
|
|
* report_ce_console is default to 0
|
|
* print on the console when report_ce_console is set
|
|
*/
|
|
if ((afsr & EFSR_CE) && log_ce_error) {
|
|
if (report_ce_console)
|
|
noprintf = 0;
|
|
else
|
|
noprintf = 1;
|
|
printf("Softerror: ECC Memory Error Corrected.\n");
|
|
}
|
|
|
|
if (afsr & EFSR_ME)
|
|
printf("\tMultiple errors.\n");
|
|
|
|
|
|
switch (cpu) {
|
|
#ifdef SUN4M_50
|
|
case CPU_SUN4M_50:
|
|
if (afsr & EFSR_GE)
|
|
printf("Graphics Error.\n");
|
|
if (afsr & EFSR_SE)
|
|
printf("Misreferenced Slot Error.\n");
|
|
break;
|
|
#else SUN4M_690
|
|
case CPU_SUN4M_690:
|
|
if (afsr & EFSR_TO) {
|
|
printf("Timeout on Write access to ");
|
|
printf("expansion memory.\n");
|
|
}
|
|
break;
|
|
#endif SUN4M_50
|
|
}
|
|
if (afsr & EFSR_UE)
|
|
printf("Uncorrectable ECC Memory Error.\n");
|
|
} else
|
|
printf("Uncorrectable ECC Memory Error.\n");
|
|
|
|
(void) prom_getprop(prom_nextnode(0), "get-unum", (caddr_t)&get_unum);
|
|
if (get_unum == (char *(*)())0)
|
|
printf("no simm decode function available\n");
|
|
else if ((!ue_on_read) && (afsr & EFSR_CE))
|
|
log_ce_mem_err(afsr, afar0, afar1, get_unum);
|
|
else if ((ue_on_read) || (afsr & EFSR_UE))
|
|
log_ue_mem_err(afar0, afar1, get_unum);
|
|
|
|
if ((ue_on_read) || (!(afsr & EFSR_CE)) || log_ce_error)
|
|
printf("\tPhysical Address = %x:%x\n", afar0 & EFAR0_PA, afar1);
|
|
|
|
if (noprintf) noprintf = 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Kernel VM initialization.
|
|
* Assumptions about kernel address space ordering:
|
|
* (yes, there are some gaps)
|
|
* o user space
|
|
* o kernel text
|
|
* o kernel data/bss
|
|
* o kernel data structures
|
|
* o Per-Cpu area for "this" processor [0xFD0xxxxx]
|
|
* o Per-Cpu areas for all processors [0xFExxxxxx]
|
|
* o debugger (optional)
|
|
* o monitor
|
|
* o dvma
|
|
* o devices, u area
|
|
*/
|
|
kvm_init()
|
|
{
|
|
register addr_t v;
|
|
union ptpe *ptpe;
|
|
extern char start[], etext[];
|
|
|
|
/*
|
|
* Put the kernel segments in kernel address space. Make them
|
|
* "kernel memory" segment objects. We make the "text" segment
|
|
* include all the fixed location devices because there are no
|
|
* soft ptes backing up those mappings.
|
|
*/
|
|
(void) seg_attach(&kas, (addr_t)KERNELBASE, (u_int)(vvm - KERNELBASE),
|
|
&ktextseg);
|
|
(void) segkmem_create(&ktextseg, (caddr_t)NULL);
|
|
(void) seg_attach(&kas, (addr_t)vvm, (u_int)
|
|
((u_int)econtig - (u_int)vvm), &kvalloc);
|
|
(void) segkmem_create(&kvalloc, (caddr_t)NULL);
|
|
(void) seg_attach(&kas, (addr_t)HEAPBASE, (u_int)(BUFLIMIT - HEAPBASE),
|
|
&bufheapseg);
|
|
(void) segkmem_create(&bufheapseg, (caddr_t)Heapptes);
|
|
#ifdef MULTIPROCESSOR
|
|
(void) seg_attach(&kas, (addr_t)VA_PERCPUME, 0x1000000, &kpercpume);
|
|
(void) segkmem_create(&kpercpume, (caddr_t)NULL);
|
|
(void) seg_attach(&kas, (addr_t)VA_PERCPU, (u_int) (nmod*PERCPUSIZE),
|
|
&kpercpu);
|
|
(void) segkmem_create(&kpercpu, (caddr_t)NULL);
|
|
#endif MULTIPROCESSOR
|
|
(void) seg_attach(&kas, (addr_t)SYSBASE,
|
|
(u_int)Syslimit - SYSBASE, &kseg);
|
|
(void) segkmem_create(&kseg, (caddr_t)Sysmap);
|
|
/*
|
|
* All level 1 entries other than the kernel were set invalid
|
|
* when our prototype level 1 table was created. Thus, we only need
|
|
* to deal with addresses above KERNELBASE here. Also, all ptes
|
|
* for this region have been allocated and locked, or they are not
|
|
* used. Thus, all we need to do is set protections. Invalid until
|
|
* start except for msgbuf/scb page which is KW.
|
|
*/
|
|
|
|
for (v = (addr_t)KERNELBASE; v < (addr_t)start; v += PAGESIZE) {
|
|
if ((u_int)v >= (((u_int)&msgbuf) & PAGEMASK) &&
|
|
(u_int)v <= (((u_int)&msgbuf + sizeof (struct msgbuf)) &
|
|
PAGEMASK))
|
|
(void) as_setprot(&kas, v, PAGESIZE,
|
|
PROT_READ | PROT_WRITE);
|
|
else
|
|
(void) as_setprot(&kas, v, PAGESIZE, 0);
|
|
}
|
|
|
|
/*
|
|
* (Normally) Read-only until end of text.
|
|
*/
|
|
(void) as_setprot(&kas, v, (u_int)(etext - v),
|
|
(u_int)(PROT_READ | PROT_EXEC | ((kernprot == 0)? PROT_WRITE : 0)));
|
|
v = (addr_t)roundup((u_int)etext, PAGESIZE);
|
|
/*
|
|
* Writable to end of allocated region.
|
|
*/
|
|
(void) as_setprot(&kas, v, (u_int)(econtig - v),
|
|
PROT_READ | PROT_WRITE | PROT_EXEC);
|
|
v = (addr_t)roundup((u_int)econtig, PAGESIZE);
|
|
/*
|
|
* The following invalidate segu, and bufheapseg
|
|
*/
|
|
#ifdef MULTIPROCESSOR
|
|
/*
|
|
* Invalid to VA_PERCPUME
|
|
* skip VA_PERCPUME -> (VA_PERCPUME + nmod*PERCPUSIZE)
|
|
*/
|
|
(void) as_setprot(&kas, v, VA_PERCPUME - (u_int)v, 0);
|
|
v = (addr_t)roundup((u_int)(VA_PERCPU + nmod*PERCPUSIZE), PAGESIZE);
|
|
#endif MULTIPROCESSOR
|
|
|
|
/*
|
|
* Invalid to Syslimit.
|
|
*/
|
|
(void) as_setprot(&kas, v, (u_int)(Syslimit - v), 0);
|
|
v = (addr_t)Syslimit;
|
|
/*
|
|
* We rely on the fact that all the kernel ptes in the upper segment
|
|
* are in order, so we only need to locate them once.
|
|
*/
|
|
ptpe = hat_ptefind(&kas, v);
|
|
/*
|
|
* Invalid until the Debug and monitor area
|
|
* Note: running this up to the end was a bad idea,
|
|
* it caused us to walk off the end of "ptes" and into
|
|
* the "ctxs" array. This may be due to the fact that
|
|
* we are giving the last of eight megs to the fakeprom,
|
|
* but who knows.
|
|
*/
|
|
for (; (u_int)v < (u_int)DEBUGSTART; v += MMU_PAGESIZE, ptpe++)
|
|
ptpe->pte.EntryType = MMU_ET_INVALID;
|
|
/*
|
|
* Now create a segment for the DVMA virtual
|
|
* addresses using the segkmem segment driver.
|
|
*/
|
|
(void) seg_attach(&kas, DVMA, (u_int)mmu_ptob(dvmasize), &kdvmaseg);
|
|
(void) segkmem_create(&kdvmaseg, (caddr_t)NULL);
|
|
/*
|
|
* Flush the PDC of any old mappings.
|
|
*/
|
|
mmu_flushall();
|
|
}
|
|
|
|
/*
|
|
* pagecopy() and pagezero() assume that they will not be called at
|
|
* interrupt level. But pagecopy can sleep during its operations so
|
|
* we use a cache of addresses here to manage the destination mappings.
|
|
*/
|
|
#define VA_CACHE_SIZE 2 /* must be between 1 and 31 */
|
|
#define VA_CACHE_MASK ((1 << VA_CACHE_SIZE) - 1)
|
|
|
|
static int va_cache_valid = 0;
|
|
static addr_t va_cache[VA_CACHE_SIZE];
|
|
|
|
/*
|
|
* Copy the data from `addr' to physical page represented by `pp'.
|
|
* `addr' is a (user) virtual address which we might fault on.
|
|
*/
|
|
pagecopy(addr, pp)
|
|
addr_t addr;
|
|
struct page *pp;
|
|
{
|
|
register addr_t va;
|
|
register u_long a;
|
|
union {
|
|
struct pte u_pte;
|
|
int u_ipte;
|
|
} pte;
|
|
int dohat;
|
|
int s;
|
|
|
|
if (va_cache_valid) {
|
|
/* pull address out of cache */
|
|
a = 0;
|
|
for (;;) {
|
|
if ((va_cache_valid & (1 << a)) != 0) {
|
|
va_cache_valid &= ~(1 << a);
|
|
va = va_cache[a];
|
|
break;
|
|
}
|
|
a++;
|
|
}
|
|
} else {
|
|
/*
|
|
* Cannot get an entry from the cache,
|
|
* allocate a slot for ourselves.
|
|
*/
|
|
s = splhigh();
|
|
while ((a = rmalloc(kernelmap, (long)CLSIZE)) == NULL) {
|
|
mapwant(kernelmap)++;
|
|
(void) sleep((caddr_t)kernelmap, PZERO - 1);
|
|
}
|
|
(void) splx(s);
|
|
va = Sysbase + mmu_ptob(a);
|
|
hat_pteload(&kseg, va, (struct page *)NULL, &mmu_pteinvalid, 1);
|
|
}
|
|
|
|
if (!pp->p_mapping) {
|
|
pte.u_pte.PhysicalPageNumber = page_pptonum(pp);
|
|
pte.u_pte.AccessPermissions = MMU_STD_SRWX;
|
|
pte.u_pte.EntryType = MMU_ET_PTE;
|
|
hat_pteload(&kseg, va, (struct page *)NULL,
|
|
&pte.u_pte, PTELD_LOCK | PTELD_NOSYNC);
|
|
dohat = 0;
|
|
} else {
|
|
hat_mempte(pp, PROT_READ | PROT_WRITE, &pte.u_pte, va);
|
|
hat_pteload(&kseg, va, pp, &pte.u_pte,
|
|
PTELD_LOCK | PTELD_NOSYNC);
|
|
dohat = 1;
|
|
}
|
|
|
|
(void) copyin((caddr_t)addr, va, PAGESIZE);
|
|
|
|
if (dohat) {
|
|
hat_unload(&kseg, va, PAGESIZE);
|
|
} else {
|
|
vac_pageflush(va);
|
|
hat_pteload(&kseg, va, (struct page *)NULL,
|
|
&mmu_pteinvalid, PTELD_LOCK | PTELD_NOSYNC);
|
|
}
|
|
|
|
if ((va_cache_valid & VA_CACHE_MASK) == VA_CACHE_MASK) {
|
|
/* cache is full, just return the virtual space */
|
|
hat_unlock(&kseg, va);
|
|
s = splhigh();
|
|
rmfree(kernelmap, (long)CLSIZE, (u_long)btokmx(va));
|
|
(void) splx(s);
|
|
} else {
|
|
/* find the first non-valid slot and store va there */
|
|
a = 0;
|
|
for (;;) {
|
|
if ((va_cache_valid & (1 << a)) == 0) {
|
|
va_cache[a] = va;
|
|
va_cache_valid |= (1 << a);
|
|
break;
|
|
}
|
|
a++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Zero the physical page from off to off + len given by `pp'
|
|
* without changing the reference and modified bits of page.
|
|
* pagezero uses global CADDR2 and assumes that no one uses this
|
|
* map at interrupt level and no one sleeps with an active mapping there.
|
|
*/
|
|
pagezero(pp, off, len)
|
|
struct page *pp;
|
|
u_int off, len;
|
|
{
|
|
int dohat;
|
|
union {
|
|
struct pte u_pte;
|
|
int u_ipte;
|
|
} pte;
|
|
|
|
ASSERT((int)len > 0 && (int)off >= 0 && off + len <= PAGESIZE);
|
|
|
|
if (!pp->p_mapping) {
|
|
pte.u_pte.PhysicalPageNumber = page_pptonum(pp);
|
|
pte.u_pte.EntryType = MMU_ET_PTE;
|
|
pte.u_pte.AccessPermissions = MMU_STD_SRWX;
|
|
hat_pteload(&kseg, CADDR2, (struct page *)NULL,
|
|
&pte.u_pte, PTELD_LOCK | PTELD_NOSYNC);
|
|
dohat = 0;
|
|
} else {
|
|
hat_mempte(pp, PROT_READ | PROT_WRITE, &pte.u_pte,
|
|
(addr_t)CADDR2);
|
|
hat_pteload(&kseg, CADDR2, pp, &pte.u_pte,
|
|
PTELD_LOCK | PTELD_NOSYNC);
|
|
dohat = 1;
|
|
}
|
|
|
|
bzero(CADDR2 + off, len);
|
|
|
|
if (dohat) {
|
|
hat_unload(&kseg, CADDR2, PAGESIZE);
|
|
} else {
|
|
vac_pageflush(CADDR2);
|
|
hat_pteload(&kseg, CADDR2, (struct page *)NULL,
|
|
&mmu_pteinvalid, PTELD_LOCK | PTELD_NOSYNC);
|
|
}
|
|
}
|
|
|
|
#ifdef VMEMDEBUG
|
|
/*
|
|
* Calculate the checksum for the physical page given by `pp'
|
|
* without changing the reference and modified bits of page.
|
|
* pagesum uses global CADDR2 and assumes that no one uses this
|
|
* map at interrupt level and no one sleeps with an active mapping there.
|
|
*/
|
|
int
|
|
pagesum(pp)
|
|
struct page *pp;
|
|
{
|
|
int dohat;
|
|
union {
|
|
struct pte u_pte;
|
|
int u_ipte;
|
|
} pte;
|
|
int checksum = 0;
|
|
int *first, *last, *ip;
|
|
|
|
if (!pp->p_mapping) {
|
|
pte.u_pte.PhysicalPageNumber = page_pptonum(pp);
|
|
pte.u_pte.EntryType = MMU_ET_PTE;
|
|
pte.u_pte.AccessPermissions = MMU_STD_SRWX;
|
|
hat_pteload(&kseg, CADDR2, (struct page *)NULL,
|
|
&pte.u_pte, PTELD_LOCK | PTELD_NOSYNC);
|
|
dohat = 0;
|
|
} else {
|
|
hat_mempte(pp, PROT_READ | PROT_WRITE, &pte.u_pte,
|
|
(addr_t)CADDR2);
|
|
hat_pteload(&kseg, CADDR2, pp, &pte.u_pte,
|
|
PTELD_LOCK | PTELD_NOSYNC);
|
|
dohat = 1;
|
|
}
|
|
|
|
first = (int *)CADDR2;
|
|
last = (int *)(CADDR2 + PAGESIZE);
|
|
|
|
for (ip = first; ip < last; ip++)
|
|
checksum += *ip;
|
|
|
|
if (dohat) {
|
|
hat_unload(&kseg, CADDR2, PAGESIZE);
|
|
} else {
|
|
vac_pageflush(CADDR2);
|
|
hat_pteload(&kseg, CADDR2, (struct page *)NULL,
|
|
&mmu_pteinvalid, PTELD_LOCK | PTELD_NOSYNC);
|
|
}
|
|
|
|
return (checksum);
|
|
}
|
|
#endif VMEMDEBUG
|
|
|
|
/*
|
|
* Console put and get character routines.
|
|
*/
|
|
cnputc(c)
|
|
register int c;
|
|
{
|
|
register int s;
|
|
|
|
s = spl7();
|
|
if (c == '\n')
|
|
prom_putchar('\r');
|
|
prom_putchar(c);
|
|
(void) splx(s);
|
|
}
|
|
|
|
int
|
|
cngetc()
|
|
{
|
|
extern u_char prom_getchar();
|
|
|
|
return ((int)prom_getchar());
|
|
}
|
|
|
|
caddr_t
|
|
kstackinit(stackbase)
|
|
register caddr_t stackbase;
|
|
{
|
|
register u_int sp;
|
|
|
|
sp = KERNSTACK - SA(MINFRAME + sizeof (struct regs));
|
|
sp = (u_int)stackbase + sp;
|
|
if (sp & 0x7)
|
|
sp -= 4;
|
|
return ((caddr_t)sp);
|
|
}
|
|
|
|
/*
|
|
* fork a kernel process at address "startaddr".
|
|
* Used by main to create init and pageout.
|
|
*/
|
|
/* ARGSUSED */
|
|
kern_proc(startaddr, arg)
|
|
void (*startaddr)();
|
|
int arg;
|
|
{
|
|
register struct seguser *newseg;
|
|
register struct frame *fp;
|
|
struct proc *pp;
|
|
|
|
/* This segment may not be needed for some kernel processes. */
|
|
newseg = (struct seguser *) segu_get();
|
|
if (newseg == (struct seguser *) 0)
|
|
panic("can't create kernel process");
|
|
fp = (struct frame *)KSTACK(newseg);
|
|
fp->fr_local[6] = (u_int)startaddr; /* l6 gets start address */
|
|
fp->fr_local[5] = 1; /* means fork a kernel proc */
|
|
fp->fr_local[0] = arg; /* l0 gets the argument */
|
|
pp = freeproc;
|
|
freeproc = pp->p_nxt;
|
|
newproc(0, (struct as *)0, (int)0, (struct file *)0, newseg, pp);
|
|
}
|
|
|
|
/*
|
|
* For things that depend on register state being on the stack,
|
|
* copy any register windows that get saved into the window buffer
|
|
* (in the uarea) onto the stack. This normally gets fixed up
|
|
* before returning to a user program. Callers of this routine
|
|
* require this to happen immediately because a later kernel
|
|
* operation depends on window state (like instruction simulation).
|
|
*/
|
|
flush_user_windows_to_stack()
|
|
{
|
|
register int j, k;
|
|
register char *sp;
|
|
register struct pcb *pcb = &u.u_pcb;
|
|
|
|
flush_user_windows();
|
|
|
|
j = pcb->pcb_wbcnt;
|
|
while (j > 0) {
|
|
sp = pcb->pcb_spbuf[--j];
|
|
if (((int)sp & (STACK_ALIGN - 1)) ||
|
|
copyout((caddr_t)&pcb->pcb_wbuf[j],
|
|
sp, sizeof (struct rwindow))) {
|
|
/* failed to copy out window, don't do anything */
|
|
} else {
|
|
/* succeded; move other windows down */
|
|
pcb->pcb_wbcnt--;
|
|
for (k = j; k < pcb->pcb_wbcnt; k++) {
|
|
pcb->pcb_spbuf[k] = pcb->pcb_spbuf[k+1];
|
|
bcopy((caddr_t)&pcb->pcb_wbuf[k+1],
|
|
(caddr_t)&pcb->pcb_wbuf[k],
|
|
sizeof (struct rwindow));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* called by assembler files that want to CALL_DEBUG
|
|
*/
|
|
call_debug_from_asm()
|
|
{
|
|
int s;
|
|
|
|
s = splzs(); /* protect against re-entrancy */
|
|
(void) setjmp(&u.u_pcb.pcb_regs); /* for kadb */
|
|
CALL_DEBUG();
|
|
(void) splx(s);
|
|
}
|
|
|
|
/*
|
|
* Send a directed interrupt of specified level to a cpu.
|
|
*/
|
|
|
|
send_dirint(cpuix, int_level)
|
|
int cpuix; /* cpu to be interrupted */
|
|
int int_level; /* interrupt level */
|
|
{
|
|
INT_REGS->cpu[cpuix].set_pend = 1 << (int_level + IR_SOFT_SHIFT);
|
|
}
|
|
|
|
/*
|
|
* Clear a directed interrupt of specified level on a cpu.
|
|
*/
|
|
|
|
clr_dirint(cpuix, int_level)
|
|
int cpuix; /* cpu to be interrupted */
|
|
int int_level; /* interrupt level */
|
|
{
|
|
INT_REGS->cpu[cpuix].clr_pend = 1 << (int_level + IR_SOFT_SHIFT);
|
|
}
|
|
|
|
/*
|
|
* Set Interrupt Target Register to the specified target.
|
|
*/
|
|
|
|
set_itr(cpuix)
|
|
int cpuix; /* cpu to receive the undirected interrupts */
|
|
{
|
|
INT_REGS->itr = cpuix;
|
|
}
|
|
|
|
/*
|
|
* Get Interrupt Target Register target.
|
|
*/
|
|
|
|
get_itr()
|
|
{
|
|
return INT_REGS->itr;
|
|
}
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
|
|
struct {
|
|
int (*fn) ();
|
|
int a[5];
|
|
} xc;
|
|
|
|
int xc_ready = 0; /* true when xc init is done */
|
|
int xc_atnlvl = 0; /* attention nest level */
|
|
int xc_callmap = 0; /* map of cpus at attention */
|
|
|
|
unsigned xc_atnat = 0; /* timestamp of last atn entry */
|
|
|
|
unsigned xc_attns = 0; /* number of attention interrupts */
|
|
unsigned xc_servs = 0; /* number of xc services requested */
|
|
unsigned xc_attnt = 0; /* microseconds inside xc attention */
|
|
|
|
xc_init()
|
|
{
|
|
int cix;
|
|
|
|
for (cix = 0; cix < nmod; ++cix) {
|
|
atom_init(PerCPU[cix].xc_doit);
|
|
atom_clr(PerCPU[cix].xc_doit);
|
|
atom_init(PerCPU[cix].xc_busy);
|
|
atom_clr(PerCPU[cix].xc_busy);
|
|
}
|
|
xc_ready = 1;
|
|
}
|
|
|
|
xc_loop()
|
|
{
|
|
register int (*fn) ();
|
|
register int a0, a1, a2, a3, a4;
|
|
|
|
extern int klock;
|
|
/*
|
|
* We may be here a while.
|
|
* If the ITR points to us, then
|
|
* forward it to the lock holder so
|
|
* he has the option of allowing
|
|
* interrupts to occur.
|
|
*/
|
|
if (get_itr() == cpuid)
|
|
set_itr(klock & 3);
|
|
|
|
/*
|
|
* we are going to be here
|
|
* for a while. swing the ITR
|
|
* over to the master so the
|
|
* system can take interrupts
|
|
* if it wants to.
|
|
*/
|
|
if (get_itr() == cpuid)
|
|
set_itr(klock & 3);
|
|
|
|
while (1) {
|
|
atom_clr(xc_busy);
|
|
atom_wfs(xc_doit);
|
|
fn = xc.fn;
|
|
a0 = xc.a[0];
|
|
a1 = xc.a[1];
|
|
a2 = xc.a[2];
|
|
a3 = xc.a[3];
|
|
a4 = xc.a[4];
|
|
atom_clr(xc_doit);
|
|
if (!fn)
|
|
break;
|
|
(*fn) (a0, a1, a2, a3, a4);
|
|
}
|
|
}
|
|
|
|
xc_serv()
|
|
{
|
|
register int (*fn) ();
|
|
register int a0, a1, a2, a3, a4;
|
|
register int css;
|
|
|
|
if (!xc_ready || !cpu_exists || !atom_setp(xc_doit))
|
|
return;
|
|
|
|
css = cpu_supv;
|
|
cpu_supv = 3;
|
|
|
|
fn = xc.fn;
|
|
a0 = xc.a[0];
|
|
a1 = xc.a[1];
|
|
a2 = xc.a[2];
|
|
a3 = xc.a[3];
|
|
a4 = xc.a[4];
|
|
atom_clr(xc_doit);
|
|
if (fn)
|
|
(*fn) (a0, a1, a2, a3, a4);
|
|
atom_clr(xc_busy);
|
|
cpu_supv = css;
|
|
}
|
|
|
|
xc_callsync()
|
|
{
|
|
register int map, bit;
|
|
register int cix;
|
|
|
|
if (!xc_ready || !cpu_exists)
|
|
return;
|
|
|
|
map = xc_callmap;
|
|
for (cix = 0, bit = 1; cix < nmod; ++cix, bit <<= 1)
|
|
if (map & bit)
|
|
atom_wfc(PerCPU[cix].xc_busy);
|
|
}
|
|
|
|
xc_callstart(o0, o1, o2, o3, o4, func)
|
|
int o0, o1, o2, o3, o4;
|
|
int (*func) ();
|
|
{
|
|
register int s;
|
|
register int map, bit;
|
|
register int cix;
|
|
|
|
if (!xc_ready || !cpu_exists)
|
|
return;
|
|
|
|
s = spl8();
|
|
|
|
if (xc_atnlvl)
|
|
map = xc_callmap;
|
|
else
|
|
xc_callmap = map = procset & ~(1 << cpuid);
|
|
|
|
for (cix = 0, bit = 1; cix < nmod; ++cix, bit <<= 1)
|
|
if (map & bit)
|
|
atom_wfc(PerCPU[cix].xc_busy);
|
|
|
|
xc.a[0] = o0;
|
|
xc.a[1] = o1;
|
|
xc.a[2] = o2;
|
|
xc.a[3] = o3;
|
|
xc.a[4] = o4;
|
|
xc.fn = func;
|
|
|
|
for (cix = 0, bit = 1; cix < nmod; ++cix, bit <<= 1)
|
|
if (map & bit) {
|
|
atom_set(PerCPU[cix].xc_busy);
|
|
atom_set(PerCPU[cix].xc_doit);
|
|
if (xc_atnlvl == 0)
|
|
send_dirint(cix, 15);
|
|
}
|
|
xc_servs++;
|
|
for (cix = 0, bit = 1; cix < nmod; ++cix, bit <<= 1)
|
|
if (map & bit)
|
|
atom_wfc(PerCPU[cix].xc_doit);
|
|
(void) splx(s);
|
|
}
|
|
|
|
xc_attention()
|
|
{
|
|
register int s = spl8();
|
|
|
|
if (xc_atnlvl == 0) {
|
|
xc_attns++;
|
|
xc_atnat = utimers.timer_lsw;
|
|
xc_callstart(0, 0, 0, 0, 0, xc_loop);
|
|
}
|
|
xc_atnlvl++;
|
|
(void) splx(s);
|
|
|
|
}
|
|
|
|
xc_dismissed()
|
|
{
|
|
register int s = spl8();
|
|
|
|
if (xc_atnlvl == 1) {
|
|
xc_callstart(0, 0, 0, 0, 0, (int (*)())0);
|
|
xc_attnt += (utimers.timer_lsw - xc_atnat) >> 10;
|
|
}
|
|
xc_atnlvl--;
|
|
(void) splx(s);
|
|
}
|
|
|
|
xc_noop()
|
|
{
|
|
}
|
|
|
|
int
|
|
xc_sync(o0, o1, o2, o3, o4, func)
|
|
int o0, o1, o2, o3, o4;
|
|
int (*func) ();
|
|
{
|
|
register int rv;
|
|
|
|
if (!func)
|
|
func = xc_noop;
|
|
|
|
if (!xc_ready || !cpu_exists)
|
|
return (*func) (o0, o1, o2, o3, o4);
|
|
|
|
xc_callstart(o0, o1, o2, o3, o4, func);
|
|
rv = (*func) (o0, o1, o2, o3, o4);
|
|
xc_callsync();
|
|
|
|
return (rv);
|
|
}
|
|
|
|
xc_oncpu(o0, o1, o2, o3, cix, func)
|
|
int o0, o1, o2, o3;
|
|
int cix;
|
|
int (*func) ();
|
|
{
|
|
register int s;
|
|
|
|
if (cix == cpuid) {
|
|
(*func) (o0, o1, o2, o3);
|
|
return;
|
|
}
|
|
|
|
if ((cix < 0) || (cix >= NCPU) ||
|
|
!xc_ready || !cpu_exists ||
|
|
!(procset & (1<<cix)) ||
|
|
!PerCPU[cix].cpu_exists)
|
|
return;
|
|
|
|
s = spl8();
|
|
atom_wfc(PerCPU[cix].xc_busy);
|
|
xc.a[0] = o0;
|
|
xc.a[1] = o1;
|
|
xc.a[2] = o2;
|
|
xc.a[3] = o3;
|
|
xc.fn = func;
|
|
atom_set(PerCPU[cix].xc_busy);
|
|
atom_set(PerCPU[cix].xc_doit);
|
|
if (xc_atnlvl == 0)
|
|
send_dirint(cix, 15);
|
|
xc_servs++;
|
|
atom_wfc(PerCPU[cix].xc_busy);
|
|
(void) splx(s);
|
|
}
|
|
#endif MULTIPROCESSOR
|
|
|
|
#ifdef IOMMU
|
|
iom_init()
|
|
{
|
|
union iommu_ctl_reg ioctl_reg;
|
|
union iommu_base_reg iobase_reg;
|
|
|
|
/* load iommu base addr */
|
|
iobase_reg.base_uint = 0;
|
|
iobase_reg.base_reg.base = ((u_int) phys_iopte) >> IOMMU_PTE_BASE_SHIFT;
|
|
iommu_set_base(iobase_reg.base_uint);
|
|
|
|
/* set control reg and turn it on */
|
|
ioctl_reg.ctl_uint = 0;
|
|
ioctl_reg.ctl_reg.enable = 1;
|
|
ioctl_reg.ctl_reg.range = IOMMU_CTL_RANGE;
|
|
iommu_set_ctl(ioctl_reg.ctl_uint);
|
|
|
|
/* kindly flush all tlbs for any leftovers */
|
|
iommu_flush_all();
|
|
}
|
|
|
|
/*
|
|
* NOTE: this routine takes DVMA_ADDR as seen from IOMMU:
|
|
* dvma >= IOMMU_DVMA_BASE && dvma_addr <= 0xFFFFFFFF
|
|
*/
|
|
iom_dvma_pteload(mempfn, dvma_addr, flag)
|
|
int mempfn;
|
|
u_int dvma_addr;
|
|
int flag;
|
|
|
|
{
|
|
union iommu_pte *piopte, tmp_pte;
|
|
|
|
piopte = dvma_pfn_to_iopte(iommu_btop(dvma_addr - IOMMU_DVMA_BASE));
|
|
|
|
/* load iopte */
|
|
tmp_pte.iopte_uint = 0;
|
|
|
|
tmp_pte.iopte.pfn = mempfn;
|
|
tmp_pte.iopte.valid = 1;
|
|
if (flag & IOM_WRITE)
|
|
tmp_pte.iopte.write = 1;
|
|
|
|
if ((cache == CACHE_PAC) || (cache == CACHE_PAC_E) ||
|
|
(vac && (flag & IOM_CACHE)))
|
|
tmp_pte.iopte.cache = 1;
|
|
|
|
*piopte = tmp_pte;
|
|
|
|
}
|
|
|
|
/*
|
|
* NOTE: this routine takes map_addr returned by rmget().
|
|
*/
|
|
iom_pteload(mempfn, map_addr, flag, io_map)
|
|
int mempfn;
|
|
int map_addr;
|
|
int flag;
|
|
struct map *io_map;
|
|
|
|
{
|
|
union iommu_pte *piopte, tmp_pte;
|
|
|
|
if ((piopte = iom_ptefind(map_addr, io_map)) == NULL)
|
|
panic("iom_pteload: bad map addr");
|
|
|
|
/* load iopte */
|
|
tmp_pte.iopte_uint = 0;
|
|
|
|
tmp_pte.iopte.pfn = mempfn;
|
|
tmp_pte.iopte.valid = 1;
|
|
if (flag & IOM_WRITE)
|
|
tmp_pte.iopte.write = 1;
|
|
|
|
if ((cache == CACHE_PAC) || (cache == CACHE_PAC_E) ||
|
|
(vac && (flag & IOM_CACHE)))
|
|
tmp_pte.iopte.cache = 1;
|
|
|
|
*piopte = tmp_pte;
|
|
|
|
}
|
|
|
|
/*
|
|
* NOTE: this routines take DVMA_ADDR as seen from IOMMU:
|
|
* dvma >= IOMMU_DVMA_BASE && dvma_addr <= 0xFFFFFFFF
|
|
*/
|
|
iom_dvma_unload(dvma_addr)
|
|
u_int dvma_addr;
|
|
|
|
{
|
|
union iommu_pte *piopte;
|
|
|
|
piopte = dvma_pfn_to_iopte(iommu_btop(dvma_addr - IOMMU_DVMA_BASE));
|
|
|
|
/* in-line pteunload. invalid iopte */
|
|
piopte-> iopte_uint = 0;
|
|
|
|
/* flush old TLB */
|
|
iommu_addr_flush((int) dvma_addr & IOMMU_FLUSH_MSK);
|
|
|
|
}
|
|
|
|
iom_pteunload(piopte)
|
|
union iommu_pte *piopte;
|
|
|
|
{
|
|
/* invalid iopte */
|
|
piopte-> iopte_uint = 0;
|
|
|
|
/* flush old TLB */
|
|
iommu_addr_flush((iopte_to_dvma(piopte)) & IOMMU_FLUSH_MSK);
|
|
}
|
|
|
|
iom_pagesync(piopte)
|
|
union iommu_pte *piopte;
|
|
|
|
{
|
|
struct page *pp;
|
|
extern u_int get_iommu_entry();
|
|
|
|
/* Don't do in-line non-cached loads. instead call the assembly
|
|
* routine to handle condition. Bugid 1101875.
|
|
*/
|
|
pp = page_numtopp(get_iommu_entry(piopte) >> IOPTE_PFN_SHIFT);
|
|
|
|
if (pp) {
|
|
pp->p_ref = 1;
|
|
if (get_iommu_entry(piopte)& IOPTE_WRITE_BIT)
|
|
pp->p_mod = 1;
|
|
}
|
|
}
|
|
|
|
union iommu_pte*
|
|
iom_ptefind(map_addr, io_map)
|
|
int map_addr;
|
|
struct map *io_map;
|
|
|
|
{
|
|
int dvma_pfn;
|
|
|
|
dvma_pfn = map_addr_to_dvma_pfn(map_addr, io_map);
|
|
return (dvma_pfn_to_iopte(dvma_pfn));
|
|
}
|
|
|
|
/*
|
|
* this routine coverts address returned by rmget() to PFN RELATIVE
|
|
* to the starting of IOMMU_DVMA_BASE.
|
|
* NOTE: the map passed in is used as only a "hint", we
|
|
* look at the map_addr to find out the exact map.
|
|
*/
|
|
map_addr_to_dvma_pfn(map_addr, io_map)
|
|
int map_addr;
|
|
struct map *io_map;
|
|
|
|
{
|
|
|
|
/*
|
|
* map_addr is the address driver required from mb_xxx
|
|
* routines. It is not necessarily right address for IOMMU.
|
|
*/
|
|
|
|
|
|
if (io_map == sbusmap || io_map == bigsbusmap || io_map == mbutlmap) {
|
|
if (map_addr >= SBUSMAP_BASE && map_addr <
|
|
(SBUSMAP_BASE+SBUSMAP_SIZE))
|
|
/* sbusmap */
|
|
return (iommu_btop(IOMMU_SBUS_IOPBMEM_BASE)
|
|
+ map_addr - IOMMU_DVMA_DVFN);
|
|
|
|
if (map_addr >= BIGSBUSMAP_BASE && map_addr <
|
|
(BIGSBUSMAP_BASE+BIGSBUSMAP_SIZE))
|
|
return (map_addr - IOMMU_DVMA_DVFN);
|
|
|
|
if (map_addr >= MBUTLMAP_BASE && map_addr <
|
|
(MBUTLMAP_BASE+MBUTLMAP_SIZE))
|
|
return (map_addr - IOMMU_DVMA_DVFN);
|
|
|
|
/* error */
|
|
panic("map_addr_to_dvma_pfn: bad sbus map_addr");
|
|
/*NOTREACHED*/
|
|
}
|
|
if (cpu == CPU_SUN4M_690) {
|
|
if (io_map == vme24map || io_map == vme32map)
|
|
return (map_addr + IOMMU_IOPBMEM_DVFN
|
|
- IOMMU_DVMA_DVFN);
|
|
} else if (io_map == altsbusmap) {
|
|
if (map_addr >= ALTSBUSMAP_BASE && map_addr <
|
|
(ALTSBUSMAP_BASE+ALTSBUSMAP_SIZE))
|
|
return (map_addr - IOMMU_DVMA_DVFN);
|
|
}
|
|
|
|
if (io_map == sunpcmap) {
|
|
if (map_addr >= IOMMU_SUNPCMEM_BASE && map_addr <
|
|
(IOMMU_SUNPCMEM_BASE+IOMMU_SUNPCMEM_SIZE))
|
|
return (map_addr - IOMMU_DVMA_DVFN);
|
|
}
|
|
|
|
/* error!! */
|
|
panic("map_addr_to_dvma_pfn: unknown map");
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
/*
|
|
* find the corresponding map for a given map_addr.
|
|
* NOTE: the map passed in is as a "hint" only, we need to
|
|
* look at the map_addr to find out the exact map.
|
|
*/
|
|
struct map *
|
|
map_addr_to_map(map_addr, map)
|
|
int map_addr;
|
|
struct map *map;
|
|
|
|
{
|
|
|
|
if (map == sbusmap || map == bigsbusmap || map == mbutlmap) {
|
|
if (map_addr >= SBUSMAP_BASE && map_addr <
|
|
(SBUSMAP_BASE+SBUSMAP_SIZE))
|
|
return (sbusmap);
|
|
|
|
if (map_addr >= BIGSBUSMAP_BASE && map_addr <
|
|
(BIGSBUSMAP_BASE+BIGSBUSMAP_SIZE))
|
|
return (bigsbusmap);
|
|
|
|
if (map_addr >= MBUTLMAP_BASE && map_addr <
|
|
(MBUTLMAP_BASE+MBUTLMAP_SIZE))
|
|
return (mbutlmap);
|
|
|
|
} else { /* VME and alt.sbus maps */
|
|
if (cpu == CPU_SUN4M_690) {
|
|
if (map_addr >= VME24MAP_BASE && map_addr <
|
|
(VME24MAP_BASE+VME24MAP_SIZE))
|
|
return (vme24map);
|
|
|
|
if (map_addr >= VME32MAP_BASE && map_addr <
|
|
(VME32MAP_BASE+VME32MAP_SIZE))
|
|
return (vme32map);
|
|
} else {
|
|
if (map_addr >= ALTSBUSMAP_BASE && map_addr <
|
|
(ALTSBUSMAP_BASE+ALTSBUSMAP_SIZE))
|
|
return (altsbusmap);
|
|
}
|
|
if (map_addr >= IOMMU_SUNPCMEM_BASE && map_addr <
|
|
(IOMMU_SUNPCMEM_BASE+IOMMU_SUNPCMEM_SIZE)) {
|
|
return (sunpcmap);
|
|
}
|
|
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
#endif IOMMU
|
|
|
|
#ifdef IOC
|
|
/*
|
|
* NOTE: this routine is really a kludge now, since it assumes
|
|
* each ioc line maps 2 pages of IOMMU and SRMMU. Should
|
|
* be rewritten in a more general way.
|
|
*/
|
|
|
|
ioc_adj_start(pfn)
|
|
int pfn;
|
|
|
|
{
|
|
if (ioc)
|
|
return ((pfn & 0x1)? pfn - 1 : pfn);
|
|
else
|
|
return (pfn);
|
|
}
|
|
|
|
/*
|
|
* NOTE: pfn passed in should INCLUDE the extra page of red-zone.
|
|
* a kludge routine.
|
|
*/
|
|
ioc_adj_end(pfn)
|
|
int pfn;
|
|
|
|
{
|
|
if (ioc)
|
|
return ((pfn & 0x1)? pfn + 1 : pfn);
|
|
else
|
|
return (pfn);
|
|
}
|
|
|
|
static
|
|
map_addr_to_ioc_sel(map_addr)
|
|
int map_addr;
|
|
|
|
{
|
|
int dvma_addr;
|
|
|
|
dvma_addr = iommu_ptob(map_addr_to_dvma_pfn(map_addr, mb_hd.mh_map));
|
|
return ((dvma_addr & IOC_ADDR_MSK) >> IOC_RW_SHIFT);
|
|
}
|
|
|
|
ioc_setup(map_addr, flags)
|
|
int map_addr;
|
|
int flags;
|
|
|
|
{
|
|
int ioc_tag = 0;
|
|
int ioc_addr;
|
|
|
|
if (ioc) {
|
|
ioc_addr = map_addr_to_ioc_sel(map_addr) | IOC_TAG_PHYS_ADDR;
|
|
|
|
if (flags & IOC_LINE_LDIOC)
|
|
ioc_tag = IOC_LINE_ENABLE;
|
|
|
|
if (flags & IOC_LINE_WRITE)
|
|
ioc_tag |= IOC_LINE_WRITE;
|
|
|
|
do_load_ioc(ioc_addr, ioc_tag);
|
|
}
|
|
|
|
}
|
|
|
|
ioc_flush(map_addr)
|
|
int map_addr;
|
|
|
|
{
|
|
|
|
extern void flush_writebuffers();
|
|
|
|
if (ioc) {
|
|
fast_ioc_flush(map_addr);
|
|
flush_writebuffers(); /* push incoming data to memory */
|
|
}
|
|
}
|
|
|
|
fast_ioc_flush(map_addr)
|
|
int map_addr;
|
|
|
|
{
|
|
|
|
int ioc_addr;
|
|
|
|
if (ioc) {
|
|
ioc_addr = map_addr_to_ioc_sel(map_addr) | IOC_FLUSH_PHYS_ADDR;
|
|
do_flush_ioc(ioc_addr);
|
|
|
|
/*
|
|
* FIXME: This read is to sync. with h/w flush to
|
|
* make sure it's really flushed.
|
|
*/
|
|
(void) ioc_read_tag(map_addr); /* sync with VIC */
|
|
}
|
|
}
|
|
|
|
ioc_read_tag(map_addr)
|
|
int map_addr;
|
|
|
|
{
|
|
int ioc_addr;
|
|
int tv = 0;
|
|
|
|
if (ioc) {
|
|
ioc_addr = map_addr_to_ioc_sel(map_addr) | IOC_TAG_PHYS_ADDR;
|
|
tv = do_read_ioc(ioc_addr);
|
|
}
|
|
return (tv);
|
|
}
|
|
|
|
ioc_init()
|
|
{
|
|
int ioc_addr, i;
|
|
|
|
/* invalidate all ioc lines */
|
|
if (ioc) {
|
|
for (i = 0; i < IOC_CACHE_LINES; i++) {
|
|
ioc_addr = i * IOC_LINE_MAP;
|
|
ioc_addr &= IOC_ADDR_MSK;
|
|
ioc_addr >>= IOC_RW_SHIFT;
|
|
ioc_addr |= IOC_TAG_PHYS_ADDR;
|
|
do_load_ioc(ioc_addr, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Turn on the io cache during startup.
|
|
*/
|
|
|
|
iocache_on()
|
|
{
|
|
(*VMEBUS_ICR) |= VMEICP_IOC;
|
|
}
|
|
|
|
/*
|
|
* check to see if this transaction can be IOC'ed.
|
|
* NOTE: this is the new scheme used in 4.1.1. B_IOCACHE is now obsolete.
|
|
*
|
|
* returns: IOC_LINE_INVALID: sbus/IOC is not on.
|
|
* IOC_LINE_LDIOC: vme & IOCable
|
|
* IOC_LINE_NOIOC: vme & NOT IOCable
|
|
*/
|
|
ioc_able(bp, io_map)
|
|
struct buf *bp;
|
|
struct map *io_map;
|
|
|
|
{
|
|
/* if ioc not present or disabled, don't think about it. */
|
|
if (!ioc) {
|
|
return (IOC_LINE_INVALID);
|
|
}
|
|
/* if not from vme24map or vme32map, ignore it. */
|
|
if ((io_map != vme24map) && (io_map != vme32map)) {
|
|
return (IOC_LINE_INVALID);
|
|
}
|
|
|
|
/* B_WRITE does not require ioc_flush, always cache!! */
|
|
if (!(bp->b_flags & B_READ)) {
|
|
return (IOC_LINE_LDIOC);
|
|
}
|
|
|
|
/* if address badly aligned, dont cache it. */
|
|
if ((u_int)bp->b_un.b_addr & IOC_LINEMASK) {
|
|
return (IOC_LINE_NOIOC);
|
|
}
|
|
|
|
/* if count badly aligned, dont cache it. */
|
|
if (bp->b_bcount & IOC_LINEMASK) {
|
|
return (IOC_LINE_NOIOC);
|
|
}
|
|
|
|
/* properly aligned read: iocache it. */
|
|
return (IOC_LINE_LDIOC);
|
|
}
|
|
|
|
#endif IOC
|
|
|
|
avail_at_vaddr(vaddr)
|
|
u_int vaddr;
|
|
{
|
|
struct memlist *pmemptr;
|
|
int rv = 0;
|
|
|
|
for (pmemptr = virtmemory; pmemptr; pmemptr = pmemptr->next)
|
|
if ((vaddr >= pmemptr-> address) &&
|
|
((vaddr - pmemptr-> address) < pmemptr-> size)) {
|
|
rv = pmemptr->size - (vaddr - pmemptr->address);
|
|
break;
|
|
}
|
|
return (rv);
|
|
}
|
|
|
|
int
|
|
calc_memsize()
|
|
{
|
|
struct memlist *memp;
|
|
register int found;
|
|
|
|
for (found = 0, memp = availmemory; memp; memp = memp->next)
|
|
found += memp->size;
|
|
return (found);
|
|
}
|
|
|
|
#define OLDDELAY(n) { register int N = ((unsigned)((n) << 4) >> cpudelay); \
|
|
while (--N > 0); }
|
|
|
|
/*
|
|
* set delay constant for usec_delay()
|
|
* NOTE: we use the fact that the per-
|
|
* processor clocks are available and
|
|
* mapped properly at "utimers".
|
|
*/
|
|
setcpudelay()
|
|
{
|
|
extern struct count14 utimers;
|
|
unsigned r; /* timer resolution, ~ns */
|
|
unsigned e; /* delay time, us */
|
|
unsigned es; /* delay time, ~ns */
|
|
unsigned t, f; /* for time measurement */
|
|
int s; /* saved PSR for inhibiting ints */
|
|
|
|
r = 512; /* worst supported timer resolution, ~ns */
|
|
es = r * 100; /* target delay in ~ns */
|
|
e = ((es + 1023) >> 10); /* request delay in us, round up */
|
|
es = e << 10; /* adjusted target delay in ~ns */
|
|
Cpudelay = 1; /* initial guess */
|
|
DELAY(1); /* first time may be weird */
|
|
do {
|
|
Cpudelay <<= 1; /* double until big enough */
|
|
do {
|
|
s = spl8();
|
|
t = utimers.timer_lsw;
|
|
DELAY(e);
|
|
f = utimers.timer_lsw;
|
|
(void) splx(s);
|
|
} while (f < t);
|
|
t = f - t;
|
|
} while (t < es);
|
|
Cpudelay = (Cpudelay * es + t) / t;
|
|
if (Cpudelay < 0)
|
|
Cpudelay = 0;
|
|
|
|
do {
|
|
s = spl8();
|
|
t = utimers.timer_lsw;
|
|
DELAY(e);
|
|
f = utimers.timer_lsw;
|
|
(void) splx(s);
|
|
} while (f < t);
|
|
t = f - t;
|
|
|
|
/*
|
|
* set up old delay stuff, so grotty old binary drivers compiled
|
|
* under SunOS 4.0 still have half a chance of working.
|
|
*/
|
|
cpudelay = 11;
|
|
do {
|
|
cpudelay--;
|
|
do {
|
|
s = spl8();
|
|
t = utimers.timer_lsw;
|
|
OLDDELAY(e);
|
|
f = utimers.timer_lsw;
|
|
(void) splx(s);
|
|
} while (f < t);
|
|
t = f - t;
|
|
} while ((t < es) && (cpudelay > 0));
|
|
}
|
|
|
|
cpanic(src, line, str, exp, act)
|
|
char *src;
|
|
int line;
|
|
char *str;
|
|
int exp;
|
|
int act;
|
|
{
|
|
#ifdef prom_eprintf
|
|
prom_eprintf("%s, line %d: %s; exp=%x, act=%x\n",
|
|
src, line, str, exp, act);
|
|
#endif prom_eprintf
|
|
panic(str);
|
|
}
|
|
|
|
char *cpuidmsgs[] = {
|
|
"cpuid all ok",
|
|
"cpuid vac err",
|
|
"cpuid tlb err",
|
|
"cpuid tbr err",
|
|
"cpuid mid err",
|
|
};
|
|
|
|
ccheck(src, line)
|
|
char *src;
|
|
int line;
|
|
{
|
|
int code;
|
|
int exp;
|
|
int act;
|
|
|
|
code = check_cpuid(&exp, &act);
|
|
if (code)
|
|
cpanic(src, line, cpuidmsgs[code], exp, act);
|
|
}
|
|
|
|
/*
|
|
* Setup system implementation dependent function vectors
|
|
*/
|
|
void
|
|
system_setup(systype)
|
|
int systype;
|
|
{
|
|
switch (systype) {
|
|
#ifdef SUN4M_690
|
|
case CPU_SUN4M_690:
|
|
p4m690_sys_setfunc();
|
|
break;
|
|
#endif
|
|
#ifdef SUN4M_50
|
|
case CPU_SUN4M_50:
|
|
p4m50_sys_setfunc();
|
|
break;
|
|
#endif
|
|
#ifdef SUN4M_35
|
|
case CPU_SUN4M_35:
|
|
msparc_sys_setfunc();
|
|
break;
|
|
#endif
|
|
default:
|
|
/* assume it conforms to "generic" sun4m */
|
|
break;
|
|
}
|
|
init_all_fsr();
|
|
}
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
|
|
/*
|
|
* topproc:
|
|
* return a pointer to the proc
|
|
* at the front of the run queue,
|
|
* or NULL if there is none.
|
|
*/
|
|
struct proc *
|
|
topproc()
|
|
{
|
|
register unsigned wqs;
|
|
register int ix;
|
|
|
|
wqs = whichqs;
|
|
/*
|
|
* If no processes are waiting on the
|
|
* run queue, we are all done.
|
|
*/
|
|
if (wqs == 0)
|
|
return (struct proc *)0;
|
|
/*
|
|
* Locate the top process in the run queue.
|
|
*/
|
|
ix = 0;
|
|
while ((wqs&1)==0) {
|
|
wqs >>= 1;
|
|
ix ++;
|
|
}
|
|
return qs[ix].ph_link;
|
|
}
|
|
|
|
/*
|
|
* bestcpu:
|
|
* return the "best" cpu to
|
|
* run this process, or -1
|
|
* if nobody should run it.
|
|
*/
|
|
int
|
|
bestcpu(p)
|
|
struct proc *p;
|
|
{
|
|
register unsigned pam;
|
|
static int prevcpu = 0;
|
|
extern int procset;
|
|
int cpmax, cpwho, ix, who;
|
|
unsigned bit;
|
|
|
|
/*
|
|
* The null process should not be staged.
|
|
*/
|
|
if (p == (struct proc *)0)
|
|
return -1;
|
|
|
|
pam = p->p_pam & procset;
|
|
|
|
/*
|
|
* If it can't run anywhere,
|
|
* don't run it anywhere.
|
|
*/
|
|
if (pam == 0)
|
|
return -1;
|
|
|
|
/*
|
|
* Scan the CPUs that can
|
|
* run this process, to find
|
|
* the cpu with highest "curpri".
|
|
* Scan starting with the CPU
|
|
* following the one most recently
|
|
* returned, so ties get spread
|
|
* around the list.
|
|
*
|
|
* Seed the "max" value and index
|
|
* with the processor that most
|
|
* recently ran this process to
|
|
* give it some favor.
|
|
*
|
|
* If someone that can run this
|
|
* process is idle, we are done.
|
|
*/
|
|
cpwho = p->p_cpuid & 0xFF;
|
|
cpmax = PerCPU[cpwho].curpri;
|
|
who = prevcpu;
|
|
for (ix=0; ix<NCPU; ++ix) {
|
|
if (++who >= NCPU)
|
|
who = 0;
|
|
bit = 1<<who;
|
|
if (pam & bit) {
|
|
if (PerCPU[who].cpu_idle)
|
|
return who + NCPU;
|
|
if (PerCPU[who].curpri > cpmax) {
|
|
cpmax = PerCPU[who].curpri;
|
|
cpwho = who;
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* If we did not find a CPU that
|
|
* would want to run this process
|
|
* over what it is now doing,
|
|
* don't run it anywhere.
|
|
*/
|
|
if (cpmax < p->p_pri)
|
|
return -1;
|
|
/*
|
|
* cpwho is the index of the "best" processor
|
|
* to run the process on the front of the queue.
|
|
* save it for next time, too.
|
|
*/
|
|
prevcpu = cpwho;
|
|
return cpwho;
|
|
}
|
|
|
|
#ifdef OPENPROMS
|
|
/*
|
|
* un-hide the promlib services we are intercepting
|
|
*/
|
|
#undef prom_exit_to_mon
|
|
#undef prom_enter_mon
|
|
#undef prom_printf
|
|
#undef prom_eprintf
|
|
#undef prom_writestr
|
|
#undef prom_putchar
|
|
#undef prom_mayput
|
|
|
|
extern void prom_exit_to_mon();
|
|
extern void prom_enter_mon();
|
|
extern int prom_printf();
|
|
extern void prom_writestr();
|
|
extern void prom_putchar();
|
|
extern int prom_mayput();
|
|
|
|
unsigned inprom = 0;
|
|
|
|
mpprom_enter_slave()
|
|
{
|
|
extern label_t panic_label;
|
|
flush_windows();
|
|
(void) setjmp(&panic_label);
|
|
|
|
if (klock_knock()) {
|
|
inprom = 1;
|
|
prom_enter_mon();
|
|
} else {
|
|
(void) prom_idlecpu(0);
|
|
}
|
|
|
|
if (swapl(0, &inprom)) { /* first one out resumes the others */
|
|
extern unsigned snid[NCPU];
|
|
register unsigned nid;
|
|
register int ix;
|
|
|
|
for (ix = 0; ix < nmod; ++ix)
|
|
if (ix != cpuid)
|
|
if (nid = snid[ix])
|
|
(void) prom_resumecpu(nid);
|
|
}
|
|
}
|
|
|
|
mpprom_enter()
|
|
{
|
|
(void) xc_sync(0, 0, 0, 0, 0, mpprom_enter_slave);
|
|
}
|
|
|
|
mpprom_exit_slave()
|
|
{
|
|
if (klock_knock())
|
|
prom_exit_to_mon();
|
|
else
|
|
(void) prom_stopcpu(0);
|
|
}
|
|
|
|
mpprom_exit()
|
|
{
|
|
(void) xc_sync(0, 0, 0, 0, 0, mpprom_exit_slave);
|
|
}
|
|
|
|
/* VARARGS1 */
|
|
mpprom_printf(fmt, a, b, c, d, e)
|
|
char *fmt;
|
|
{
|
|
#ifdef MULTIPROCESSOR
|
|
klock_require(__FILE__, __LINE__, "mpprom_printf");
|
|
xc_attention();
|
|
#endif MULTIPROCESSOR
|
|
prom_printf(fmt, a, b, c, d, e);
|
|
#ifdef MULTIPROCESSOR
|
|
xc_dismissed();
|
|
#endif MULTIPROCESSOR
|
|
}
|
|
|
|
/* VARARGS1 */
|
|
mpprom_eprintf(fmt, a, b, c, d, e)
|
|
char *fmt;
|
|
{
|
|
#ifdef MULTIPROCESSOR
|
|
klock_require(__FILE__, __LINE__, "mpprom_printf");
|
|
xc_attention();
|
|
#endif MULTIPROCESSOR
|
|
if (fmt) prom_printf(fmt, a, b, c, d, e);
|
|
#ifdef PROM_PARANOIA
|
|
prom_printf("stack may be at %x or %x\n",
|
|
framepointer(), stackpointer());
|
|
prom_enter_mon();
|
|
#endif PROM_PARANOIA
|
|
#ifdef MULTIPROCESSOR
|
|
xc_dismissed();
|
|
#endif MULTIPROCESSOR
|
|
}
|
|
|
|
mpprom_write_nl7(buf, size)
|
|
char *buf;
|
|
int size;
|
|
{
|
|
extern void prom_putchar();
|
|
int c;
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
klock_require(__FILE__, __LINE__, "mpprom_printf");
|
|
xc_attention();
|
|
#endif MULTIPROCESSOR
|
|
while (size-->0)
|
|
if (c = 0x7F & *buf++) {
|
|
if (c == '\n')
|
|
prom_putchar('\r');
|
|
prom_putchar(c);
|
|
}
|
|
#ifdef MULTIPROCESSOR
|
|
xc_dismissed();
|
|
#endif MULTIPROCESSOR
|
|
}
|
|
|
|
mpprom_write(buf, size)
|
|
char *buf;
|
|
u_int size;
|
|
{
|
|
extern void prom_putchar();
|
|
|
|
#ifdef MULTIPROCESSOR
|
|
klock_require(__FILE__, __LINE__, "mpprom_printf");
|
|
xc_attention();
|
|
#endif MULTIPROCESSOR
|
|
prom_writestr(buf, size);
|
|
#ifdef MULTIPROCESSOR
|
|
xc_dismissed();
|
|
#endif MULTIPROCESSOR
|
|
}
|
|
|
|
mpprom_putchar(c)
|
|
{
|
|
#ifdef MULTIPROCESSOR
|
|
klock_require(__FILE__, __LINE__, "mpprom_printf");
|
|
xc_attention();
|
|
#endif MULTIPROCESSOR
|
|
prom_putchar(c);
|
|
#ifdef MULTIPROCESSOR
|
|
xc_dismissed();
|
|
#endif MULTIPROCESSOR
|
|
}
|
|
|
|
mpprom_mayput(c)
|
|
{
|
|
register int rv;
|
|
#ifdef MULTIPROCESSOR
|
|
klock_require(__FILE__, __LINE__, "mpprom_printf");
|
|
xc_attention();
|
|
#endif MULTIPROCESSOR
|
|
rv = prom_mayput(c);
|
|
#ifdef MULTIPROCESSOR
|
|
xc_dismissed();
|
|
#endif MULTIPROCESSOR
|
|
return (rv);
|
|
}
|
|
#endif OPENPROMS
|
|
|
|
/*
|
|
* klock_reqfail: report klock requirement failure
|
|
*/
|
|
klock_reqfail(fn, ln, where, act, exp, mid)
|
|
char *fn;
|
|
int ln;
|
|
char *where;
|
|
int act;
|
|
int exp;
|
|
int mid;
|
|
{
|
|
trigger_logan();
|
|
if (!fn) fn = "";
|
|
if (!where) where = "";
|
|
/*
|
|
* this is really ugly, but
|
|
* what choice do we have?
|
|
* we can't call mpprom_printf, as
|
|
* it calls klock_require, which will
|
|
* just branch right back here.
|
|
*/
|
|
halt_and_catch_fire();
|
|
|
|
prom_printf("%s(%d): klock not held%s\n",
|
|
fn, ln, where);
|
|
prom_printf("\tmid=%x exp=%x act=%x\n", mid, exp, act);
|
|
prom_printf("\tstack is at %x or %x\n",
|
|
stackpointer(), framepointer());
|
|
prom_enter_mon();
|
|
}
|
|
#endif MULTIPROCESSOR
|
|
|
|
/*
|
|
* unmap this region at L2 ptes starting at addr to end of the region
|
|
* Assumes kl1pt as the pointer to level 1 table
|
|
* Used at startup by the kernel to cleanup unused maps.
|
|
*/
|
|
static
|
|
unmap_to_end_rgn(addr)
|
|
u_int addr;
|
|
{
|
|
register union ptpe *lptp;
|
|
register union ptpe *sptp;
|
|
union ptpe a_ptpe;
|
|
u_int endaddr;
|
|
|
|
lptp = &a_ptpe;
|
|
|
|
lptp->ptpe_int =
|
|
*(u_int *)((addr_t)kl1pt + getl1in(addr) * sizeof (struct ptp));
|
|
|
|
if (lptp->ptp.EntryType == MMU_ET_INVALID) {
|
|
/* nothing to do */
|
|
return;
|
|
}
|
|
|
|
endaddr = (((u_int)addr + (L2PTSIZE - L3PTSIZE)) & ~ (L2PTSIZE-1));
|
|
|
|
for (; addr < endaddr; addr += L3PTSIZE) {
|
|
/* Get the level 2 entry and make it invalid */
|
|
|
|
sptp = &(((struct l2pt *)ptptopte(lptp->ptp.PageTablePointer))->ptpe[getl2in(addr)]);
|
|
sptp->ptpe_int = 0;
|
|
}
|
|
}
|
|
|