/* $NetBSD: machdep.c,v 1.218 1996/12/09 22:51:55 fvdl Exp $ */ /*- * Copyright (c) 1993, 1994, 1995, 1996 Charles M. Hannum. All rights reserved. * Copyright (c) 1996 Jason R. Thorpe. All rights reserved. * Copyright (c) 1992 Terrence R. Lambert. * Copyright (c) 1982, 1987, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)machdep.c 7.4 (Berkeley) 6/3/91 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef SYSVMSG #include #endif #ifdef SYSVSEM #include #endif #ifdef SYSVSHM #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DDB #include #include #include #include #endif #ifdef VM86 #include #endif #include "apm.h" #if NAPM > 0 #include #endif #include "isa.h" #include "npx.h" #if NNPX > 0 extern struct proc *npxproc; #endif /* the following is used externally (sysctl_hw) */ char machine[] = "i386"; /* cpu "architecture" */ /* * Declare these as initialized data so we can patch them. */ int nswbuf = 0; #ifdef NBUF int nbuf = NBUF; #else int nbuf = 0; #endif #ifdef BUFPAGES int bufpages = BUFPAGES; #else int bufpages = 0; #endif int physmem; int dumpmem_low; int dumpmem_high; int boothowto; int cpu_class; struct msgbuf *msgbufp; int msgbufmapped; vm_map_t buffer_map; extern int biosbasemem, biosextmem; extern vm_offset_t avail_start, avail_end; static vm_offset_t hole_start, hole_end; static vm_offset_t avail_next; /* * Extent maps to manage I/O and ISA memory hole space. Allocate * storage for 8 regions in each, initially. Later, ioport_malloc_safe * will indicate that it's safe to use malloc() to dynamically allocate * region descriptors. * * N.B. At least two regions are _always_ allocated from the iomem * extent map; (0 -> ISA hole) and (end of ISA hole -> end of RAM). * * The extent maps are not static! Machine-dependent ISA and EISA * routines need access to them for bus address space allocation. */ static long ioport_ex_storage[EXTENT_FIXED_STORAGE_SIZE(8) / sizeof(long)]; static long iomem_ex_storage[EXTENT_FIXED_STORAGE_SIZE(8) / sizeof(long)]; struct extent *ioport_ex; struct extent *iomem_ex; static ioport_malloc_safe; caddr_t allocsys __P((caddr_t)); void dumpsys __P((void)); void identifycpu __P((void)); void init386 __P((vm_offset_t)); void consinit __P((void)); #ifdef COMPAT_NOMID static int exec_nomid __P((struct proc *, struct exec_package *)); #endif int bus_mem_add_mapping __P((bus_addr_t, bus_size_t, int, bus_space_handle_t *)); /* * Machine-dependent startup code */ void cpu_startup() { unsigned i; caddr_t v; int sz; int base, residual; vm_offset_t minaddr, maxaddr; vm_size_t size; struct pcb *pcb; int x; #if NAPM > 0 extern int biostramp_image_size; extern u_char biostramp_image[]; #endif /* * Initialize error message buffer (at end of core). */ /* avail_end was pre-decremented in pmap_bootstrap to compensate */ for (i = 0; i < btoc(sizeof(struct msgbuf)); i++) pmap_enter(pmap_kernel(), (vm_offset_t)((caddr_t)msgbufp + i * NBPG), avail_end + i * NBPG, VM_PROT_ALL, TRUE); msgbufmapped = 1; printf(version); identifycpu(); printf("real mem = %d\n", ctob(physmem)); /* * Find out how much space we need, allocate it, * and then give everything true virtual addresses. */ sz = (int)allocsys((caddr_t)0); if ((v = (caddr_t)kmem_alloc(kernel_map, round_page(sz))) == 0) panic("startup: no room for tables"); if (allocsys(v) - v != sz) panic("startup: table size inconsistency"); /* * Now allocate buffers proper. They are different than the above * in that they usually occupy more virtual memory than physical. */ size = MAXBSIZE * nbuf; buffer_map = kmem_suballoc(kernel_map, (vm_offset_t *)&buffers, &maxaddr, size, TRUE); minaddr = (vm_offset_t)buffers; if (vm_map_find(buffer_map, vm_object_allocate(size), (vm_offset_t)0, &minaddr, size, FALSE) != KERN_SUCCESS) panic("startup: cannot allocate buffers"); if ((bufpages / nbuf) >= btoc(MAXBSIZE)) { /* don't want to alloc more physical mem than needed */ bufpages = btoc(MAXBSIZE) * nbuf; } base = bufpages / nbuf; residual = bufpages % nbuf; for (i = 0; i < nbuf; i++) { vm_size_t curbufsize; vm_offset_t curbuf; /* * First buffers get (base+1) physical pages * allocated for them. The rest get (base) physical pages. * * The rest of each buffer occupies virtual space, * but has no physical memory allocated for it. */ curbuf = (vm_offset_t)buffers + i * MAXBSIZE; curbufsize = CLBYTES * (i < residual ? base+1 : base); vm_map_pageable(buffer_map, curbuf, curbuf+curbufsize, FALSE); vm_map_simplify(buffer_map, curbuf); } /* * Allocate a submap for exec arguments. This map effectively * limits the number of processes exec'ing at any time. */ exec_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, 16*NCARGS, TRUE); /* * Allocate a submap for physio */ phys_map = kmem_suballoc(kernel_map, &minaddr, &maxaddr, VM_PHYS_SIZE, TRUE); /* * Finally, allocate mbuf pool. Since mclrefcnt is an off-size * we use the more space efficient malloc in place of kmem_alloc. */ mclrefcnt = (char *)malloc(NMBCLUSTERS+CLBYTES/MCLBYTES, M_MBUF, M_NOWAIT); bzero(mclrefcnt, NMBCLUSTERS+CLBYTES/MCLBYTES); mb_map = kmem_suballoc(kernel_map, (vm_offset_t *)&mbutl, &maxaddr, VM_MBUF_SIZE, FALSE); /* * Initialize callouts */ callfree = callout; for (i = 1; i < ncallout; i++) callout[i-1].c_next = &callout[i]; printf("avail mem = %ld\n", ptoa(cnt.v_free_count)); printf("using %d buffers containing %d bytes of memory\n", nbuf, bufpages * CLBYTES); /* * Set up buffers, so they can be used to read disk labels. */ bufinit(); #if NAPM > 0 /* * this should be caught at kernel build time, but put it here * in case someone tries to fake it out... */ #ifdef DIAGNOSTIC if (biostramp_image_size > NBPG) panic("biostramp_image_size too big: %x vs. %x\n", biostramp_image_size, NBPG); #endif pmap_enter(pmap_kernel(), (vm_offset_t)APM_BIOSTRAMP, /* virtual */ (vm_offset_t)APM_BIOSTRAMP, /* physical */ VM_PROT_ALL, /* protection */ TRUE); /* wired down */ bcopy(biostramp_image, (caddr_t)APM_BIOSTRAMP, biostramp_image_size); #ifdef DEBUG printf("biostramp installed @ %x\n", APM_BIOSTRAMP); #endif #endif /* * Configure the system. */ ioport_malloc_safe = 1; configure(); /* * Set up proc0's TSS and LDT. */ curpcb = pcb = &proc0.p_addr->u_pcb; pcb->pcb_flags = 0; pcb->pcb_tss.tss_ioopt = ((caddr_t)pcb->pcb_iomap - (caddr_t)&pcb->pcb_tss) << 16; for (x = 0; x < sizeof(pcb->pcb_iomap) / 4; x++) pcb->pcb_iomap[x] = 0xffffffff; pcb->pcb_ldt_sel = GSEL(GLDT_SEL, SEL_KPL); pcb->pcb_cr0 = rcr0(); pcb->pcb_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL); pcb->pcb_tss.tss_esp0 = (int)proc0.p_addr + USPACE - 16; tss_alloc(pcb); ltr(pcb->pcb_tss_sel); lldt(pcb->pcb_ldt_sel); proc0.p_md.md_regs = (struct trapframe *)pcb->pcb_tss.tss_esp0 - 1; } /* * Allocate space for system data structures. We are given * a starting virtual address and we return a final virtual * address; along the way we set each data structure pointer. * * We call allocsys() with 0 to find out how much space we want, * allocate that much and fill it with zeroes, and then call * allocsys() again with the correct base virtual address. */ caddr_t allocsys(v) register caddr_t v; { #define valloc(name, type, num) \ v = (caddr_t)(((name) = (type *)v) + (num)) #ifdef REAL_CLISTS valloc(cfree, struct cblock, nclist); #endif valloc(callout, struct callout, ncallout); valloc(swapmap, struct map, nswapmap = maxproc * 2); #ifdef SYSVSHM valloc(shmsegs, struct shmid_ds, shminfo.shmmni); #endif #ifdef SYSVSEM valloc(sema, struct semid_ds, seminfo.semmni); valloc(sem, struct sem, seminfo.semmns); /* This is pretty disgusting! */ valloc(semu, int, (seminfo.semmnu * seminfo.semusz) / sizeof(int)); #endif #ifdef SYSVMSG valloc(msgpool, char, msginfo.msgmax); valloc(msgmaps, struct msgmap, msginfo.msgseg); valloc(msghdrs, struct msg, msginfo.msgtql); valloc(msqids, struct msqid_ds, msginfo.msgmni); #endif /* * Determine how many buffers to allocate. We use 10% of the * first 2MB of memory, and 5% of the rest, with a minimum of 16 * buffers. We allocate 1/2 as many swap buffer headers as file * i/o buffers. */ if (bufpages == 0) if (physmem < btoc(2 * 1024 * 1024)) bufpages = physmem / (10 * CLSIZE); else bufpages = (btoc(2 * 1024 * 1024) + physmem) / (20 * CLSIZE); if (nbuf == 0) { nbuf = bufpages; if (nbuf < 16) nbuf = 16; } /* * XXX stopgap measure to prevent wasting too much KVM on * the sparsely filled buffer cache. */ if (nbuf * MAXBSIZE > VM_MAX_KERNEL_BUF) nbuf = VM_MAX_KERNEL_BUF / MAXBSIZE; if (nswbuf == 0) { nswbuf = (nbuf / 2) &~ 1; /* force even */ if (nswbuf > 256) nswbuf = 256; /* sanity */ } valloc(swbuf, struct buf, nswbuf); valloc(buf, struct buf, nbuf); return v; } /* * Info for CTL_HW */ char cpu_model[120]; extern char version[]; /* * Note: these are just the ones that may not have a cpuid instruction. * We deal with the rest in a different way. */ struct cpu_nocpuid_nameclass i386_nocpuid_cpus[] = { { CPUVENDOR_INTEL, "Intel", "386SX", CPUCLASS_386 }, /* CPU_386SX */ { CPUVENDOR_INTEL, "Intel", "386DX", CPUCLASS_386 }, /* CPU_386 */ { CPUVENDOR_INTEL, "Intel", "486SX", CPUCLASS_486 }, /* CPU_486SX */ { CPUVENDOR_INTEL, "Intel", "486DX", CPUCLASS_486 }, /* CPU_486 */ { CPUVENDOR_CYRIX, "Cyrix", "486DLC", CPUCLASS_486 }, /* CPU_486DLC */ { CPUVENDOR_NEXGEN,"NexGen","586", CPUCLASS_386 }, /* CPU_NX586 */ }; const char *classnames[] = { "386", "486", "586", "686" }; const char *modifiers[] = { "", "OverDrive ", "Dual ", "" }; struct cpu_cpuid_nameclass i386_cpuid_cpus[] = { { "GenuineIntel", CPUVENDOR_INTEL, "Intel", /* Family 4 */ { { CPUCLASS_486, { "486DX", "486DX", "486DX", "486DX2", "486SL", "486SX2", 0, "486DX2 W/B Enhanced", "486DX4", 0, 0, 0, 0, 0, 0, 0, "486" /* Default */ } }, /* Family 5 */ { CPUCLASS_586, { 0, "Pentium", "Pentium (P54C)", "Pentium (P24T)", "Pentium", "Pentium", 0, "Pentium (P54C)", 0, 0, 0, 0, 0, 0, 0, 0, "Pentium" /* Default */ } }, /* Family 6 */ { CPUCLASS_686, { 0, "Pentium Pro", 0, 0, "Pentium Pro", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Pentium Pro" /* Default */ } } } }, { "AuthenticAMD", CPUVENDOR_AMD, "AMD", /* Family 4 */ { { CPUCLASS_486, { 0, 0, 0, "Am486DX2 W/T", 0, 0, 0, "Am486DX2 W/B", "Am486DX4 W/T or Am5x86 W/T 150", "Am486DX4 W/B or Am5x86 W/B 150", 0, 0, 0, 0, "Am5x86 W/T 133/160", "Am5x86 W/B 133/160", "Am486 or Am5x86" /* Default */ }, }, /* Family 5 */ { CPUCLASS_586, { "K5", "K5", 0, 0, 0, 0, "K6", 0, 0, 0, 0, 0, 0, 0, 0, 0, "K5 or K6", /* Default */ }, }, /* Family 6, not yet available from AMD */ { CPUCLASS_686, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Pentium Pro compatible" /* Default */ }, } } }, { "CyrixInstead", CPUVENDOR_CYRIX, "Cyrix", /* Family 4 */ { { CPUCLASS_486, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "486" /* Default */ }, }, /* Family 5 */ { CPUCLASS_586, { 0, 0, "6x86", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "6x86" /* Default */ } }, /* Family 6, not yet available from Cyrix */ { CPUCLASS_686, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Pentium Pro compatible" /* Default */ } } } } }; #define CPUDEBUG void identifycpu() { extern char cpu_vendor[]; extern int cpu_id; const char *name, *modifier, *vendorname; int class = CPUCLASS_386, vendor, i, max; int family, model, step, modif; struct cpu_cpuid_nameclass *cpup = NULL; if (cpuid_level == -1) { #ifdef DIAGNOSTIC if (cpu < 0 || cpu >= (sizeof i386_nocpuid_cpus/sizeof(struct cpu_nocpuid_nameclass))) panic("unknown cpu type %d\n", cpu); #endif name = i386_nocpuid_cpus[cpu].cpu_name; vendor = i386_nocpuid_cpus[cpu].cpu_vendor; vendorname = i386_nocpuid_cpus[cpu].cpu_vendorname; class = i386_nocpuid_cpus[cpu].cpu_class; modifier = ""; } else { max = sizeof (i386_cpuid_cpus) / sizeof (i386_cpuid_cpus[0]); modif = (cpu_id >> 12) & 3; family = (cpu_id >> 8) & 15; if (family < CPU_MINFAMILY) panic("identifycpu: strange family value"); model = (cpu_id >> 4) & 15; step = cpu_id & 15; #ifdef CPUDEBUG printf("cpu0: family %x model %x step %x\n", family, model, step); #endif for (i = 0; i < max; i++) { if (!strncmp(cpu_vendor, i386_cpuid_cpus[i].cpu_id, 12)) { cpup = &i386_cpuid_cpus[i]; break; } } if (cpup == NULL) { vendor = CPUVENDOR_UNKNOWN; if (cpu_vendor[0] != '\0') vendorname = &cpu_vendor[0]; else vendorname = "Unknown"; if (family > CPU_MAXFAMILY) family = CPU_MAXFAMILY; class = family - 3; modifier = ""; name = ""; } else { vendor = cpup->cpu_vendor; vendorname = cpup->cpu_vendorname; modifier = modifiers[modif]; if (family > CPU_MAXFAMILY) { family = CPU_MAXFAMILY; model = CPU_DEFMODEL; } else if (model > CPU_MAXMODEL) model = CPU_DEFMODEL; i = family - CPU_MINFAMILY; name = cpup->cpu_family[i].cpu_models[model]; if (name == NULL) name = cpup->cpu_family[i].cpu_models[CPU_DEFMODEL]; class = cpup->cpu_family[i].cpu_class; } } sprintf(cpu_model, "%s %s%s (%s-class)", vendorname, modifier, name, classnames[class]); printf("cpu0: %s\n", cpu_model); cpu_class = class; /* * Now that we have told the user what they have, * let them know if that machine type isn't configured. */ switch (cpu_class) { #if !defined(I386_CPU) && !defined(I486_CPU) && !defined(I586_CPU) && !defined(I686_CPU) #error No CPU classes configured. #endif #ifndef I686_CPU case CPUCLASS_686: printf("NOTICE: this kernel does not support Pentium Pro CPU class\n"); #ifdef I586_CPU printf("NOTICE: lowering CPU class to i586\n"); cpu_class = CPUCLASS_586; break; #endif #endif #ifndef I586_CPU case CPUCLASS_586: printf("NOTICE: this kernel does not support Pentium CPU class\n"); #ifdef I486_CPU printf("NOTICE: lowering CPU class to i486\n"); cpu_class = CPUCLASS_486; break; #endif #endif #ifndef I486_CPU case CPUCLASS_486: printf("NOTICE: this kernel does not support i486 CPU class\n"); #ifdef I386_CPU printf("NOTICE: lowering CPU class to i386\n"); cpu_class = CPUCLASS_386; break; #endif #endif #ifndef I386_CPU case CPUCLASS_386: printf("NOTICE: this kernel does not support i386 CPU class\n"); panic("no appropriate CPU class available"); #endif default: break; } if (cpu == CPU_486DLC) { #ifndef CYRIX_CACHE_WORKS printf("WARNING: CYRIX 486DLC CACHE UNCHANGED.\n"); #else #ifndef CYRIX_CACHE_REALLY_WORKS printf("WARNING: CYRIX 486DLC CACHE ENABLED IN HOLD-FLUSH MODE.\n"); #else printf("WARNING: CYRIX 486DLC CACHE ENABLED.\n"); #endif #endif } #if defined(I486_CPU) || defined(I586_CPU) || defined(I686_CPU) /* * On a 486 or above, enable ring 0 write protection. */ if (cpu_class >= CPUCLASS_486) lcr0(rcr0() | CR0_WP); #endif } /* * machine dependent system variables. */ int cpu_sysctl(name, namelen, oldp, oldlenp, newp, newlen, p) int *name; u_int namelen; void *oldp; size_t *oldlenp; void *newp; size_t newlen; struct proc *p; { dev_t consdev; /* all sysctl names at this level are terminal */ if (namelen != 1) return (ENOTDIR); /* overloaded */ switch (name[0]) { case CPU_CONSDEV: if (cn_tab != NULL) consdev = cn_tab->cn_dev; else consdev = NODEV; return (sysctl_rdstruct(oldp, oldlenp, newp, &consdev, sizeof consdev)); case CPU_BIOSBASEMEM: return (sysctl_rdint(oldp, oldlenp, newp, biosbasemem)); case CPU_BIOSEXTMEM: return (sysctl_rdint(oldp, oldlenp, newp, biosextmem)); case CPU_NKPDE: return (sysctl_rdint(oldp, oldlenp, newp, nkpde)); default: return (EOPNOTSUPP); } /* NOTREACHED */ } #ifdef COMPAT_IBCS2 void ibcs2_sendsig __P((sig_t, int, int, u_long)); void ibcs2_sendsig(catcher, sig, mask, code) sig_t catcher; int sig, mask; u_long code; { extern int bsd_to_ibcs2_sig[]; sendsig(catcher, bsd_to_ibcs2_sig[sig], mask, code); } #endif /* * Send an interrupt to process. * * Stack is set up to allow sigcode stored * in u. to call routine, followed by kcall * to sigreturn routine below. After sigreturn * resets the signal mask, the stack, and the * frame pointer, it returns to the user * specified pc, psl. */ void sendsig(catcher, sig, mask, code) sig_t catcher; int sig, mask; u_long code; { register struct proc *p = curproc; register struct trapframe *tf; struct sigframe *fp, frame; struct sigacts *psp = p->p_sigacts; int oonstack; extern char sigcode[], esigcode[]; /* * Build the argument list for the signal handler. */ frame.sf_signum = sig; tf = p->p_md.md_regs; oonstack = psp->ps_sigstk.ss_flags & SS_ONSTACK; /* * Allocate space for the signal handler context. */ if ((psp->ps_flags & SAS_ALTSTACK) && !oonstack && (psp->ps_sigonstack & sigmask(sig))) { fp = (struct sigframe *)(psp->ps_sigstk.ss_sp + psp->ps_sigstk.ss_size - sizeof(struct sigframe)); psp->ps_sigstk.ss_flags |= SS_ONSTACK; } else { fp = (struct sigframe *)tf->tf_esp - 1; } frame.sf_code = code; frame.sf_scp = &fp->sf_sc; frame.sf_handler = catcher; /* * Build the signal context to be used by sigreturn. */ frame.sf_sc.sc_err = tf->tf_err; frame.sf_sc.sc_trapno = tf->tf_trapno; frame.sf_sc.sc_onstack = oonstack; frame.sf_sc.sc_mask = mask; #ifdef VM86 if (tf->tf_eflags & PSL_VM) { frame.sf_sc.sc_gs = tf->tf_vm86_gs; frame.sf_sc.sc_fs = tf->tf_vm86_fs; frame.sf_sc.sc_es = tf->tf_vm86_es; frame.sf_sc.sc_ds = tf->tf_vm86_ds; frame.sf_sc.sc_eflags = get_vflags(p); } else #endif { __asm("movl %%gs,%w0" : "=r" (frame.sf_sc.sc_gs)); __asm("movl %%fs,%w0" : "=r" (frame.sf_sc.sc_fs)); frame.sf_sc.sc_es = tf->tf_es; frame.sf_sc.sc_ds = tf->tf_ds; frame.sf_sc.sc_eflags = tf->tf_eflags; } frame.sf_sc.sc_edi = tf->tf_edi; frame.sf_sc.sc_esi = tf->tf_esi; frame.sf_sc.sc_ebp = tf->tf_ebp; frame.sf_sc.sc_ebx = tf->tf_ebx; frame.sf_sc.sc_edx = tf->tf_edx; frame.sf_sc.sc_ecx = tf->tf_ecx; frame.sf_sc.sc_eax = tf->tf_eax; frame.sf_sc.sc_eip = tf->tf_eip; frame.sf_sc.sc_cs = tf->tf_cs; frame.sf_sc.sc_esp = tf->tf_esp; frame.sf_sc.sc_ss = tf->tf_ss; if (copyout(&frame, fp, sizeof(frame)) != 0) { /* * Process has trashed its stack; give it an illegal * instruction to halt it in its tracks. */ sigexit(p, SIGILL); /* NOTREACHED */ } /* * Build context to run handler in. */ __asm("movl %w0,%%gs" : : "r" (GSEL(GUDATA_SEL, SEL_UPL))); __asm("movl %w0,%%fs" : : "r" (GSEL(GUDATA_SEL, SEL_UPL))); tf->tf_es = GSEL(GUDATA_SEL, SEL_UPL); tf->tf_ds = GSEL(GUDATA_SEL, SEL_UPL); tf->tf_eip = (int)(((char *)PS_STRINGS) - (esigcode - sigcode)); tf->tf_cs = GSEL(GUCODE_SEL, SEL_UPL); tf->tf_eflags &= ~(PSL_T|PSL_VM|PSL_AC); tf->tf_esp = (int)fp; tf->tf_ss = GSEL(GUDATA_SEL, SEL_UPL); } /* * System call to cleanup state after a signal * has been taken. Reset signal mask and * stack state from context left by sendsig (above). * Return to previous pc and psl as specified by * context left by sendsig. Check carefully to * make sure that the user has not modified the * psl to gain improper privileges or to cause * a machine fault. */ int sys_sigreturn(p, v, retval) struct proc *p; void *v; register_t *retval; { struct sys_sigreturn_args /* { syscallarg(struct sigcontext *) sigcntxp; } */ *uap = v; struct sigcontext *scp, context; register struct trapframe *tf; tf = p->p_md.md_regs; /* * The trampoline code hands us the context. * It is unsafe to keep track of it ourselves, in the event that a * program jumps out of a signal handler. */ scp = SCARG(uap, sigcntxp); if (copyin((caddr_t)scp, &context, sizeof(*scp)) != 0) return (EFAULT); /* * Restore signal context. */ #ifdef VM86 if (context.sc_eflags & PSL_VM) { tf->tf_vm86_gs = context.sc_gs; tf->tf_vm86_fs = context.sc_fs; tf->tf_vm86_es = context.sc_es; tf->tf_vm86_ds = context.sc_ds; set_vflags(p, context.sc_eflags); } else #endif { /* * Check for security violations. If we're returning to * protected mode, the CPU will validate the segment registers * automatically and generate a trap on violations. We handle * the trap, rather than doing all of the checking here. */ if (((context.sc_eflags ^ tf->tf_eflags) & PSL_USERSTATIC) != 0 || !USERMODE(context.sc_cs, context.sc_eflags)) return (EINVAL); /* %fs and %gs were restored by the trampoline. */ tf->tf_es = context.sc_es; tf->tf_ds = context.sc_ds; tf->tf_eflags = context.sc_eflags; } tf->tf_edi = context.sc_edi; tf->tf_esi = context.sc_esi; tf->tf_ebp = context.sc_ebp; tf->tf_ebx = context.sc_ebx; tf->tf_edx = context.sc_edx; tf->tf_ecx = context.sc_ecx; tf->tf_eax = context.sc_eax; tf->tf_eip = context.sc_eip; tf->tf_cs = context.sc_cs; tf->tf_esp = context.sc_esp; tf->tf_ss = context.sc_ss; if (context.sc_onstack & 01) p->p_sigacts->ps_sigstk.ss_flags |= SS_ONSTACK; else p->p_sigacts->ps_sigstk.ss_flags &= ~SS_ONSTACK; p->p_sigmask = context.sc_mask & ~sigcantmask; return (EJUSTRETURN); } int waittime = -1; struct pcb dumppcb; void boot(howto, bootstr) int howto; char *bootstr; { extern int cold; if (cold) { howto |= RB_HALT; goto haltsys; } boothowto = howto; if ((howto & RB_NOSYNC) == 0 && waittime < 0) { waittime = 0; vfs_shutdown(); /* * If we've been adjusting the clock, the todr * will be out of synch; adjust it now. */ resettodr(); } /* Disable interrupts. */ splhigh(); /* Do a dump if requested. */ if ((howto & (RB_DUMP | RB_HALT)) == RB_DUMP) dumpsys(); haltsys: doshutdownhooks(); if (howto & RB_HALT) { #if NAPM > 0 && !defined(APM_NO_POWEROFF) /* turn off, if we can. But try to turn disk off and * wait a bit first--some disk drives are slow to clean up * and users have reported disk corruption. */ delay(500000); apm_set_powstate(APM_DEV_DISK(0xff), APM_SYS_OFF); delay(500000); apm_set_powstate(APM_DEV_ALLDEVS, APM_SYS_OFF); #endif printf("\n"); printf("The operating system has halted.\n"); printf("Please press any key to reboot.\n\n"); cngetc(); } printf("rebooting...\n"); cpu_reset(); for(;;) ; /*NOTREACHED*/ } /* * These variables are needed by /sbin/savecore */ u_long dumpmag = 0x8fca0101; /* magic number */ int dumpsize = 0; /* pages */ long dumplo = 0; /* blocks */ /* * This is called by configure to set dumplo and dumpsize. * Dumps always skip the first CLBYTES of disk space * in case there might be a disk label stored there. * If there is extra space, put dump at the end to * reduce the chance that swapping trashes it. */ void dumpconf() { int nblks; /* size of dump area */ int maj; if (dumpdev == NODEV) return; maj = major(dumpdev); if (maj < 0 || maj >= nblkdev) panic("dumpconf: bad dumpdev=0x%x", dumpdev); if (bdevsw[maj].d_psize == NULL) return; nblks = (*bdevsw[maj].d_psize)(dumpdev); if (nblks <= ctod(1)) return; dumpsize = btoc(IOM_END + ctob(dumpmem_high)); /* Always skip the first CLBYTES, in case there is a label there. */ if (dumplo < ctod(1)) dumplo = ctod(1); /* Put dump at end of partition, and make it fit. */ if (dumpsize > dtoc(nblks - dumplo)) dumpsize = dtoc(nblks - dumplo); if (dumplo < nblks - ctod(dumpsize)) dumplo = nblks - ctod(dumpsize); } /* * Doadump comes here after turning off memory management and * getting on the dump stack, either when called above, or by * the auto-restart code. */ #define BYTES_PER_DUMP NBPG /* must be a multiple of pagesize XXX small */ static vm_offset_t dumpspace; vm_offset_t reserve_dumppages(p) vm_offset_t p; { dumpspace = p; return (p + BYTES_PER_DUMP); } void dumpsys() { unsigned bytes, i, n; int maddr, psize; daddr_t blkno; int (*dump) __P((dev_t, daddr_t, caddr_t, size_t)); int error; /* Save registers. */ savectx(&dumppcb); msgbufmapped = 0; /* don't record dump msgs in msgbuf */ if (dumpdev == NODEV) return; /* * For dumps during autoconfiguration, * if dump device has already configured... */ if (dumpsize == 0) dumpconf(); if (dumplo < 0) return; printf("\ndumping to dev %x, offset %ld\n", dumpdev, dumplo); psize = (*bdevsw[major(dumpdev)].d_psize)(dumpdev); printf("dump "); if (psize == -1) { printf("area unavailable\n"); return; } #if 0 /* XXX this doesn't work. grr. */ /* toss any characters present prior to dump */ while (sget() != NULL); /*syscons and pccons differ */ #endif bytes = ctob(dumpmem_high) + IOM_END; maddr = 0; blkno = dumplo; dump = bdevsw[major(dumpdev)].d_dump; error = 0; for (i = 0; i < bytes; i += n) { /* * Avoid dumping the ISA memory hole, and areas that * BIOS claims aren't in low memory. */ if (i >= ctob(dumpmem_low) && i < IOM_END) { n = IOM_END - i; maddr += n; blkno += btodb(n); continue; } /* Print out how many MBs we to go. */ n = bytes - i; if (n && (n % (1024*1024)) == 0) printf("%d ", n / (1024 * 1024)); /* Limit size for next transfer. */ if (n > BYTES_PER_DUMP) n = BYTES_PER_DUMP; (void) pmap_map(dumpspace, maddr, maddr + n, VM_PROT_READ); error = (*dump)(dumpdev, blkno, (caddr_t)dumpspace, n); if (error) break; maddr += n; blkno += btodb(n); /* XXX? */ #if 0 /* XXX this doesn't work. grr. */ /* operator aborting dump? */ if (sget() != NULL) { error = EINTR; break; } #endif } switch (error) { case ENXIO: printf("device bad\n"); break; case EFAULT: printf("device not ready\n"); break; case EINVAL: printf("area improper\n"); break; case EIO: printf("i/o error\n"); break; case EINTR: printf("aborted from console\n"); break; case 0: printf("succeeded\n"); break; default: printf("error %d\n", error); break; } printf("\n\n"); delay(5000000); /* 5 seconds */ } #ifdef HZ /* * If HZ is defined we use this code, otherwise the code in * /sys/i386/i386/microtime.s is used. The other code only works * for HZ=100. */ void microtime(tvp) register struct timeval *tvp; { int s = splhigh(); *tvp = time; tvp->tv_usec += tick; splx(s); while (tvp->tv_usec > 1000000) { tvp->tv_sec++; tvp->tv_usec -= 1000000; } } #endif /* HZ */ /* * Clear registers on exec */ void setregs(p, pack, stack, retval) struct proc *p; struct exec_package *pack; u_long stack; register_t *retval; { register struct pcb *pcb = &p->p_addr->u_pcb; register struct trapframe *tf; #if NNPX > 0 /* If we were using the FPU, forget about it. */ if (npxproc == p) npxdrop(); #endif #ifdef USER_LDT if (pcb->pcb_flags & PCB_USER_LDT) i386_user_cleanup(pcb); #endif p->p_md.md_flags &= ~MDP_USEDFPU; pcb->pcb_flags = 0; tf = p->p_md.md_regs; __asm("movl %w0,%%gs" : : "r" (LSEL(LUDATA_SEL, SEL_UPL))); __asm("movl %w0,%%fs" : : "r" (LSEL(LUDATA_SEL, SEL_UPL))); tf->tf_es = LSEL(LUDATA_SEL, SEL_UPL); tf->tf_ds = LSEL(LUDATA_SEL, SEL_UPL); tf->tf_ebp = 0; tf->tf_ebx = (int)PS_STRINGS; tf->tf_eip = pack->ep_entry; tf->tf_cs = LSEL(LUCODE_SEL, SEL_UPL); tf->tf_eflags = PSL_USERSET; tf->tf_esp = stack; tf->tf_ss = LSEL(LUDATA_SEL, SEL_UPL); retval[1] = 0; } /* * Initialize segments and descriptor tables */ union descriptor gdt[NGDT]; union descriptor ldt[NLDT]; struct gate_descriptor idt[NIDT]; extern struct user *proc0paddr; void setgate(gd, func, args, type, dpl) struct gate_descriptor *gd; void *func; int args, type, dpl; { gd->gd_looffset = (int)func; gd->gd_selector = GSEL(GCODE_SEL, SEL_KPL); gd->gd_stkcpy = args; gd->gd_xx = 0; gd->gd_type = type; gd->gd_dpl = dpl; gd->gd_p = 1; gd->gd_hioffset = (int)func >> 16; } void setregion(rd, base, limit) struct region_descriptor *rd; void *base; size_t limit; { rd->rd_limit = (int)limit; rd->rd_base = (int)base; } void setsegment(sd, base, limit, type, dpl, def32, gran) struct segment_descriptor *sd; void *base; size_t limit; int type, dpl, def32, gran; { sd->sd_lolimit = (int)limit; sd->sd_lobase = (int)base; sd->sd_type = type; sd->sd_dpl = dpl; sd->sd_p = 1; sd->sd_hilimit = (int)limit >> 16; sd->sd_xx = 0; sd->sd_def32 = def32; sd->sd_gran = gran; sd->sd_hibase = (int)base >> 24; } #define IDTVEC(name) __CONCAT(X, name) extern IDTVEC(div), IDTVEC(dbg), IDTVEC(nmi), IDTVEC(bpt), IDTVEC(ofl), IDTVEC(bnd), IDTVEC(ill), IDTVEC(dna), IDTVEC(dble), IDTVEC(fpusegm), IDTVEC(tss), IDTVEC(missing), IDTVEC(stk), IDTVEC(prot), IDTVEC(page), IDTVEC(rsvd), IDTVEC(fpu), IDTVEC(align), IDTVEC(syscall), IDTVEC(osyscall); void init386(first_avail) vm_offset_t first_avail; { int x; struct region_descriptor region; extern void consinit __P((void)); proc0.p_addr = proc0paddr; /* * Initialize the I/O port and I/O mem extent maps. * Note: we don't have to check the return value since * creation of a fixed extent map will never fail (since * descriptor storage has already been allocated). * * N.B. The iomem extent manages _all_ physical addresses * on the machine. When the amount of RAM is found, the two * extents of RAM are allocated from the map (0 -> ISA hole * and end of ISA hole -> end of RAM). */ ioport_ex = extent_create("ioport", 0x0, 0xffff, M_DEVBUF, (caddr_t)ioport_ex_storage, sizeof(ioport_ex_storage), EX_NOCOALESCE|EX_NOWAIT); iomem_ex = extent_create("iomem", 0x0, 0xffffffff, M_DEVBUF, (caddr_t)iomem_ex_storage, sizeof(iomem_ex_storage), EX_NOCOALESCE|EX_NOWAIT); consinit(); /* XXX SHOULD NOT BE DONE HERE */ /* make gdt gates and memory segments */ setsegment(&gdt[GCODE_SEL].sd, 0, 0xfffff, SDT_MEMERA, SEL_KPL, 1, 1); setsegment(&gdt[GDATA_SEL].sd, 0, 0xfffff, SDT_MEMRWA, SEL_KPL, 1, 1); setsegment(&gdt[GLDT_SEL].sd, ldt, sizeof(ldt) - 1, SDT_SYSLDT, SEL_KPL, 0, 0); setsegment(&gdt[GUCODE_SEL].sd, 0, i386_btop(VM_MAXUSER_ADDRESS) - 1, SDT_MEMERA, SEL_UPL, 1, 1); setsegment(&gdt[GUDATA_SEL].sd, 0, i386_btop(VM_MAXUSER_ADDRESS) - 1, SDT_MEMRWA, SEL_UPL, 1, 1); /* bios trampoline GDT entries */ setsegment(&gdt[GBIOSCODE_SEL].sd, 0, 0xfffff, SDT_MEMERA, SEL_KPL, 0, 0); setsegment(&gdt[GBIOSDATA_SEL].sd, 0, 0xfffff, SDT_MEMRWA, SEL_KPL, 0, 0); /* make ldt gates and memory segments */ setgate(&ldt[LSYS5CALLS_SEL].gd, &IDTVEC(osyscall), 1, SDT_SYS386CGT, SEL_UPL); ldt[LUCODE_SEL] = gdt[GUCODE_SEL]; ldt[LUDATA_SEL] = gdt[GUDATA_SEL]; ldt[LBSDICALLS_SEL] = ldt[LSYS5CALLS_SEL]; /* exceptions */ for (x = 0; x < NIDT; x++) setgate(&idt[x], &IDTVEC(rsvd), 0, SDT_SYS386TGT, SEL_KPL); setgate(&idt[ 0], &IDTVEC(div), 0, SDT_SYS386TGT, SEL_KPL); setgate(&idt[ 1], &IDTVEC(dbg), 0, SDT_SYS386TGT, SEL_KPL); setgate(&idt[ 2], &IDTVEC(nmi), 0, SDT_SYS386TGT, SEL_KPL); setgate(&idt[ 3], &IDTVEC(bpt), 0, SDT_SYS386TGT, SEL_UPL); setgate(&idt[ 4], &IDTVEC(ofl), 0, SDT_SYS386TGT, SEL_KPL); setgate(&idt[ 5], &IDTVEC(bnd), 0, SDT_SYS386TGT, SEL_KPL); setgate(&idt[ 6], &IDTVEC(ill), 0, SDT_SYS386TGT, SEL_KPL); setgate(&idt[ 7], &IDTVEC(dna), 0, SDT_SYS386TGT, SEL_KPL); setgate(&idt[ 8], &IDTVEC(dble), 0, SDT_SYS386TGT, SEL_KPL); setgate(&idt[ 9], &IDTVEC(fpusegm), 0, SDT_SYS386TGT, SEL_KPL); setgate(&idt[ 10], &IDTVEC(tss), 0, SDT_SYS386TGT, SEL_KPL); setgate(&idt[ 11], &IDTVEC(missing), 0, SDT_SYS386TGT, SEL_KPL); setgate(&idt[ 12], &IDTVEC(stk), 0, SDT_SYS386TGT, SEL_KPL); setgate(&idt[ 13], &IDTVEC(prot), 0, SDT_SYS386TGT, SEL_KPL); setgate(&idt[ 14], &IDTVEC(page), 0, SDT_SYS386TGT, SEL_KPL); setgate(&idt[ 16], &IDTVEC(fpu), 0, SDT_SYS386TGT, SEL_KPL); setgate(&idt[ 17], &IDTVEC(align), 0, SDT_SYS386TGT, SEL_KPL); setgate(&idt[128], &IDTVEC(syscall), 0, SDT_SYS386TGT, SEL_UPL); setregion(®ion, gdt, sizeof(gdt) - 1); lgdt(®ion); setregion(®ion, idt, sizeof(idt) - 1); lidt(®ion); #if NISA > 0 isa_defaultirq(); #endif splhigh(); enable_intr(); /* * Use BIOS values passed in from the boot program. * * XXX Not only does probing break certain 386 AT relics, but * not all BIOSes (Dell, Compaq, others) report the correct * amount of extended memory. */ avail_end = biosextmem ? IOM_END + biosextmem * 1024 : biosbasemem * 1024; /* just temporary use */ /* * Allocate the physical addresses used by RAM from the iomem * extent map. This is done before the addresses are * page rounded just to make sure we get them all. */ if (extent_alloc_region(iomem_ex, 0, IOM_BEGIN, EX_NOWAIT)) { /* XXX What should we do? */ printf("WARNING: CAN'T ALLOCATE BASE RAM FROM IOMEM EXTENT MAP!\n"); } if (avail_end > IOM_END && extent_alloc_region(iomem_ex, IOM_END, (avail_end - IOM_END), EX_NOWAIT)) { /* XXX What should we do? */ printf("WARNING: CAN'T ALLOCATE EXTENDED MEMORY FROM IOMEM EXTENT MAP!\n"); } /* Round down to whole pages. */ biosbasemem &= -(NBPG / 1024); biosextmem &= -(NBPG / 1024); #if NAPM > 0 avail_start = 2*NBPG; /* save us a page! */ #else avail_start = NBPG; /* BIOS leaves data in low memory */ /* and VM system doesn't work with phys 0 */ #endif avail_end = biosextmem ? IOM_END + biosextmem * 1024 : biosbasemem * 1024; /* number of pages of physmem addr space */ physmem = btoc((biosbasemem + biosextmem) * 1024); dumpmem_low = btoc(biosbasemem * 1024); dumpmem_high = btoc(biosextmem * 1024); /* * Initialize for pmap_free_pages and pmap_next_page. * These guys should be page-aligned. */ hole_start = biosbasemem * 1024; /* we load right after the I/O hole; adjust hole_end to compensate */ hole_end = round_page(first_avail); avail_next = avail_start; if (physmem < btoc(2 * 1024 * 1024)) { printf("warning: too little memory available; running in degraded mode\n" "press a key to confirm\n\n"); cngetc(); } /* call pmap initialization to make new kernel address space */ pmap_bootstrap((vm_offset_t)atdevbase + IOM_SIZE); #ifdef DDB ddb_init(); if (boothowto & RB_KDB) Debugger(); #endif #ifdef KGDB if (boothowto & RB_KDB) kgdb_connect(0); #endif } struct queue { struct queue *q_next, *q_prev; }; /* * insert an element into a queue */ void _insque(v1, v2) void *v1; void *v2; { register struct queue *elem = v1, *head = v2; register struct queue *next; next = head->q_next; elem->q_next = next; head->q_next = elem; elem->q_prev = head; next->q_prev = elem; } /* * remove an element from a queue */ void _remque(v) void *v; { register struct queue *elem = v; register struct queue *next, *prev; next = elem->q_next; prev = elem->q_prev; next->q_prev = prev; prev->q_next = next; elem->q_prev = 0; } #ifdef COMPAT_NOMID static int exec_nomid(p, epp) struct proc *p; struct exec_package *epp; { int error; u_long midmag, magic; u_short mid; struct exec *execp = epp->ep_hdr; /* check on validity of epp->ep_hdr performed by exec_out_makecmds */ midmag = ntohl(execp->a_midmag); mid = (midmag >> 16) & 0xffff; magic = midmag & 0xffff; if (magic == 0) { magic = (execp->a_midmag & 0xffff); mid = MID_ZERO; } midmag = mid << 16 | magic; switch (midmag) { case (MID_ZERO << 16) | ZMAGIC: /* * 386BSD's ZMAGIC format: */ error = exec_aout_prep_oldzmagic(p, epp); break; case (MID_ZERO << 16) | QMAGIC: /* * BSDI's QMAGIC format: * same as new ZMAGIC format, but with different magic number */ error = exec_aout_prep_zmagic(p, epp); break; case (MID_ZERO << 16) | NMAGIC: /* * BSDI's NMAGIC format: * same as NMAGIC format, but with different magic number * and with text starting at 0. */ error = exec_aout_prep_oldnmagic(p, epp); break; case (MID_ZERO << 16) | OMAGIC: /* * BSDI's OMAGIC format: * same as OMAGIC format, but with different magic number * and with text starting at 0. */ error = exec_aout_prep_oldomagic(p, epp); break; default: error = ENOEXEC; } return error; } #endif /* * cpu_exec_aout_makecmds(): * cpu-dependent a.out format hook for execve(). * * Determine of the given exec package refers to something which we * understand and, if so, set up the vmcmds for it. * * On the i386, old (386bsd) ZMAGIC binaries and BSDI QMAGIC binaries * if COMPAT_NOMID is given as a kernel option. */ int cpu_exec_aout_makecmds(p, epp) struct proc *p; struct exec_package *epp; { int error = ENOEXEC; #ifdef COMPAT_NOMID if ((error = exec_nomid(p, epp)) == 0) return error; #endif /* ! COMPAT_NOMID */ return error; } u_int pmap_free_pages() { if (avail_next <= hole_start) return ((hole_start - avail_next) / NBPG + (avail_end - hole_end) / NBPG); else return ((avail_end - avail_next) / NBPG); } int pmap_next_page(addrp) vm_offset_t *addrp; { if (avail_next + NBPG > avail_end) return FALSE; if (avail_next + NBPG > hole_start && avail_next < hole_end) avail_next = hole_end; *addrp = avail_next; avail_next += NBPG; return TRUE; } int pmap_page_index(pa) vm_offset_t pa; { if (pa >= avail_start && pa < hole_start) return i386_btop(pa - avail_start); if (pa >= hole_end && pa < avail_end) return i386_btop(pa - hole_end + hole_start - avail_start); return -1; } /* * consinit: * initialize the system console. * XXX - shouldn't deal with this initted thing, but then, * it shouldn't be called from init386 either. */ void consinit() { static int initted; if (initted) return; initted = 1; cninit(); } void cpu_reset() { struct region_descriptor region; /* Toggle the hardware reset line on the keyboard controller. */ outb(KBCMDP, KBC_PULSE0); delay(20000); outb(KBCMDP, KBC_PULSE0); delay(20000); /* * Try to cause a triple fault and watchdog reset by setting the * IDT to point to nothing. */ setregion(®ion, 0, 0); lidt(®ion); /* * Try to cause a triple fault and watchdog reset by unmapping the * entire address space. */ bzero((caddr_t)PTD, NBPG); pmap_update(); for (;;); } int bus_space_map(t, bpa, size, cacheable, bshp) bus_space_tag_t t; bus_addr_t bpa; bus_size_t size; int cacheable; bus_space_handle_t *bshp; { int error; struct extent *ex; /* * Pick the appropriate extent map. */ switch (t) { case I386_BUS_SPACE_IO: ex = ioport_ex; break; case I386_BUS_SPACE_MEM: ex = iomem_ex; break; default: panic("bus_space_map: bad bus space tag"); } /* * Before we go any further, let's make sure that this * region is available. */ error = extent_alloc_region(ex, bpa, size, EX_NOWAIT | (ioport_malloc_safe ? EX_MALLOCOK : 0)); if (error) return (error); /* * For I/O space, that's all she wrote. */ if (t == I386_BUS_SPACE_IO) { *bshp = bpa; return (0); } /* * For memory space, map the bus physical address to * a kernel virtual address. */ error = bus_mem_add_mapping(bpa, size, cacheable, bshp); if (error) { if (extent_free(ex, bpa, size, EX_NOWAIT | (ioport_malloc_safe ? EX_MALLOCOK : 0))) { printf("bus_space_map: pa 0x%lx, size 0x%lx\n", bpa, size); printf("bus_space_map: can't free region\n"); } } return (error); } int bus_space_alloc(t, rstart, rend, size, alignment, boundary, cacheable, bpap, bshp) bus_space_tag_t t; bus_addr_t rstart, rend; bus_size_t size, alignment, boundary; int cacheable; bus_addr_t *bpap; bus_space_handle_t *bshp; { struct extent *ex; u_long bpa; int error; /* * Pick the appropriate extent map. */ switch (t) { case I386_BUS_SPACE_IO: ex = ioport_ex; break; case I386_BUS_SPACE_MEM: ex = iomem_ex; break; default: panic("bus_space_alloc: bad bus space tag"); } /* * Sanity check the allocation against the extent's boundaries. */ if (rstart < ex->ex_start || rend > ex->ex_end) panic("bus_space_alloc: bad region start/end"); /* * Do the requested allocation. */ error = extent_alloc_subregion(ex, rstart, rend, size, alignment, boundary, EX_NOWAIT | (ioport_malloc_safe ? EX_MALLOCOK : 0), &bpa); if (error) return (error); /* * For I/O space, that's all she wrote. */ if (t == I386_BUS_SPACE_IO) { *bshp = *bpap = bpa; return (0); } /* * For memory space, map the bus physical address to * a kernel virtual address. */ error = bus_mem_add_mapping(bpa, size, cacheable, bshp); if (error) { if (extent_free(iomem_ex, bpa, size, EX_NOWAIT | (ioport_malloc_safe ? EX_MALLOCOK : 0))) { printf("bus_space_alloc: pa 0x%lx, size 0x%lx\n", bpa, size); printf("bus_space_alloc: can't free region\n"); } } *bpap = bpa; return (error); } int bus_mem_add_mapping(bpa, size, cacheable, bshp) bus_addr_t bpa; bus_size_t size; int cacheable; bus_space_handle_t *bshp; { u_long pa, endpa; vm_offset_t va; pa = i386_trunc_page(bpa); endpa = i386_round_page((bpa + size) - 1); #ifdef DIAGNOSTIC if (endpa <= pa) panic("bus_mem_add_mapping: overflow"); #endif va = kmem_alloc_pageable(kernel_map, endpa - pa); if (va == 0) return (ENOMEM); *bshp = (bus_space_handle_t)(va + (bpa & PGOFSET)); for (; pa < endpa; pa += NBPG, va += NBPG) { pmap_enter(pmap_kernel(), va, pa, VM_PROT_READ | VM_PROT_WRITE, TRUE); if (!cacheable) pmap_changebit(pa, PG_N, ~0); else pmap_changebit(pa, 0, ~PG_N); } return 0; } void bus_space_unmap(t, bsh, size) bus_space_tag_t t; bus_space_handle_t bsh; bus_size_t size; { struct extent *ex; u_long va, endva; bus_addr_t bpa; /* * Find the correct extent and bus physical address. */ switch (t) { case I386_BUS_SPACE_IO: ex = ioport_ex; bpa = bsh; break; case I386_BUS_SPACE_MEM: ex = iomem_ex; va = i386_trunc_page(bsh); endva = i386_round_page((bsh + size) - 1); #ifdef DIAGNOSTIC if (endva <= va) panic("bus_space_unmap: overflow"); #endif bpa = pmap_extract(pmap_kernel(), va) + (bsh & PGOFSET); /* * Free the kernel virtual mapping. */ kmem_free(kernel_map, va, endva - va); break; default: panic("bus_space_unmap: bad bus space tag"); } if (extent_free(ex, bpa, size, EX_NOWAIT | (ioport_malloc_safe ? EX_MALLOCOK : 0))) { printf("bus_space_unmap: %s 0x%lx, size 0x%lx\n", (t == I386_BUS_SPACE_IO) ? "port" : "pa", bpa, size); printf("bus_space_unmap: can't free region\n"); } } void bus_space_free(t, bsh, size) bus_space_tag_t t; bus_space_handle_t bsh; bus_size_t size; { /* bus_space_unmap() does all that we need to do. */ bus_space_unmap(t, bsh, size); } int bus_space_subregion(t, bsh, offset, size, nbshp) bus_space_tag_t t; bus_space_handle_t bsh; bus_size_t offset, size; bus_space_handle_t *nbshp; { *nbshp = bsh + offset; return (0); }